slideshow 0.6 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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