Class: Slideshow::Gen

Inherits:
Object
  • Object
show all
Defined in:
lib/slideshow.rb

Constant Summary collapse

KNOWN_TEXTILE_EXTNAMES =
[ '.textile', '.t' ]
KNOWN_MARKDOWN_EXTNAMES =
[ '.markdown', '.mark', '.m', '.txt', '.text' ]
BUILTIN_MANIFESTS =
[ 'fullerscreen.txt', 'fullerscreen.txt.gen',
's5.txt','s5.txt.gen',
's6.txt','s6.txt.gen' ]

Instance Method Summary collapse

Constructor Details

#initializeGen

Returns a new instance of Gen.



176
177
178
179
180
# File 'lib/slideshow.rb', line 176

def initialize
  @logger = Logger.new(STDOUT)
  @logger.level = Logger::INFO
  @opts = Opts.new
end

Instance Method Details

#cache_dirObject



190
191
192
# File 'lib/slideshow.rb', line 190

def cache_dir
  PLATFORM =~ /win32/ ? win32_cache_dir : File.join(File.expand_path("~"), ".slideshow")
end

#create_slideshow(fn) ⇒ Object



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'lib/slideshow.rb', line 305

def create_slideshow( fn )

    logger.debug "manifest=#{opts.manifest}"
    manifest = load_manifest( opts.manifest )
    # pp manifest

    # expand output path in current dir and make sure output path exists
    outpath = File.expand_path( opts.output_path ) 
    logger.debug "outpath=#{outpath}"
    File.makedirs( outpath ) unless File.directory? outpath 

    dirname  = File.dirname( fn )
    basename = File.basename( fn, '.*' )
    extname  = File.extname( fn )
    logger.debug "dirname=#{dirname}, basename=#{basename}, extname=#{extname}"

  known_extnames = KNOWN_TEXTILE_EXTNAMES + KNOWN_MARKDOWN_EXTNAMES
                
  if extname.empty? then
    extname  = ".textile"   # default to .textile 
    
    known_extnames.each do |e|
       logger.debug "File.exists? #{dirname}/#{basename}#{e}"
       if File.exists?( "#{dirname}/#{basename}#{e}" ) then         
          extname = e
          logger.debug "extname=#{extname}"
          break
       end
    end     
  end

  inname  =  "#{dirname}/#{basename}#{extname}"

  logger.debug "inname=#{inname}"
  
  content = File.read( inname )
  
  # todo: read headers before command line options (lets you override options using commandline switch)
  
  # read source document
  # strip leading optional headers (key/value pairs) including optional empty lines

  read_headers = true
  content = ""

  File.open( inname ).readlines.each do |line|
    if read_headers && line =~ /^\s*(\w[\w-]*)[ \t]*:[ \t]*(.*)/
      key = $1.downcase
      value = $2.strip
    
      logger.debug "  adding option: key=>#{key}< value=>#{value}<"
      opts.put( key, value )
    elsif line =~ /^\s*$/
      content << line  unless read_headers
    else
      read_headers = false
      content << line
    end
  end

  # run pre-filters (built-in macros)
  # o replace {{{  w/ <pre class='code'>
  # o replace }}}  w/ </pre>
  content.gsub!( "{{{{{{", "<pre class='code'>_S9BEGIN_" )
  content.gsub!( "}}}}}}", "_S9END_</pre>" )  
  content.gsub!( "{{{", "<pre class='code'>" )
  content.gsub!( "}}}", "</pre>" )
  # restore escaped {{{}}} I'm sure there's a better way! Rubyize this! Anyone?
  content.gsub!( "_S9BEGIN_", "{{{" )
  content.gsub!( "_S9END_", "}}}" )

  opts.set_defaults
  
  params = Params.new( basename, opts )
  
  puts "Preparing slideshow '#{basename}'..."

  # convert light-weight markup to hypertext
  
  if KNOWN_MARKDOWN_EXTNAMES.include?( extname )
    content = Maruku.new( content, {:on_error => :raise} ).to_html
    # old code: content = BlueCloth.new( content ).to_html
  else
    # turn off hard line breaks
    # turn off span caps (see http://rubybook.ca/2008/08/16/redcloth)
    red = RedCloth.new( content, [:no_span_caps] )
    red.hard_breaks = false
    content = red.to_html
  end
  

  # post-processing

  slide_counter = 0
  content2 = ''
  
  # wrap h1's in slide divs; note use just <h1 since some processors add ids e.g. <h1 id='x'>
  content.each_line do |line|
     if line.include?( '<h1' ) then
        content2 << "\n\n</div>"  if slide_counter > 0
        content2 << "<div class='slide'>\n\n"
        slide_counter += 1
     end
     content2 << line
  end
  content2 << "\n\n</div>"   if slide_counter > 0

