slideshow 0.6 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/slideshow.rb +540 -534
  2. metadata +2 -2
@@ -1,535 +1,541 @@
1
- require 'optparse'
2
- require 'erb'
3
- require 'redcloth'
4
- require 'maruku'
5
- require 'logger'
6
- require 'fileutils'
7
- require 'ftools'
8
- require 'hpricot'
9
- require 'uv'
10
-
11
-
12
- module Slideshow
13
-
14
- VERSION = '0.6'
15
-
16
- class Params
17
-
18
- def initialize( name, headers )
19
- @svgname = "#{name}.svg"
20
- @cssname = "#{name}.css"
21
- @headers = headers
22
- end
23
-
24
- def params_binding
25
- binding
26
- end
27
-
28
- end
29
-
30
- # todo: split (command line) options and headers?
31
- # e.g. share (command line) options between slide shows (but not headers?)
32
-
33
- class Opts
34
-
35
- def initialize
36
- @hash = {}
37
- end
38
-
39
- def put( key, value )
40
- key = key.downcase
41
- if key.eql? 'code-theme' then
42
- code_theme = value
43
- elsif key.eql? 'gradient' then
44
- gradient = value
45
- elsif key.eql? 'gradient-colors' then
46
- gradient_colors = value
47
- elsif key.eql? 'gradient-color' then
48
- gradient_color = value
49
- elsif key.eql? 'gradient-theme' then
50
- gradient-theme = value
51
- else
52
- @hash[ key ] = value
53
- end
54
- end
55
-
56
- def code_theme=( value )
57
- @hash[ 'code-theme' ] = value.tr( '-', '_' )
58
- end
59
-
60
- def gradient=( value )
61
- values = value.split( ' ' )
62
- @hash[ 'gradient-theme' ] = values[0].tr( '-', '_' )
63
- @hash[ 'gradient-color1' ] = values[1] if values[1]
64
- @hash[ 'gradient-color2' ] = values[2] if values[2]
65
- end
66
-
67
- def gradient_colors=( value )
68
- values = value.split( ' ' )
69
- @hash[ 'gradient-color1' ] = values[0]
70
- @hash[ 'gradient-color2' ] = values[1] if values[1]
71
- end
72
-
73
- def gradient_color=( value )
74
- @hash[ 'gradient-color1' ] = value
75
- end
76
-
77
- def gradient_theme=( value )
78
- @hash[ 'gradient-theme' ] = value.tr( '-', '_' )
79
- end
80
-
81
- def get( key, default )
82
- value = @hash[ key ]
83
- value.nil? ? default : value
84
- end
85
-
86
- def []( key )
87
- value = @hash[ key ]
88
- value.nil? ? "- #{key} not found -" : value
89
- end
90
-
91
- def get_boolean( key, default )
92
- value = @hash[ key ]
93
- if value.nil?
94
- default
95
- else
96
- if( value == true || value.downcase == 'true' || value.downcase == 'yes' || value.downcase == 'on' )
97
- true
98
- else
99
- false
100
- end
101
- end
102
- end
103
-
104
- def generate?
105
- get_boolean( 'generate', false )
106
- end
107
-
108
- def has_includes?
109
- @hash[ 'include' ]
110
- end
111
-
112
- def includes
113
- # fix: use os-agnostic delimiter (use : for Mac/Unix?)
114
- has_includes? ? @hash['include'].split( ';' ) : []
115
- end
116
-
117
- def s5?
118
- get_boolean( 's5', false )
119
- end
120
-
121
- def fullerscreen?
122
- get_boolean( 'fuller', false ) || get_boolean( 'fullerscreen', false )
123
- end
124
-
125
- def code_theme
126
- get( 'code-theme', 'amy' )
127
- end
128
-
129
- def code_line_numbers?
130
- get_boolean( 'code-line-numbers', true )
131
- end
132
-
133
- DEFAULTS =
134
- [
135
- [ 'title', 'Untitled Slide Show' ],
136
- [ 'gradient-theme', 'dark' ],
137
- [ 'gradient-color1', 'red' ],
138
- [ 'gradient-color2', 'black' ],
139
- [ 'code-theme', 'amy' ],
140
- [ 'code-line-numbers', 'true' ]
141
- ]
142
-
143
- def set_defaults
144
- DEFAULTS.each do | item |
145
- key = item[0]
146
- value = item[1]
147
- @hash[ key ] = value if @hash[ key ].nil?
148
- end
149
- end
150
-
151
- end # class Opts
152
-
153
-
154
- class Gen
155
-
156
- KNOWN_TEXTILE_EXTNAMES = [ '.textile', '.t' ]
157
- KNOWN_MARKDOWN_EXTNAMES = [ '.markdown', '.mark', '.m', '.txt', '.text' ]
158
-
159
- def initialize
160
- @logger = Logger.new(STDOUT)
161
- @logger.level = Logger::INFO
162
- @opts = Opts.new
163
- end
164
-
165
- def logger
166
- @logger
167
- end
168
-
169
- def opts
170
- @opts
171
- end
172
-
173
- def cache_dir
174
- PLATFORM =~ /win32/ ? win32_cache_dir : File.join(File.expand_path("~"), ".slideshow")
175
- end
176
-
177
- def win32_cache_dir
178
- unless File.exists?(home = ENV['HOMEDRIVE'] + ENV['HOMEPATH'])
179
- puts "No HOMEDRIVE or HOMEPATH environment variable. Set one to save a" +
180
- "local cache of stylesheets for syntax highlighting and more."
181
- return false
182
- else
183
- return File.join(home, 'slideshow')
184
- end
185
- end
186
-
187
- def load_template( name, builtin )
188
-
189
- if opts.has_includes?
190
- opts.includes.each do |path|
191
- logger.debug "File.exists? #{path}/#{name}"
192
- if File.exists?( "#{path}/#{name}" ) then
193
-
194
- puts "Loading custom template #{path}/#{name}..."
195
- return File.read( "#{path}/#{name}" )
196
-
197
- end
198
- end
199
- end
200
- # fallback load builtin template packaged with gem
201
- load_builtin_template( builtin )
202
- end
203
-
204
- def load_builtin_template( name )
205
- templatesdir = "#{File.dirname(__FILE__)}/templates"
206
- logger.debug "templatesdir=#{templatesdir}"
207
-
208
- File.read( "#{templatesdir}/#{name}" )
209
- end
210
-
211
- def render_template( content, b=TOPLEVEL_BINDING )
212
- ERB.new( content ).result( b )
213
- end
214
-
215
-
216
- def create_slideshow_templates
217
-
218
- if opts.s5?
219
- files = [[ 's5/header.html.erb', 'header.html.erb' ],
220
- [ 's5/footer.html.erb', 'footer.html.erb' ],
221
- [ 's5/style.css.erb', 'style.css.erb' ]]
222
- elsif opts.fullerscreen? # use fullerscreen templates
223
- files = [[ 'header.html.erb', 'header.html.erb' ],
224
- [ 'footer.html.erb', 'footer.html.erb' ],
225
- [ 'style.css.erb', 'style.css.erb' ]]
226
- else # use default s6 templates
227
- files = [[ 's6/header.html.erb', 'header.html.erb' ],
228
- [ 's6/footer.html.erb', 'footer.html.erb' ],
229
- [ 's6/style.css.erb', 'style.css.erb' ]]
230
- end
231
-
232
- # background theming shared between s5/s6/fullerscreen
233
- files << [ 'gradient.svg.erb', 'gradient.svg.erb' ]
234
-
235
- files.each do |file|
236
- source = "#{File.dirname(__FILE__)}/templates/#{file[0]}"
237
- dest = "#{file[1]}"
238
-
239
- puts "Copying '#{source}' to '#{dest}'"
240
- File.copy( source, dest )
241
- end
242
-
243
- puts "Done."
244
- end
245
-
246
- def create_slideshow( fn )
247
-
248
- if opts.s5?
249
- headerdoc = load_template( 'header.html.erb', 's5/header.html.erb' )
250
- footerdoc = load_template( 'footer.html.erb', 's5/footer.html.erb' )
251
- styledoc = load_template( 'style.css.erb', 's5/style.css.erb' )
252
- elsif opts.fullerscreen? # use fullerscreen templates
253
- headerdoc = load_template( 'header.html.erb', 'header.html.erb' )
254
- footerdoc = load_template( 'footer.html.erb', 'footer.html.erb' )
255
- styledoc = load_template( 'style.css.erb', 'style.css.erb' )
256
- else # use default s6 templates
257
- headerdoc = load_template( 'header.html.erb', 's6/header.html.erb' )
258
- footerdoc = load_template( 'footer.html.erb', 's6/footer.html.erb' )
259
- styledoc = load_template( 'style.css.erb', 's6/style.css.erb' )
260
- end
261
-
262
- # background theming shared between s5/s6/fullerscreen
263
- gradientdoc = load_template( 'gradient.svg.erb', 'gradient.svg.erb' )
264
-
265
- basename = File.basename( fn, '.*' )
266
- extname = File.extname( fn )
267
-
268
- known_extnames = KNOWN_TEXTILE_EXTNAMES + KNOWN_MARKDOWN_EXTNAMES
269
-
270
- if extname.eql?("") then
271
- extname = ".textile" # default to .textile
272
-
273
- known_extnames.each { |e|
274
- logger.debug "File.exists? #{basename}#{e}"
275
- if File.exists?( "#{basename}#{e}" ) then
276
- extname = e
277
- logger.debug "extname=#{extname}"
278
- break
279
- end
280
- }
281
- end
282
-
283
- inname = "#{basename}#{extname}"
284
- outname = "#{basename}.html"
285
- svgname = "#{basename}.svg"
286
- cssname = "#{basename}.css"
287
-
288
- logger.debug "inname=#{inname}"
289
-
290
- content = File.read( inname )
291
-
292
- # read source document
293
- # strip leading optional headers (key/value pairs) including optional empty lines
294
-
295
- read_headers = true
296
- content = ""
297
-
298
- File.open( inname ).readlines.each do |line|
299
- if read_headers && line =~ /^\s*(\w[\w-]*)[ \t]*:[ \t]*(.*)/
300
- key = $1.downcase
301
- value = $2.strip
302
-
303
- logger.debug " adding option: key=>#{key}< value=>#{value}<"
304
- opts.put( key, value )
305
- elsif line =~ /^\s*$/
306
- content << line unless read_headers
307
- else
308
- read_headers = false
309
- content << line
310
- end
311
- end
312
-
313
- # run pre-filters (built-in macros)
314
- # o replace {{{ w/ <pre class='code'>
315
- # o replace }}} w/ </pre>
316
- content.gsub!( "{{{{{{", "<pre class='code'>_S9BEGIN_" )
317
- content.gsub!( "}}}}}}", "_S9END_</pre>" )
318
- content.gsub!( "{{{", "<pre class='code'>" )
319
- content.gsub!( "}}}", "</pre>" )
320
- # restore escaped {{{}}} I'm sure there's a better way! Rubyize this! Anyone?
321
- content.gsub!( "_S9BEGIN_", "{{{" )
322
- content.gsub!( "_S9END_", "}}}" )
323
-
324
- opts.set_defaults
325
-
326
- params = Params.new( basename, opts )
327
-
328
- puts "Preparing slideshow theme '#{svgname}'..."
329
-
330
- out = File.new( svgname, "w+" )
331
- out << render_template( gradientdoc, params.params_binding )
332
- out.flush
333
- out.close
334
-
335
- puts "Preparing slideshow '#{outname}'..."
336
-
337
- # convert light-weight markup to hypertext
338
-
339
- if KNOWN_MARKDOWN_EXTNAMES.include?( extname )
340
- content = Maruku.new( content, {:on_error => :raise} ).to_html
341
- # old code: content = BlueCloth.new( content ).to_html
342
- else
343
- # turn off hard line breaks
344
- # turn off span caps (see http://rubybook.ca/2008/08/16/redcloth)
345
- red = RedCloth.new( content, [:no_span_caps] )
346
- red.hard_breaks = false
347
- content = red.to_html
348
- end
349
-
350
-
351
- # post-processing
352
-
353
- slide_counter = 0
354
- content2 = ''
355
-
356
- # wrap h1's in slide divs; note use just <h1 since some processors add ids e.g. <h1 id='x'>
357
- content.each_line { |line|
358
- if line.include?( '<h1' ) then
359
- content2 << "\n\n</div>" if slide_counter > 0
360
- content2 << "<div class='slide'>\n\n"
361
- slide_counter += 1
362
- end
363
- content2 << line
364
- }
365
- content2 << "\n\n</div>" if slide_counter > 0
366
-
367
- ## todo: run syntax highlighting before markup/textilize? lets us add textile to highlighted code?
368
- ## avoid undoing escaped entities?
369
-
370
- include_code_stylesheet = false
371
- # syntax highlight code
372
- # todo: can the code handle escaped entities? e.g. &gt;
373
- doc = Hpricot(content2)
374
- doc.search("pre.code, pre > code").each do |e|
375
- if e.inner_html =~ /^\s*#!(\w+)/
376
- lang = $1.downcase
377
- if e.inner_html =~ /^\{\{\{/ # {{{ assumes escape/literal #!lang
378
- # do nothing; next
379
- logger.debug " skipping syntax highlighting using lang=#{lang}; assumimg escaped literal"
380
- else
381
- logger.debug " syntax highlighting using lang=#{lang}"
382
- if Uv.syntaxes.include?(lang)
383
- code = e.inner_html.sub(/^\s*#!\w+/, '').strip
384
-
385
- code.gsub!( "&lt;", "<" )
386
- code.gsub!( "&gt;", ">" )
387
- code.gsub!( "&amp;", "&" )
388
- # todo: missing any other entities? use CGI::unescapeHTML?
389
- logger.debug "code=>#{code}<"
390
-
391
- code_highlighted = Uv.parse( code, "xhtml", lang, opts.code_line_numbers?, opts.code_theme )
392
- # old code: e.inner_html = code_highlighted
393
- # todo: is it ok to replace the pre.code enclosing element to avoid duplicates?
394
- e.swap( code_highlighted )
395
- include_code_stylesheet = true
396
- end
397
- end
398
- end
399
- end
400
-
401
- content2 = doc.to_s
402
-
403
-
404
- out = File.new( outname, "w+" )
405
- out << render_template( headerdoc, params.params_binding )
406
- out << content2
407
- out << render_template( footerdoc, params.params_binding )
408
- out.flush
409
- out.close
410
-
411
- puts "Preparing slideshow stylesheet '#{cssname}'..."
412
-
413
- out = File.new( cssname, "w+" )
414
- out << render_template( styledoc, params.params_binding )
415
-
416
- if include_code_stylesheet
417
- logger.debug "cache_dir=#{cache_dir}"
418
-
419
- FileUtils.mkdir(cache_dir) unless File.exists?(cache_dir) if cache_dir
420
- Uv.copy_files "xhtml", cache_dir
421
-
422
- theme = opts.code_theme
423
-
424
- theme_content = File.read( "#{cache_dir}/css/#{theme}.css" )
425
-
426
- out << "/* styles for code syntax highlighting theme '#{theme}' */\n"
427
- out << "\n"
428
- out << theme_content
429
- end
430
-
431
- out.flush
432
- out.close
433
-
434
-
435
- if opts.s5?
436
- # copy s5 machinery to s5 folder
437
- # todo/fix: is there a better way to check if the dir exists?
438
- Dir.mkdir( 's5' ) unless File.directory?( 's5' )
439
-
440
- [ 'opera.css', 'outline.css', 'print.css', 's5-core.css', 'slides.js' ].each do |name|
441
-
442
- source = "#{File.dirname(__FILE__)}/templates/s5/#{name}"
443
- dest = "s5/#{name}"
444
-
445
- logger.debug "copying '#{source} ' to '#{dest}'"
446
- File.copy( source, dest )
447
- end
448
- elsif opts.fullerscreen?
449
- # do nothing; slideshow machinery built into plugin (requires install!)
450
- else
451
- # copy s6 machinery to s6 folder
452
- Dir.mkdir( 's6' ) unless File.directory?( 's6' )
453
-
454
- [ 'outline.css', 'print.css', 'slides.css', 'slides.js', 'jquery.js' ].each do |name|
455
-
456
- source = "#{File.dirname(__FILE__)}/templates/s6/#{name}"
457
- dest = "s6/#{name}"
458
-
459
- logger.debug "copying '#{source} ' to '#{dest}'"
460
- File.copy( source, dest )
461
- end
462
- end
463
-
464
- puts "Done."
465
- end
466
-
467
-
468
- def run( args )
469
-
470
- opt=OptionParser.new do |cmd|
471
-
472
- cmd.banner = "Usage: slideshow [options] name"
473
-
474
- #todo/fix: use -s5 option without optional hack? possible with OptionParser package/lib?
475
- # use -5 switch instead?
476
- cmd.on( '-s[OPTIONAL]', '--s5', 'S5 Compatible Slide Show' ) { opts.put( 's5', true ) }
477
- cmd.on( '-f[OPTIONAL]', '--fullerscreen', 'FullerScreen Compatible Slide Show' ) { opts.put( 'fuller', true ) }
478
- # opts.on( "-s", "--style STYLE", "Select Stylesheet" ) { |s| $options[:style]=s }
479
- # opts.on( "-v", "--version", "Show version" ) {}
480
-
481
- cmd.on( '-g', '--generate', 'Generate Slide Show Templates' ) { opts.put( 'generate', true ) }
482
- # use -d or -o to select output directory for slideshow or slideshow templates?
483
- # cmd.on( '-d', '--directory DIRECTORY', 'Output Directory' ) { |s| opts.put( 'directory', s ) }
484
- cmd.on( '-i', '--include PATH', 'Load Path' ) { |s| opts.put( 'include', s ) }
485
-
486
- cmd.on( "-t", "--trace", "Show debug trace" ) {
487
- logger.datetime_format = "%H:%H:%S"
488
- logger.level = Logger::DEBUG
489
- }
490
- cmd.on_tail( "-h", "--help", "Show this message" ) {
491
- puts
492
- puts "Slide Show (S9) is a free web alternative to PowerPoint or KeyNote in Ruby"
493
- puts
494
- puts cmd.help
495
- puts
496
- puts "Examples:"
497
- puts " slideshow microformats"
498
- puts " slideshow microformats.textile"
499
- puts " slideshow -s5 microformats # S5 compatible"
500
- puts " slideshow -f microformats # FullerScreen compatible"
501
- puts
502
- puts "More examles:"
503
- puts " slideshow -g # Generate slide show templates"
504
- puts " slideshow -g -s5 # Generate S5 compatible slide show templates"
505
- puts " slideshow -g -f # Generate FullerScreen compatible slide show templates"
506
- puts
507
- puts " slideshow -i . microformats # Use slide show templates in path ."
508
- puts " slideshow -i . -s5 microformats # Use S5 compatible slide show templates in path ."
509
- puts
510
- puts "Further information:"
511
- puts " http://slideshow.rubyforge.org"
512
- exit
513
- }
514
- end
515
-
516
- opt.parse!( args )
517
-
518
- puts "Slide Show (S9) Version: #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
519
-
520
- if opts.generate?
521
- create_slideshow_templates
522
- else
523
- args.each { |fn| create_slideshow( fn ) }
524
- end
525
- end
526
-
527
- end # class Gen
528
-
529
- def Slideshow.main
530
- Gen.new.run(ARGV)
531
- end
532
-
533
- end # module Slideshow
534
-
1
+ require 'optparse'
2
+ require 'erb'
3
+ require 'redcloth'
4
+ require 'maruku'
5
+ require 'logger'
6
+ require 'fileutils'
7
+ require 'ftools'
8
+ require 'hpricot'
9
+ require 'uv'
10
+
11
+
12
+ module Slideshow
13
+
14
+ VERSION = '0.6.1'
15
+
16
+ class Params
17
+
18
+ def initialize( name, headers )
19
+ @svgname = "#{name}.svg"
20
+ @cssname = "#{name}.css"
21
+ @headers = headers
22
+ end
23
+
24
+ def params_binding
25
+ binding
26
+ end
27
+
28
+ end
29
+
30
+ # todo: split (command line) options and headers?
31
+ # e.g. share (command line) options between slide shows (but not headers?)
32
+
33
+ class Opts
34
+
35
+ def initialize
36
+ @hash = {}
37
+ end
38
+
39
+ def put( key, value )
40
+ key = normalize_key( key )
41
+ setter = "#{key}=".to_sym
42
+
43
+ if respond_to? setter
44
+ send setter, value
45
+ else
46
+ @hash[ key ] = value
47
+ end
48
+ end
49
+
50
+ def code_theme=( value )
51
+ @hash[ :code_theme ] = value.tr( '-', '_' )
52
+ end
53
+
54
+ def gradient=( value )
55
+ put_gradient( value, :theme, :color1, :color2 )
56
+ end
57
+
58
+ def gradient_colors=( value )
59
+ put_gradient( value, :color1, :color2 )
60
+ end
61
+
62
+ def gradient_color=( value )
63
+ put_gradient( value, :color1 )
64
+ end
65
+
66
+ def gradient_theme=( value )
67
+ put_gradient( value, :theme )
68
+ end
69
+
70
+ def []( key )
71
+ value = @hash[ normalize_key( key ) ]
72
+ if value.nil?
73
+ puts "** Warning: header '#{key}' undefined"
74
+ "- #{key} not found -"
75
+ else
76
+ value
77
+ end
78
+ end
79
+
80
+ def generate?
81
+ get_boolean( 'generate', false )
82
+ end
83
+
84
+ def has_includes?
85
+ @hash[ :include ]
86
+ end
87
+
88
+ def includes
89
+ # fix: use os-agnostic delimiter (use : for Mac/Unix?)
90
+ has_includes? ? @hash[ :include ].split( ';' ) : []
91
+ end
92
+
93
+ def s5?
94
+ get_boolean( 's5', false )
95
+ end
96
+
97
+ def fullerscreen?
98
+ get_boolean( 'fuller', false ) || get_boolean( 'fullerscreen', false )
99
+ end
100
+
101
+ def code_theme
102
+ get( 'code-theme', DEFAULTS[ :code_theme ] )
103
+ end
104
+
105
+ def code_line_numbers?
106
+ get_boolean( 'code-line-numbers', DEFAULTS[ :code_line_numbers ] )
107
+ end
108
+
109
+ DEFAULTS =
110
+ {
111
+ :title => 'Untitled Slide Show',
112
+ :footer => '',
113
+ :subfooter => '',
114
+ :gradient_theme => 'dark',
115
+ :gradient_color1 => 'red',
116
+ :gradient_color2 => 'black',
117
+ :code_theme => 'amy',
118
+ :code_line_numbers => 'true'
119
+ }
120
+
121
+ def set_defaults
122
+ DEFAULTS.each_pair do | key, value |
123
+ @hash[ key ] = value if @hash[ key ].nil?
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def normalize_key( key )
130
+ key.to_s.downcase.tr('-', '_').to_sym
131
+ end
132
+
133
+ # Assigns the given gradient-* keys to the values in the given string.
134
+ def put_gradient( string, *keys )
135
+ values = string.split( ' ' )
136
+
137
+ values.zip(keys).each do |v, k|
138
+ @hash[ normalize_key( "gradient-#{k}" ) ] = v.tr( '-', '_' )
139
+ end
140
+ end
141
+
142
+ def get( key, default )
143
+ @hash.fetch( normalize_key(key), default )
144
+ end
145
+
146
+ def get_boolean( key, default )
147
+ value = @hash[ normalize_key( key ) ]
148
+ if value.nil?
149
+ default
150
+ else
151
+ (value == true || value =~ /true|yes|on/i) ? true : false
152
+ end
153
+ end
154
+
155
+ end # class Opts
156
+
157
+
158
+ class Gen
159
+
160
+ KNOWN_TEXTILE_EXTNAMES = [ '.textile', '.t' ]
161
+ KNOWN_MARKDOWN_EXTNAMES = [ '.markdown', '.mark', '.m', '.txt', '.text' ]
162
+
163
+ def initialize
164
+ @logger = Logger.new(STDOUT)
165
+ @logger.level = Logger::INFO
166
+ @opts = Opts.new
167
+ end
168
+
169
+ def logger
170
+ @logger
171
+ end
172
+
173
+ def opts
174
+ @opts
175
+ end
176
+
177
+ def cache_dir
178
+ PLATFORM =~ /win32/ ? win32_cache_dir : File.join(File.expand_path("~"), ".slideshow")
179
+ end
180
+
181
+ def win32_cache_dir
182
+ unless File.exists?(home = ENV['HOMEDRIVE'] + ENV['HOMEPATH'])
183
+ puts "No HOMEDRIVE or HOMEPATH environment variable. Set one to save a" +
184
+ "local cache of stylesheets for syntax highlighting and more."
185
+ return false
186
+ else
187
+ return File.join(home, 'slideshow')
188
+ end
189
+ end
190
+
191
+ def load_template( name, builtin )
192
+
193
+ if opts.has_includes?
194
+ opts.includes.each do |path|
195
+ logger.debug "File.exists? #{path}/#{name}"
196
+
197
+ if File.exists?( "#{path}/#{name}" ) then
198
+ puts "Loading custom template #{path}/#{name}..."
199
+ return File.read( "#{path}/#{name}" )
200
+ end
201
+ end
202
+ end
203
+
204
+ # fallback load builtin template packaged with gem
205
+ load_builtin_template( builtin )
206
+ end
207
+
208
+ def load_builtin_template( name )
209
+ templatesdir = "#{File.dirname(__FILE__)}/templates"
210
+ logger.debug "templatesdir=#{templatesdir}"
211
+
212
+ File.read( "#{templatesdir}/#{name}" )
213
+ end
214
+
215
+ def render_template( content, b=TOPLEVEL_BINDING )
216
+ ERB.new( content ).result( b )
217
+ end
218
+
219
+
220
+ def create_slideshow_templates
221
+
222
+ files =
223
+ case
224
+ when opts.s5?
225
+ [[ 's5/header.html.erb', 'header.html.erb' ],
226
+ [ 's5/footer.html.erb', 'footer.html.erb' ],
227
+ [ 's5/style.css.erb', 'style.css.erb' ]]
228
+ when opts.fullerscreen? # use fullerscreen templates
229
+ [[ 'header.html.erb', 'header.html.erb' ],
230
+ [ 'footer.html.erb', 'footer.html.erb' ],
231
+ [ 'style.css.erb', 'style.css.erb' ]]
232
+ else # use default s6 templates
233
+ [[ 's6/header.html.erb', 'header.html.erb' ],
234
+ [ 's6/footer.html.erb', 'footer.html.erb' ],
235
+ [ 's6/style.css.erb', 'style.css.erb' ]]
236
+ end
237
+
238
+ # background theming shared between s5/s6/fullerscreen
239
+ files << [ 'gradient.svg.erb', 'gradient.svg.erb' ]
240
+
241
+ files.each do |file|
242
+ source = "#{File.dirname(__FILE__)}/templates/#{file[0]}"
243
+ dest = "#{file[1]}"
244
+
245
+ puts "Copying '#{source}' to '#{dest}'"
246
+ File.copy( source, dest )
247
+ end
248
+
249
+ puts "Done."
250
+ end
251
+
252
+ def create_slideshow( fn )
253
+
254
+ if opts.s5?
255
+ headerdoc = load_template( 'header.html.erb', 's5/header.html.erb' )
256
+ footerdoc = load_template( 'footer.html.erb', 's5/footer.html.erb' )
257
+ styledoc = load_template( 'style.css.erb', 's5/style.css.erb' )
258
+ elsif opts.fullerscreen? # use fullerscreen templates
259
+ headerdoc = load_template( 'header.html.erb', 'header.html.erb' )
260
+ footerdoc = load_template( 'footer.html.erb', 'footer.html.erb' )
261
+ styledoc = load_template( 'style.css.erb', 'style.css.erb' )
262
+ else # use default s6 templates
263
+ headerdoc = load_template( 'header.html.erb', 's6/header.html.erb' )
264
+ footerdoc = load_template( 'footer.html.erb', 's6/footer.html.erb' )
265
+ styledoc = load_template( 'style.css.erb', 's6/style.css.erb' )
266
+ end
267
+
268
+ # background theming shared between s5/s6/fullerscreen
269
+ gradientdoc = load_template( 'gradient.svg.erb', 'gradient.svg.erb' )
270
+
271
+ basename = File.basename( fn, '.*' )
272
+ extname = File.extname( fn )
273
+
274
+ known_extnames = KNOWN_TEXTILE_EXTNAMES + KNOWN_MARKDOWN_EXTNAMES
275
+
276
+ if extname.empty? then
277
+ extname = ".textile" # default to .textile
278
+
279
+ known_extnames.each do |e|
280
+ logger.debug "File.exists? #{basename}#{e}"
281
+ if File.exists?( "#{basename}#{e}" ) then
282
+ extname = e
283
+ logger.debug "extname=#{extname}"
284
+ break
285
+ end
286
+ end
287
+ end
288
+
289
+ inname = "#{basename}#{extname}"
290
+ outname = "#{basename}.html"
291
+ svgname = "#{basename}.svg"
292
+ cssname = "#{basename}.css"
293
+
294
+ logger.debug "inname=#{inname}"
295
+
296
+ content = File.read( inname )
297
+
298
+ # read source document
299
+ # strip leading optional headers (key/value pairs) including optional empty lines
300
+
301
+ read_headers = true
302
+ content = ""
303
+
304
+ File.open( inname ).readlines.each do |line|
305
+ if read_headers && line =~ /^\s*(\w[\w-]*)[ \t]*:[ \t]*(.*)/
306
+ key = $1.downcase
307
+ value = $2.strip
308
+
309
+ logger.debug " adding option: key=>#{key}< value=>#{value}<"
310
+ opts.put( key, value )
311
+ elsif line =~ /^\s*$/
312
+ content << line unless read_headers
313
+ else
314
+ read_headers = false
315
+ content << line
316
+ end
317
+ end
318
+
319
+ # run pre-filters (built-in macros)
320
+ # o replace {{{ w/ <pre class='code'>
321
+ # o replace }}} w/ </pre>
322
+ content.gsub!( "{{{{{{", "<pre class='code'>_S9BEGIN_" )
323
+ content.gsub!( "}}}}}}", "_S9END_</pre>" )
324
+ content.gsub!( "{{{", "<pre class='code'>" )
325
+ content.gsub!( "}}}", "</pre>" )
326
+ # restore escaped {{{}}} I'm sure there's a better way! Rubyize this! Anyone?
327
+ content.gsub!( "_S9BEGIN_", "{{{" )
328
+ content.gsub!( "_S9END_", "}}}" )
329
+
330
+ opts.set_defaults
331
+
332
+ params = Params.new( basename, opts )
333
+
334
+ puts "Preparing slideshow theme '#{svgname}'..."
335
+
336
+ out = File.new( svgname, "w+" )
337
+ out << render_template( gradientdoc, params.params_binding )
338
+ out.flush
339
+ out.close
340
+
341
+ puts "Preparing slideshow '#{outname}'..."
342
+
343
+ # convert light-weight markup to hypertext
344
+
345
+ if KNOWN_MARKDOWN_EXTNAMES.include?( extname )
346
+ content = Maruku.new( content, {:on_error => :raise} ).to_html
347
+ # old code: content = BlueCloth.new( content ).to_html
348
+ else
349
+ # turn off hard line breaks
350
+ # turn off span caps (see http://rubybook.ca/2008/08/16/redcloth)
351
+ red = RedCloth.new( content, [:no_span_caps] )
352
+ red.hard_breaks = false
353
+ content = red.to_html
354
+ end
355
+
356
+
357
+ # post-processing
358
+
359
+ slide_counter = 0
360
+ content2 = ''
361
+
362
+ # wrap h1's in slide divs; note use just <h1 since some processors add ids e.g. <h1 id='x'>
363
+ content.each_line do |line|
364
+ if line.include?( '<h1' ) then
365
+ content2 << "\n\n</div>" if slide_counter > 0
366
+ content2 << "<div class='slide'>\n\n"
367
+ slide_counter += 1
368
+ end
369
+ content2 << line
370
+ end
371
+ content2 << "\n\n</div>" if slide_counter > 0
372
+
373
+ ## todo: run syntax highlighting before markup/textilize? lets us add textile to highlighted code?
374
+ ## avoid undoing escaped entities?
375
+
376
+ include_code_stylesheet = false
377
+ # syntax highlight code
378
+ # todo: can the code handle escaped entities? e.g. &gt;
379
+ doc = Hpricot(content2)
380
+ doc.search("pre.code, pre > code").each do |e|
381
+ if e.inner_html =~ /^\s*#!(\w+)/
382
+ lang = $1.downcase
383
+ if e.inner_html =~ /^\{\{\{/ # {{{ assumes escape/literal #!lang
384
+ # do nothing; next
385
+ logger.debug " skipping syntax highlighting using lang=#{lang}; assumimg escaped literal"
386
+ else
387
+ logger.debug " syntax highlighting using lang=#{lang}"
388
+ if Uv.syntaxes.include?(lang)
389
+ code = e.inner_html.sub(/^\s*#!\w+/, '').strip
390
+
391
+ code.gsub!( "&lt;", "<" )
392
+ code.gsub!( "&gt;", ">" )
393
+ code.gsub!( "&amp;", "&" )
394
+ # todo: missing any other entities? use CGI::unescapeHTML?
395
+ logger.debug "code=>#{code}<"
396
+
397
+ code_highlighted = Uv.parse( code, "xhtml", lang, opts.code_line_numbers?, opts.code_theme )
398
+ # old code: e.inner_html = code_highlighted
399
+ # todo: is it ok to replace the pre.code enclosing element to avoid duplicates?
400
+ e.swap( code_highlighted )
401
+ include_code_stylesheet = true
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ content2 = doc.to_s
408
+
409
+
410
+ out = File.new( outname, "w+" )
411
+ out << render_template( headerdoc, params.params_binding )
412
+ out << content2
413
+ out << render_template( footerdoc, params.params_binding )
414
+ out.flush
415
+ out.close
416
+
417
+ puts "Preparing slideshow stylesheet '#{cssname}'..."
418
+
419
+ out = File.new( cssname, "w+" )
420
+ out << render_template( styledoc, params.params_binding )
421
+
422
+ if include_code_stylesheet
423
+ logger.debug "cache_dir=#{cache_dir}"
424
+
425
+ FileUtils.mkdir(cache_dir) unless File.exists?(cache_dir) if cache_dir
426
+ Uv.copy_files "xhtml", cache_dir
427
+
428
+ theme = opts.code_theme
429
+
430
+ theme_content = File.read( "#{cache_dir}/css/#{theme}.css" )
431
+
432
+ out << "/* styles for code syntax highlighting theme '#{theme}' */\n"
433
+ out << "\n"
434
+ out << theme_content
435
+ end
436
+
437
+ out.flush
438
+ out.close
439
+
440
+
441
+ if opts.s5?
442
+ # copy s5 machinery to s5 folder
443
+ # todo/fix: is there a better way to check if the dir exists?
444
+ Dir.mkdir( 's5' ) unless File.directory?( 's5' )
445
+
446
+ [ 'opera.css', 'outline.css', 'print.css', 's5-core.css', 'slides.js' ].each do |name|
447
+
448
+ source = "#{File.dirname(__FILE__)}/templates/s5/#{name}"
449
+ dest = "s5/#{name}"
450
+
451
+ logger.debug "copying '#{source} ' to '#{dest}'"
452
+ File.copy( source, dest )
453
+ end
454
+ elsif opts.fullerscreen?
455
+ # do nothing; slideshow machinery built into plugin (requires install!)
456
+ else
457
+ # copy s6 machinery to s6 folder
458
+ Dir.mkdir( 's6' ) unless File.directory?( 's6' )
459
+
460
+ [ 'outline.css', 'print.css', 'slides.css', 'slides.js', 'jquery.js' ].each do |name|
461
+
462
+ source = "#{File.dirname(__FILE__)}/templates/s6/#{name}"
463
+ dest = "s6/#{name}"
464
+
465
+ logger.debug "copying '#{source} ' to '#{dest}'"
466
+ File.copy( source, dest )
467
+ end
468
+ end
469
+
470
+ puts "Done."
471
+ end
472
+
473
+
474
+ def run( args )
475
+
476
+ opt=OptionParser.new do |cmd|
477
+
478
+ cmd.banner = "Usage: slideshow [options] name"
479
+
480
+ #todo/fix: use -s5 option without optional hack? possible with OptionParser package/lib?
481
+ # use -5 switch instead?
482
+ cmd.on( '-s[OPTIONAL]', '--s5', 'S5 Compatible Slide Show' ) { opts.put( 's5', true ) }
483
+ cmd.on( '-f[OPTIONAL]', '--fullerscreen', 'FullerScreen Compatible Slide Show' ) { opts.put( 'fuller', true ) }
484
+ # opts.on( "-s", "--style STYLE", "Select Stylesheet" ) { |s| $options[:style]=s }
485
+ # opts.on( "-v", "--version", "Show version" ) {}
486
+
487
+ cmd.on( '-g', '--generate', 'Generate Slide Show Templates' ) { opts.put( 'generate', true ) }
488
+ # use -d or -o to select output directory for slideshow or slideshow templates?
489
+ # cmd.on( '-d', '--directory DIRECTORY', 'Output Directory' ) { |s| opts.put( 'directory', s ) }
490
+ cmd.on( '-i', '--include PATH', 'Load Path' ) { |s| opts.put( 'include', s ) }
491
+
492
+ cmd.on( "-t", "--trace", "Show debug trace" ) {
493
+ logger.datetime_format = "%H:%H:%S"
494
+ logger.level = Logger::DEBUG
495
+ }
496
+ cmd.on_tail( "-h", "--help", "Show this message" ) {
497
+ puts
498
+ puts "Slide Show (S9) is a free web alternative to PowerPoint or KeyNote in Ruby"
499
+ puts
500
+ puts cmd.help
501
+ puts
502
+ puts "Examples:"
503
+ puts " slideshow microformats"
504
+ puts " slideshow microformats.textile"
505
+ puts " slideshow -s5 microformats # S5 compatible"
506
+ puts " slideshow -f microformats # FullerScreen compatible"
507
+ puts
508
+ puts "More examles:"
509
+ puts " slideshow -g # Generate slide show templates"
510
+ puts " slideshow -g -s5 # Generate S5 compatible slide show templates"
511
+ puts " slideshow -g -f # Generate FullerScreen compatible slide show templates"
512
+ puts
513
+ puts " slideshow -i . microformats # Use slide show templates in path ."
514
+ puts " slideshow -i . -s5 microformats # Use S5 compatible slide show templates in path ."
515
+ puts
516
+ puts "Further information:"
517
+ puts " http://slideshow.rubyforge.org"
518
+ exit
519
+ }
520
+ end
521
+
522
+ opt.parse!( args )
523
+
524
+ puts "Slide Show (S9) Version: #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
525
+
526
+ if opts.generate?
527
+ create_slideshow_templates
528
+ else
529
+ args.each { |fn| create_slideshow( fn ) }
530
+ end
531
+ end
532
+
533
+ end # class Gen
534
+
535
+ def Slideshow.main
536
+ Gen.new.run(ARGV)
537
+ end
538
+
539
+ end # module Slideshow
540
+
535
541
  Slideshow.main if __FILE__ == $0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slideshow
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.6"
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-26 00:00:00 -07:00
12
+ date: 2008-08-30 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency