slideshow 0.8.5 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,143 @@
1
+ # builtin text filters
2
+ # called before text_to_html
3
+ #
4
+ # use web filters for processing html/hypertext
5
+
6
+ module TextFilter
7
+
8
+ def comments_percent_style( content )
9
+ # remove comments
10
+ # % comments
11
+ # %begin multiline comment
12
+ # %end multiline comment
13
+
14
+ # track statistics
15
+ comments_multi = 0
16
+ comments_single = 0
17
+ comments_end = 0
18
+
19
+ # remove multi-line comments
20
+ content.gsub!(/^%begin.*?%end/m) do |match|
21
+ comments_multi += 1
22
+ ""
23
+ end
24
+
25
+ # remove everyting starting w/ %end (note, can only be once in file)
26
+ content.sub!(/^%end.*/m) do |match|
27
+ comments_end += 1
28
+ ""
29
+ end
30
+
31
+ # remove single-line comments
32
+ content.gsub!(/^%.*/ ) do |match|
33
+ comments_single += 1
34
+ ""
35
+ end
36
+
37
+ puts " Removing comments (#{comments_single} %-lines, " +
38
+ "#{comments_multi} %begin/%end-blocks, #{comments_end} %end-blocks)..."
39
+
40
+ content
41
+ end
42
+
43
+ def skip_end_directive( content )
44
+ # codex-style __SKIP__, __END__ directive
45
+ # ruby note: .*? is non-greedy (shortest-possible) regex match
46
+ content.gsub!(/__SKIP__.*?__END__/m, '')
47
+ content.sub!(/__END__.*/m, '')
48
+ content
49
+ end
50
+
51
+ def include_helper_hack( content )
52
+ # note: include is a ruby keyword; rename to __include__ so we can use it
53
+
54
+ include_counter = 0
55
+
56
+ content.gsub!( /<%=[ \t]*include/ ) do |match|
57
+ include_counter += 1
58
+ '<%= __include__'
59
+ end
60
+
61
+ puts " Patching embedded Ruby (erb) code aliases (#{include_counter} include)..."
62
+
63
+ content
64
+ end
65
+
66
+ # allow plugins/helpers; process source (including header) using erb
67
+ def erb( content )
68
+ puts " Running embedded Ruby (erb) code/helpers..."
69
+
70
+ content = ERB.new( content ).result( binding() )
71
+ content
72
+ end
73
+
74
+ def erb_django_style( content )
75
+
76
+ # replace expressions (support for single lines only)
77
+ # {{ expr }} -> <%= expr %>
78
+ # {% stmt %} -> <% stmt %> !! add in do if missing (for convenience)
79
+
80
+ erb_expr = 0
81
+ erb_stmt_beg = 0
82
+ erb_stmt_end = 0
83
+
84
+ content.gsub!( /\{\{([^{}\n]+?)\}\}/ ) do |match|
85
+ erb_expr += 1
86
+ "<%= #{$1} %>"
87
+ end
88
+
89
+ content.gsub!( /\{%[ \t]*end[ \t]*%\}/ ) do |match|
90
+ erb_stmt_end += 1
91
+ "<% end %>"
92
+ end
93
+
94
+ content.gsub!( /\{%([^%\n]+?)%\}/ ) do |match|
95
+ erb_stmt_beg += 1
96
+ if $1.include?('do')
97
+ "<% #{$1} %>"
98
+ else
99
+ "<% #{$1} do %>"
100
+ end
101
+ end
102
+
103
+ puts " Patching embedded Ruby (erb) code Django-style (#{erb_expr} {{-expressions," +
104
+ " #{erb_stmt_beg}/#{erb_stmt_end} {%-statements)..."
105
+
106
+ content
107
+ end
108
+
109
+ def code_block_curly_style( content )
110
+ # replace {{{ w/ <pre class='code'>
111
+ # replace }}} w/ </pre>
112
+
113
+ # track statistics
114
+ code_begin = 0
115
+ code_end = 0
116
+
117
+ content.gsub!( "{{{{{{", "<pre class='code'>_S9BEGIN_" )
118
+ content.gsub!( "}}}}}}", "_S9END_</pre>" )
119
+
120
+ content.gsub!( "{{{" ) do |match|
121
+ code_begin += 1
122
+ "<pre class='code'>"
123
+ end
124
+
125
+ content.gsub!( "}}}" ) do |match|
126
+ code_end += 1
127
+ "</pre>"
128
+ end
129
+
130
+ # restore escaped {{{}}}
131
+ content.gsub!( "_S9BEGIN_", "{{{" )
132
+ content.gsub!( "_S9END_", "}}}" )
133
+
134
+ puts " Patching code blocks (#{code_begin}/#{code_end} {{{/}}}-lines)..."
135
+
136
+ content
137
+ end
138
+
139
+ end
140
+
141
+ class Slideshow::Gen
142
+ include TextFilter
143
+ end
@@ -1,38 +1,15 @@
1
-
2
-
3
-
4
1
  module Slideshow
5
2
 
6
- class Slide < Struct.new(:header, :content)
7
- end
8
-
9
3
  class Gen
10
-
11
- KNOWN_TEXTILE_EXTNAMES = [ '.textile', '.t' ]
12
- KNOWN_MARKDOWN_EXTNAMES = [ '.markdown', '.m', '.mark', '.mkdn', '.md', '.txt', '.text' ]
13
- KNOWN_EXTNAMES = KNOWN_TEXTILE_EXTNAMES + KNOWN_MARKDOWN_EXTNAMES
14
-
15
- # note: only kramdown is listed as a dependency in gem specs (because it's Ruby only and, thus, easy to install)
16
- # if you want to use other markdown libs install the required/desired lib e.g.
17
- # use gem install rdiscount for rdiscount and so on
18
- #
19
- # also note for now the first present markdown library gets used
20
- # the search order is first come, first serve, that is: rdiscount, rpeg-markdown, maruku, bluecloth, kramdown (fallback, always present)
21
- KNOWN_MARKDOWN_LIBS = [
22
- [ 'rdiscount', lambda { |content| RDiscount.new( content ).to_html } ],
23
- [ 'rpeg-markdown', lambda { |content| PEGMarkdown.new( content ).to_html } ],
24
- [ 'maruku', lambda { |content| Maruku.new( content, {:on_error => :raise} ).to_html } ],
25
- [ 'bluecloth', lambda { |content| BlueCloth.new( content ).to_html } ],
26
- [ 'kramdown', lambda { |content| Kramdown::Document.new( content ).to_html } ]
27
- ]
28
4
 
29
5
  def initialize
30
6
  @logger = Logger.new(STDOUT)
31
7
  @logger.level = Logger::INFO
32
- @opts = Opts.new
8
+ @opts = Opts.new
9
+ @config = Config.new
33
10
  end
34
11
 
35
- attr_reader :logger, :opts
12
+ attr_reader :logger, :opts, :config
36
13
  attr_reader :session # give helpers/plugins a session-like hash
37
14
 
38
15
  def headers
@@ -47,42 +24,47 @@ class Gen
47
24
  # try to require each lib and remove any not installed
48
25
  @markdown_libs = []
49
26
 
50
- KNOWN_MARKDOWN_LIBS.each do |lib|
27
+ config.known_markdown_libs.each do |lib|
51
28
  begin
52
- require lib[0]
29
+ require lib
53
30
  @markdown_libs << lib
54
31
  rescue LoadError => ex
55
- logger.debug "Markdown library #{lib[0]} not found. Use gem install #{lib[0]} to install."
32
+ logger.debug "Markdown library #{lib} not found. Use gem install #{lib} to install."
56
33
  end
57
34
  end
58
35
 
59
- logger.debug "Installed Markdown libraries: #{@markdown_libs.map{ |lib| lib[0] }.join(', ')}"
60
- logger.debug "Using Markdown library #{@markdown_libs.first[0]}."
36
+ puts " Found #{@markdown_libs.length} Markdown libraries: #{@markdown_libs.join(', ')}"
61
37
  end
38
+
62
39
 
63
- # todo: move to filter (for easier reuse)
64
40
  def markdown_to_html( content )
65
- @markdown_libs.first[1].call( content )
66
- end
67
-
68
- # todo: move to filter (for easier reuse)
69
- def textile_to_html( content )
41
+ # call markdown filter; turn markdown lib name into method_name (mn)
42
+ # eg. rpeg-markdown => rpeg_markdown_to_html
70
43
 
71
- # JRuby workaround for RedCloth 4 multi-byte character bug
72
- # see http://jgarber.lighthouseapp.com/projects/13054/tickets/149-redcloth-4-doesnt-support-multi-bytes-content
73
- # basically convert non-ascii chars (>127) to html entities
74
-
75
- if RedCloth::EXTENSION_LANGUAGE == "Java"
76
- content = content.chars.map{ |x| x.size > 1 ? "&##{x.unpack("U*")};" : x }.join
44
+ puts " Converting Markdown-text (#{content.length} bytes) to HTML using library '#{@markdown_libs.first}'..."
45
+
46
+ mn = @markdown_libs.first.downcase.tr( '-', '_' )
47
+ mn = "#{mn}_to_html".to_sym
48
+ send mn, content # call 1st configured markdown engine e.g. kramdown_to_html( content )
49
+ end
50
+
51
+ # uses configured markup processor (textile,markdown) to generate html
52
+ def text_to_html( content )
53
+ content = case @markup_type
54
+ when :markdown
55
+ markdown_to_html( content )
56
+ when :textile
57
+ textile_to_html( content )
77
58
  end
78
-
79
- # turn off hard line breaks
80
- # turn off span caps (see http://rubybook.ca/2008/08/16/redcloth)
81
- red = RedCloth.new( content, [:no_span_caps] )
82
- red.hard_breaks = false
83
- content = red.to_html
59
+ content
84
60
  end
85
-
61
+
62
+ def guard_text( text )
63
+ # todo/fix: remove wrap_markup; replace w/ guard_text
64
+ # why: text might be css, js, not just html
65
+ wrap_markup( text )
66
+ end
67
+
86
68
  def wrap_markup( text )
87
69
  if markup_type == :textile
88
70
  # saveguard with notextile wrapper etc./no further processing needed
@@ -97,7 +79,7 @@ class Gen
97
79
  end
98
80
 
99
81
  def win32_cache_dir
100
- unless File.exists?(home = ENV['HOMEDRIVE'] + ENV['HOMEPATH'])
82
+ unless ENV['HOMEDRIVE'] && ENV['HOMEPATH'] && File.exists?(home = ENV['HOMEDRIVE'] + ENV['HOMEPATH'])
101
83
  puts "No HOMEDRIVE or HOMEPATH environment variable. Set one to save a" +
102
84
  "local cache of stylesheets for syntax highlighting and more."
103
85
  return false
@@ -277,9 +259,14 @@ class Gen
277
259
  logger.debug "using direct net http access; no proxy configured"
278
260
  proxy = OpenStruct.new # all fields return nil (e.g. proxy.host, etc.)
279
261
  end
262
+
263
+ # same as short-cut: http_proxy.get_respone( uri )
264
+ # use full code for easier changes
280
265
 
281
- http = Net::HTTP::Proxy( proxy.host, proxy.port, proxy.user, proxy.password )
282
- response = http.get_response( uri )
266
+ http_proxy = Net::HTTP::Proxy( proxy.host, proxy.port, proxy.user, proxy.password )
267
+ http = http_proxy.new( uri.host, uri.port )
268
+ request = Net::HTTP::Get.new( uri.request_uri )
269
+ response = http.request( request )
283
270
 
284
271
  unless response.code == '200' # note: responsoe.code is a string
285
272
  msg = "#{response.code} #{response.message}"
@@ -306,7 +293,21 @@ class Gen
306
293
  def fetch_slideshow_templates
307
294
  logger.debug "fetch_uri=#{opts.fetch_uri}"
308
295
 
309
- src = opts.fetch_uri
296
+ src = opts.fetch_uri
297
+
298
+ ## check for builtin shortcut (assume no / or \)
299
+ if src.index( '/' ).nil? && src.index( '\\' ).nil?
300
+ shortcut = src.clone
301
+ src = config.map_fetch_shortcut( src )
302
+
303
+ if src.nil?
304
+ puts "** Error: No mapping found for fetch shortcut '#{shortcut}'."
305
+ return
306
+ end
307
+ puts " Mapping fetch shortcut '#{shortcut}' to: #{src}"
308
+ end
309
+
310
+
310
311
  # src = 'http://github.com/geraldb/slideshow/raw/d98e5b02b87ee66485431b1bee8fb6378297bfe4/code/templates/fullerscreen.txt'
311
312
  # src = 'http://github.com/geraldb/sandbox/raw/13d4fec0908fbfcc456b74dfe2f88621614b5244/s5blank/s5blank.txt'
312
313
  uri = URI.parse( src )
@@ -409,6 +410,11 @@ class Gen
409
410
  def create_slideshow( fn )
410
411
 
411
412
  manifest_path_or_name = opts.manifest
413
+
414
+ # add .txt file extension if missing (for convenience)
415
+ manifest_path_or_name << ".txt" if File.extname( manifest_path_or_name ).empty?
416
+
417
+
412
418
  logger.debug "manifest=#{manifest_path_or_name}"
413
419
 
414
420
  # check if file exists (if yes use custom template package!) - allows you to override builtin package with same name
@@ -455,7 +461,7 @@ class Gen
455
461
  if extname.empty? then
456
462
  extname = ".textile" # default to .textile
457
463
 
458
- KNOWN_EXTNAMES.each do |e|
464
+ config.known_extnames.each do |e|
459
465
  logger.debug "File.exists? #{dirname}/#{basename}#{e}"
460
466
  if File.exists?( "#{dirname}/#{basename}#{e}" ) then
461
467
  extname = e
@@ -465,7 +471,7 @@ class Gen
465
471
  end
466
472
  end
467
473
 
468
- if KNOWN_MARKDOWN_EXTNAMES.include?( extname )
474
+ if config.known_markdown_extnames.include?( extname )
469
475
  @markup_type = :markdown
470
476
  else
471
477
  @markup_type = :textile
@@ -482,113 +488,94 @@ class Gen
482
488
 
483
489
  logger.debug "inname=#{inname}"
484
490
 
485
- content_with_headers = File.read( inname )
491
+ content = File.read( inname )
486
492
 
487
- # todo: read headers before command line options (lets you override options using commandline switch)?
488
-
489
- # read source document; split off optional header from source
490
- # strip leading optional headers (key/value pairs) including optional empty lines
493
+ # fix: add to comment text filter as first rule!!
494
+ # check for !SLIDE alias %slide (needs to get coverted to ! otherwise
495
+ # it gets removed as a comment)
496
+ content.gsub!(/^%slide/, '!SLIDE' )
491
497
 
492
- read_headers = true
493
- content = ""
498
+ # run text filters
494
499
 
495
- # fix: allow comments in header too (#)
496
-
497
- content_with_headers.each_line do |line|
498
- if read_headers && line =~ /^\s*(\w[\w-]*)[ \t]*:[ \t]*(.*)/
499
- key = $1.downcase
500
- value = $2.strip
501
-
502
- logger.debug " adding option: key=>#{key}< value=>#{value}<"
503
- opts.put( key, value )
504
- elsif line =~ /^\s*$/
505
- content << line unless read_headers
506
- else
507
- read_headers = false
508
- content << line
509
- end
500
+ config.text_filters.each do |filter|
501
+ mn = filter.tr( '-', '_' ).to_sym # construct method name (mn)
502
+ content = send( mn, content ) # call filter e.g. include_helper_hack( content )
510
503
  end
511
504
 