=begin
  ## todo: run syntax highlighting before markup/textilize? lets us add textile to highlighted code?
  ##  avoid undoing escaped entities?
  
  include_code_stylesheet = false
  # syntax highlight code
  # todo: can the code handle escaped entities? e.g. &gt; 
  doc = Hpricot(content2)
    doc.search("pre.code, pre > code").each do |e|
      if e.inner_html =~ /^\s*#!(\w+)/
        lang = $1.downcase
        if e.inner_html =~ /^\{\{\{/  # {{{ assumes escape/literal #!lang 
          # do nothing; next
          logger.debug "  skipping syntax highlighting using lang=#{lang}; assumimg escaped literal"
        else         
          logger.debug "  syntax highlighting using lang=#{lang}"
          if Uv.syntaxes.include?(lang)
            code = e.inner_html.sub(/^\s*#!\w+/, '').strip
            
            code.gsub!( "&lt;", "<" )
            code.gsub!( "&gt;", ">" )
            code.gsub!( "&amp;", "&" )
            # todo: missing any other entities? use CGI::unescapeHTML?
            logger.debug "code=>#{code}<"
            
            code_highlighted = Uv.parse( code, "xhtml", lang, opts.code_line_numbers?, opts.code_theme )
            # old code: e.inner_html = code_highlighted
            # todo: is it ok to replace the pre.code enclosing element to avoid duplicates?
            e.swap( code_highlighted )
            include_code_stylesheet = true
          end
        end
      end
    end

   content2 = doc.to_s
=end

   

  manifest.each do |entry|
    outname = entry[0]
    if outname.include? '__file__' # process
      outname = outname.gsub( '__file__', basename )
      puts "Preparing #{outname}..."

      out = File.new( with_output_path( outname, outpath ), "w+" )

      out << render_template( load_template( entry[1] ), params.params_binding )
      
      if entry.size > 2 # more than one source file? assume header and footer with content added inbetween
        out << content2 
        out << render_template( load_template( entry[2] ), params.params_binding )
      end

      out.flush
      out.close

    else # just copy verbatim if target/dest has no __file__ in name
      dest   = entry[0]      
      source = entry[1]
            
      puts "Copying to #{dest} from #{source}..."     
      File.copy( source, with_output_path( dest, outpath ) )
    end
  end

 
=begin 
  if include_code_stylesheet
     logger.debug "cache_dir=#{cache_dir}"

     FileUtils.mkdir(cache_dir) unless File.exists?(cache_dir) if cache_dir
     Uv.copy_files "xhtml", cache_dir

     theme = opts.code_theme
  
     theme_content = File.read( "#{cache_dir}/css/#{theme}.css" )
     
     out << "/* styles for code syntax highlighting theme '#{theme}' */\n"
     out << "\n"
     out << theme_content
  end
=end


  puts "Done."
end

#create_slideshow_templatesObject



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/slideshow.rb', line 285

def create_slideshow_templates
  logger.debug "manifest=#{opts.manifest}.gen"
  manifest = load_manifest( opts.manifest+".gen" )

  # expand output path in current dir and make sure output path exists
  outpath = File.expand_path( opts.output_path ) 
  logger.debug "outpath=#{outpath}"
  File.makedirs( outpath ) unless File.directory? outpath 

  manifest.each do |entry|
    dest   = entry[0]      
    source = entry[1]
                
    puts "Copying to #{dest} from #{source}..."     
    File.copy( source, with_output_path( dest, outpath ) )
  end
  
  puts "Done."   
end

#load_manifest(path) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/slideshow.rb', line 204

def load_manifest( path )
  
  # check if file exists (if yes use custom template package!) - allows you to override builtin package with same name
  if BUILTIN_MANIFESTS.include?( path ) && !File.exists?( path )  
    templatesdir = "#{File.dirname(__FILE__)}/templates"
    logger.debug "use builtin template package"
    logger.debug "templatesdir=#{templatesdir}"
    filename = "#{templatesdir}/#{path}"
  else
    templatesdir = File.dirname( path )
    logger.debug "use custom template package"
    logger.debug "templatesdir=#{templatesdir}"
    filename = path
  end

  manifest = []
  puts "  Loading template manifest #{filename}..." 

  File.open( filename ).readlines.each_with_index do |line,i|
    case line
    when /^\s*$/
      # skip empty lines
    when /^\s*#.*$/
      # skip comment lines
    else
      logger.debug "line #{i+1}: #{line.strip}"
      values = line.strip.split( /[ <,+]+/ )
      
      # add source for shortcuts (assumes relative path; if not issue warning/error)
      values << values[0] if values.size == 1
      
      # normalize all source paths (1..-1) /make full path/add template dir
      (1..values.size-1).each do |i|
        values[i] = "#{templatesdir}/#{values[i]}"
        logger.debug "  path[#{i}]=>#{values[i]}<"
      end
      
      manifest << values
    end      
  end

  manifest
end

#load_template(path) ⇒ Object



248
249
250
251
# File 'lib/slideshow.rb', line 248

def load_template( path ) 
  puts "  Loading template #{path}..."
  return File.read( path )
end

#load_template_old_delete(name, builtin) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/slideshow.rb', line 257

def load_template_old_delete( name, builtin )
  
  if opts.has_includes? 
    opts.includes.each do |path|
      logger.debug "File.exists? #{path}/#{name}"
      
      if File.exists?( "#{path}/#{name}" ) then          
        puts "Loading custom template #{path}/#{name}..."
        return File.read( "#{path}/#{name}" )
      end
    end       
  end
  
  # fallback load builtin template packaged with gem
  load_builtin_template( builtin )
end

#loggerObject



182
183
184
# File 'lib/slideshow.rb', line 182

def logger 
  @logger
end

#optsObject



186
187
188
# File 'lib/slideshow.rb', line 186

def opts
  @opts
end

#render_template(content, b = TOPLEVEL_BINDING) ⇒ Object



253
254
255
# File 'lib/slideshow.rb', line 253

def render_template( content, b=TOPLEVEL_BINDING )
  ERB.new( content ).result( b )
end

#run(args) ⇒ Object



500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/slideshow.rb', line 500

def run( args )

  opt=OptionParser.new do |cmd|
    
    cmd.banner = "Usage: slideshow [options] name"
    
    #todo/fix: use -s5 option without optional hack? possible with OptionParser package/lib?
    # use -5 switch instead?
    cmd.on( '-s[OPTIONAL]', '--s5', 'S5 Compatible Slide Show' ) { opts.put( 's5', true ); opts.put( 'manifest', 's5.txt' ) }
    cmd.on( '-f[OPTIONAL]', '--fullerscreen', 'FullerScreen Compatible Slide Show' ) { opts.put( 'fuller', true ); opts.put( 'manifest', 'fullerscreen.txt' ) }
    # opts.on( "-s", "--style STYLE", "Select Stylesheet" ) { |s| $options[:style]=s }
    # opts.on( "-v", "--version", "Show version" )  {}
    
    cmd.on( '-g', '--generate',  'Generate Slide Show Templates' ) { opts.put( 'generate', true ) }
    
    cmd.on( '-o', '--output PATH', 'outputs to Path' ) { |s| opts.put( 'output', s ) }
    
    # use -d or -o  to select output directory for slideshow or slideshow templates?
    # cmd.on( '-d', '--directory DIRECTORY', 'Output Directory' ) { |s| opts.put( 'directory', s )  }
    # cmd.on( '-i', '--include PATH', 'Load Path' ) { |s| opts.put( 'include', s ) }

    # todo: find different letter for debug trace switch (use v for version?)
    cmd.on( "-v", "--verbose", "Show debug trace" )  do
       logger.datetime_format = "%H:%H:%S"
       logger.level = Logger::DEBUG      
    end

    cmd.on( "-t", "--template TEMPLATE", "Template Manifest" ) do |t|
      # todo: do some checks on passed in template argument
      opts.put( 'manifest', t )
    end
 
    cmd.on_tail( "-h", "--help", "Show this message" ) do
         puts 
         puts "Slide Show (S9) is a free web alternative to PowerPoint or KeyNote in Ruby"
         puts
         puts cmd.help
         puts
         puts "Examples:"
         puts "  slideshow microformats"
         puts "  slideshow microformats.textile"
         puts "  slideshow -s5 microformats       # S5 compatible"
         puts "  slideshow -f microformats        # FullerScreen compatible"
         puts "  slideshow -o slides microformats # Output slideshow to slides folder"
         puts 
         puts "More examles:"
         puts "  slideshow -g      # Generate slide show templates"
         puts "  slideshow -g -s5  # Generate S5 compatible slide show templates"
         puts "  slideshow -g -f   # Generate FullerScreen compatible slide show templates"
         puts
         puts "  slideshow -t s3.txt microformats     # Use custom slide show templates"
         puts
         puts "Further information:"
         puts "  http://slideshow.rubyforge.org" 
         exit
    end
  end

  opt.parse!( args )

  puts "Slide Show (S9) Version: #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"

  if opts.generate?
    create_slideshow_templates
  else
    args.each { |fn| create_slideshow( fn ) }
  end
end

#win32_cache_dirObject



194
195
196
197
198
199
200
201
202
# File 'lib/slideshow.rb', line 194

def win32_cache_dir
  unless File.exists?(home = ENV['HOMEDRIVE'] + ENV['HOMEPATH'])
    puts "No HOMEDRIVE or HOMEPATH environment variable.  Set one to save a" +
         "local cache of stylesheets for syntax highlighting and more."
    return false
  else
    return File.join(home, 'slideshow')
  end
end

#with_output_path(dest, output_path) ⇒ Object



274
275
276
277
278
279
280
281
282
283
# File 'lib/slideshow.rb', line 274

def with_output_path( dest, output_path )
  dest_full = File.expand_path( dest, output_path )
  logger.debug "dest_full=#{dest_full}"
    
  # make sure dest path exists
  dest_path = File.dirname( dest_full )
  logger.debug "dest_path=#{dest_path}"
  File.makedirs( dest_path ) unless File.directory? dest_path
  dest_full
end