512
- opts.set_defaults
513
-
514
- # ruby note: .*? is non-greedy (shortest-possible) regex match
515
- content.gsub!(/__SKIP__.*?__END__/m, '')
516
- content.sub!(/__END__.*/m, '')
517
-
518
- # allow plugins/helpers; process source (including header) using erb
505
+ # check for !SLIDE markers; change to HTML comments
519
506
 
520
- # note: include is a ruby keyword; rename to __include__ so we can use it
521
- content.gsub!( /<%=[ \t]*include/, '<%= __include__' )
507
+ # -- slide marker stats
508
+ slide_markers = 0
522
509
 
523
- content = ERB.new( content ).result( binding )
510
+ ## todo: use html processing instruction <?s9 slide ?>
511
+ content.gsub!(/^!SLIDE(.*)/ ) do |match|
512
+ slide_markers += 1
513
+ "<!-- _S9SLIDE_ #{$1} -->"
514
+ end
524
515
 
525
- # run pre-filters (built-in macros)
526
- # o replace {{{ w/ <pre class='code'>
527
- # o replace }}} w/ </pre>
528
- content.gsub!( "{{{{{{", "<pre class='code'>_S9BEGIN_" )
529
- content.gsub!( "}}}}}}", "_S9END_</pre>" )
530
- content.gsub!( "{{{", "<pre class='code'>" )
531
- content.gsub!( "}}}", "</pre>" )
532
- # restore escaped {{{}}} I'm sure there's a better way! Rubyize this! Anyone?
533
- content.gsub!( "_S9BEGIN_", "{{{" )
534
- content.gsub!( "_S9END_", "}}}" )
516
+ puts " Processing directives (#{slide_markers} !SLIDE-directives)..."
517
+
535
518
 
536
519
  # convert light-weight markup to hypertext
537
520
 
538
- content = case @markup_type
539
- when :markdown
540
- markdown_to_html( content )
541
- when :textile
542
- textile_to_html( content )
543
- end
544
-
521
+ content = text_to_html( content )
522
+
545
523
  # post-processing
546
524
 
547
525
  slide_counter = 0
548
- content2 = ''
549
-
550
- ## todo: move this to a filter (for easier reuse)
551
526
 
552
527
  slides = []
553
528
  slide_source = ""
554
529
 
555
530
  # wrap h1's in slide divs; note: use just <h1 since some processors add ids e.g. <h1 id='x'>
556
531
  content.each_line do |line|
557
- if line.include?( '<h1' ) then
532
+ if line.include?( '<h1' ) || line.include?( '<!-- _S9SLIDE_' ) then
558
533
  if slide_counter > 0 then # found start of new slide (and, thus, end of last slide)
559
- content2 << "</div>\n"
560
534
  slides << slide_source # add slide to slide stack
561
- slide_source = "" # reset slide source buffer
535
+ slide_source = "" # reset slide source buffer
562
536
  end
563
- content2 << "<div class='slide'>\n"
564
537
  slide_counter += 1
565
538
  end
566
- content2 << line
567
539
  slide_source << line
568
540
  end
569
541
 
570
542
  if slide_counter > 0 then
571
- content2 << "</div>\n"
572
543
  slides << slide_source # add slide to slide stack
573
544
  slide_source = "" # reset slide source buffer
574
545
  end
575
546
 
576
547
  ## split slide source into header (optional) and content/body
548
+ ## plus check for classes
577
549
 
578
550
  slides2 = []
579
551
  slides.each do |slide_source|
580
552
  slide = Slide.new
581
- if slide_source =~ /^\s*(<h.+?>.*?<\/h\d>)\s+(.*)/m # try to cut off header using non-greedy .+? pattern; tip test regex online at rubular.com
553
+
554
+ ## check for css style class
555
+ if slide_source =~ /<!-- _S9SLIDE_(.*?)-->/
556
+ slide.classes = $1.strip
557
+ logger.debug " adding css classes: #{slide.classes}"
558
+ end
559
+
560
+ if slide_source =~ /^\s*(<h1.+?>.*?<\/h\d>)\s*(.*)/m # try to cut off header using non-greedy .+? pattern; tip test regex online at rubular.com
582
561
  slide.header = $1
583
- slide.content = $2
562
+ slide.content = ($2 ? $2 : "")
584
563
  logger.debug " adding slide with header:\n#{slide.header}"
585
- else
564
+ else
586
565
  slide.content = slide_source
587
566
  logger.debug " adding slide with *no* header"
588
567
  end
589
568
  slides2 << slide
590
569
  end
591
570
 
571
+ # for convenience create a string w/ all in-one-html
572
+ # no need to wrap slides in divs etc.
573
+
574
+ content2 = ""
575
+ slides2.each do |slide|
576
+ content2 << slide.to_classic_html
577
+ end
578
+
592
579
  # make content2 and slide2 available to erb template
593
580
  # -- todo: cleanup variable names and use attr_readers for content and slide
594
581
 
@@ -653,19 +640,13 @@ def run( args )
653
640
 
654
641
  cmd.on( '-o', '--output PATH', 'Output Path' ) { |s| opts.put( 'output', s ) }
655
642
 
656
- cmd.on( '-g', '--generate', 'Generate Slide Show Templates' ) { opts.put( 'generate', true ) }
643
+ cmd.on( '-g', '--generate', 'Generate Slide Show Templates (Using Built-In S6 Pack)' ) { opts.put( 'generate', true ) }
657
644
 
658
645
  cmd.on( "-t", "--template MANIFEST", "Template Manifest" ) do |t|
659
646
  # todo: do some checks on passed in template argument
660
647
  opts.put( 'manifest', t )
661
648
  end
662
649
 
663
- # shortcut: same as -t s5.txt
664
- cmd.on( '--s5', 'S5-Compatible Slide Show (same as -t s5.txt)' ) { opts.put( 's5', true ); opts.put( 'manifest', 's5.txt' ) }
665
-
666
- # shortcut: same as -t fullerscreen.txt
667
- cmd.on( '--fullerscreen', 'FullerScreen-Compatible Slide Show (same as -t fullerscreen.txt)' ) { opts.put( 'fuller', true ); opts.put( 'manifest', 'fullerscreen.txt' ) }
668
-
669
650
  # ?? opts.on( "-s", "--style STYLE", "Select Stylesheet" ) { |s| $options[:style]=s }
670
651
  # ?? opts.on( "--version", "Show version" ) {}
671
652
 
@@ -695,19 +676,16 @@ def run( args )
695
676
  puts
696
677
  puts "Examples:"
697
678
  puts " slideshow microformats"
698
- puts " slideshow microformats.textile"
679
+ puts " slideshow microformats.textile # Process slides using Textile"
680
+ puts " slideshow microformats.text # Process slides using Markdown"
699
681
  puts " slideshow -o slides microformats # Output slideshow to slides folder"
700
- puts " slideshow --s5 microformats # Use S5-compatible slide show templates"
701
- puts " slideshow --fullerscreen microformats # Use FullerScreen-compatible slide show templates"
702
682
  puts
703
683
  puts "More examles:"
704
- puts " slideshow -g # Generate slide show templates"
705
- puts " slideshow -g --s5 # Generate S5 compatible slide show templates"
706
- puts " slideshow -g --fullerscreen # Generate FullerScreen compatible slide show templates"
684
+ puts " slideshow -g # Generate slide show templates using built-in S6 pack"
707
685
  puts
708
686
  puts " slideshow -l # List installed slide show templates"
709
687
  puts " slideshow -f s5blank # Fetch (install) S5 blank starter template from internet"
710
- puts " slideshow -t s5blank.txt microformats # Use your own slide show templates (e.g. s5blank.txt)"
688
+ puts " slideshow -t s5blank microformats # Use your own slide show templates (e.g. s5blank)"
711
689
  puts
712
690
  puts "Further information:"
713
691
  puts " http://slideshow.rubyforge.org"
@@ -716,6 +694,8 @@ def run( args )
716
694
  end
717
695
 
718
696
  opt.parse!( args )
697
+
698
+ config.load
719
699
 
720
700
  puts "Slide Show (S9) Version: #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
721
701