showoffer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (206) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +559 -0
  3. data/Rakefile +27 -0
  4. data/bin/showoffer +189 -0
  5. data/lib/commandline_parser.rb +67 -0
  6. data/lib/showoff.rb +515 -0
  7. data/lib/showoff/version.rb +3 -0
  8. data/lib/showoff_utils.rb +360 -0
  9. data/public/css/960.css +653 -0
  10. data/public/css/fg.menu.css +114 -0
  11. data/public/css/onepage.css +60 -0
  12. data/public/css/pdf.css +12 -0
  13. data/public/css/presenter.css +76 -0
  14. data/public/css/reset.css +53 -0
  15. data/public/css/sh_style.css +66 -0
  16. data/public/css/showoff.css +403 -0
  17. data/public/css/spinner_bar.gif +0 -0
  18. data/public/css/theme/images/ui-bg_diagonals-small_100_f0efea_40x40.png +0 -0
  19. data/public/css/theme/images/ui-bg_flat_35_f0f0f0_40x100.png +0 -0
  20. data/public/css/theme/images/ui-bg_glass_55_fcf0ba_1x400.png +0 -0
  21. data/public/css/theme/images/ui-bg_glow-ball_25_2e2e28_600x600.png +0 -0
  22. data/public/css/theme/images/ui-bg_highlight-soft_100_f0efea_1x100.png +0 -0
  23. data/public/css/theme/images/ui-bg_highlight-soft_25_327E04_1x100.png +0 -0
  24. data/public/css/theme/images/ui-bg_highlight-soft_25_5A9D1A_1x100.png +0 -0
  25. data/public/css/theme/images/ui-bg_highlight-soft_95_ffedad_1x100.png +0 -0
  26. data/public/css/theme/images/ui-bg_inset-soft_22_3b3b35_1x100.png +0 -0
  27. data/public/css/theme/images/ui-icons_808080_256x240.png +0 -0
  28. data/public/css/theme/images/ui-icons_8DC262_256x240.png +0 -0
  29. data/public/css/theme/images/ui-icons_cd0a0a_256x240.png +0 -0
  30. data/public/css/theme/images/ui-icons_e7e6e4_256x240.png +0 -0
  31. data/public/css/theme/images/ui-icons_eeeeee_256x240.png +0 -0
  32. data/public/css/theme/images/ui-icons_ffffff_256x240.png +0 -0
  33. data/public/css/theme/ui.accordion.css +9 -0
  34. data/public/css/theme/ui.all.css +2 -0
  35. data/public/css/theme/ui.base.css +9 -0
  36. data/public/css/theme/ui.core.css +37 -0
  37. data/public/css/theme/ui.datepicker.css +62 -0
  38. data/public/css/theme/ui.dialog.css +13 -0
  39. data/public/css/theme/ui.progressbar.css +4 -0
  40. data/public/css/theme/ui.resizable.css +13 -0
  41. data/public/css/theme/ui.slider.css +17 -0
  42. data/public/css/theme/ui.tabs.css +9 -0
  43. data/public/css/theme/ui.theme.css +245 -0
  44. data/public/favicon.ico +0 -0
  45. data/public/highlight/AUTHORS.en.txt +46 -0
  46. data/public/highlight/AUTHORS.ru.txt +46 -0
  47. data/public/highlight/LICENSE +24 -0
  48. data/public/highlight/README.md +136 -0
  49. data/public/highlight/README.ru.md +140 -0
  50. data/public/highlight/classref.txt +437 -0
  51. data/public/highlight/export.html +87 -0
  52. data/public/highlight/highlight.js +630 -0
  53. data/public/highlight/highlight.pack.js +1 -0
  54. data/public/highlight/languages/1c.js +68 -0
  55. data/public/highlight/languages/apache.js +432 -0
  56. data/public/highlight/languages/avrasm.js +75 -0
  57. data/public/highlight/languages/axapta.js +43 -0
  58. data/public/highlight/languages/bash.js +56 -0
  59. data/public/highlight/languages/cmake.js +24 -0
  60. data/public/highlight/languages/cpp.js +62 -0
  61. data/public/highlight/languages/cs.js +41 -0
  62. data/public/highlight/languages/css.js +101 -0
  63. data/public/highlight/languages/delphi.js +70 -0
  64. data/public/highlight/languages/diff.js +64 -0
  65. data/public/highlight/languages/django.js +72 -0
  66. data/public/highlight/languages/dos.js +29 -0
  67. data/public/highlight/languages/erlang-repl.js +81 -0
  68. data/public/highlight/languages/erlang.js +201 -0
  69. data/public/highlight/languages/go.js +58 -0
  70. data/public/highlight/languages/haskell.js +77 -0
  71. data/public/highlight/languages/ini.js +32 -0
  72. data/public/highlight/languages/java.js +45 -0
  73. data/public/highlight/languages/javascript.js +53 -0
  74. data/public/highlight/languages/lisp.js +87 -0
  75. data/public/highlight/languages/lua.js +75 -0
  76. data/public/highlight/languages/mel.js +40 -0
  77. data/public/highlight/languages/nginx.js +224 -0
  78. data/public/highlight/languages/objectivec.js +94 -0
  79. data/public/highlight/languages/parser3.js +52 -0
  80. data/public/highlight/languages/perl.js +143 -0
  81. data/public/highlight/languages/php.js +55 -0
  82. data/public/highlight/languages/profile.js +49 -0
  83. data/public/highlight/languages/python.js +71 -0
  84. data/public/highlight/languages/renderman.js +230 -0
  85. data/public/highlight/languages/ruby.js +203 -0
  86. data/public/highlight/languages/scala.js +60 -0
  87. data/public/highlight/languages/smalltalk.js +55 -0
  88. data/public/highlight/languages/sql.js +90 -0
  89. data/public/highlight/languages/tex.js +62 -0
  90. data/public/highlight/languages/vala.js +75 -0
  91. data/public/highlight/languages/vbscript.js +30 -0
  92. data/public/highlight/languages/vhdl.js +30 -0
  93. data/public/highlight/languages/xml.js +103 -0
  94. data/public/highlight/styles/arta.css +151 -0
  95. data/public/highlight/styles/ascetic.css +45 -0
  96. data/public/highlight/styles/brown_paper.css +105 -0
  97. data/public/highlight/styles/brown_papersq.png +0 -0
  98. data/public/highlight/styles/dark.css +103 -0
  99. data/public/highlight/styles/default.css +121 -0
  100. data/public/highlight/styles/far.css +118 -0
  101. data/public/highlight/styles/github.css +129 -0
  102. data/public/highlight/styles/idea.css +118 -0
  103. data/public/highlight/styles/ir_black.css +103 -0
  104. data/public/highlight/styles/magula.css +118 -0
  105. data/public/highlight/styles/school_book.css +111 -0
  106. data/public/highlight/styles/school_book.png +0 -0
  107. data/public/highlight/styles/solarized_dark.css +96 -0
  108. data/public/highlight/styles/solarized_light.css +96 -0
  109. data/public/highlight/styles/sunburst.css +147 -0
  110. data/public/highlight/styles/vs.css +84 -0
  111. data/public/highlight/styles/zenburn.css +115 -0
  112. data/public/highlight/test.html +1609 -0
  113. data/public/js/coffee-script.js +8 -0
  114. data/public/js/core.js +79 -0
  115. data/public/js/fg.menu.js +645 -0
  116. data/public/js/jTypeWriter.js +26 -0
  117. data/public/js/jquery-1.4.2.min.js +154 -0
  118. data/public/js/jquery-print.js +109 -0
  119. data/public/js/jquery.batchImageLoad.js +56 -0
  120. data/public/js/jquery.cookie.js +96 -0
  121. data/public/js/jquery.cycle.all.js +1284 -0
  122. data/public/js/jquery.doubletap-0.1.js +105 -0
  123. data/public/js/jquery.uuid.js +24 -0
  124. data/public/js/jquery.ws-0.3pre.js +201 -0
  125. data/public/js/onepage.js +5 -0
  126. data/public/js/presenter.js +193 -0
  127. data/public/js/sh_lang/sh_bison.min.js +1 -0
  128. data/public/js/sh_lang/sh_c.min.js +1 -0
  129. data/public/js/sh_lang/sh_caml.min.js +1 -0
  130. data/public/js/sh_lang/sh_changelog.min.js +1 -0
  131. data/public/js/sh_lang/sh_coffeescript.min.js +1 -0
  132. data/public/js/sh_lang/sh_cpp.min.js +1 -0
  133. data/public/js/sh_lang/sh_csharp.min.js +1 -0
  134. data/public/js/sh_lang/sh_css.min.js +1 -0
  135. data/public/js/sh_lang/sh_cucumber.min.js +2 -0
  136. data/public/js/sh_lang/sh_desktop.min.js +1 -0
  137. data/public/js/sh_lang/sh_diff.min.js +1 -0
  138. data/public/js/sh_lang/sh_erlang.min.js +1 -0
  139. data/public/js/sh_lang/sh_flex.min.js +1 -0
  140. data/public/js/sh_lang/sh_glsl.min.js +1 -0
  141. data/public/js/sh_lang/sh_haxe.min.js +1 -0
  142. data/public/js/sh_lang/sh_html.min.js +1 -0
  143. data/public/js/sh_lang/sh_java.min.js +1 -0
  144. data/public/js/sh_lang/sh_javascript.min.js +1 -0
  145. data/public/js/sh_lang/sh_javascript_dom.min.js +1 -0
  146. data/public/js/sh_lang/sh_latex.min.js +1 -0
  147. data/public/js/sh_lang/sh_ldap.min.js +1 -0
  148. data/public/js/sh_lang/sh_log.min.js +1 -0
  149. data/public/js/sh_lang/sh_lsm.min.js +1 -0
  150. data/public/js/sh_lang/sh_m4.min.js +1 -0
  151. data/public/js/sh_lang/sh_makefile.min.js +1 -0
  152. data/public/js/sh_lang/sh_oracle.min.js +1 -0
  153. data/public/js/sh_lang/sh_pascal.min.js +1 -0
  154. data/public/js/sh_lang/sh_perl.min.js +1 -0
  155. data/public/js/sh_lang/sh_php.min.js +1 -0
  156. data/public/js/sh_lang/sh_prolog.min.js +1 -0
  157. data/public/js/sh_lang/sh_properties.min.js +1 -0
  158. data/public/js/sh_lang/sh_python.min.js +1 -0
  159. data/public/js/sh_lang/sh_ruby.min.js +1 -0
  160. data/public/js/sh_lang/sh_scala.min.js +1 -0
  161. data/public/js/sh_lang/sh_sh.min.js +1 -0
  162. data/public/js/sh_lang/sh_slang.min.js +1 -0
  163. data/public/js/sh_lang/sh_sml.min.js +1 -0
  164. data/public/js/sh_lang/sh_spec.min.js +1 -0
  165. data/public/js/sh_lang/sh_sql.min.js +1 -0
  166. data/public/js/sh_lang/sh_tcl.min.js +1 -0
  167. data/public/js/sh_lang/sh_xml.min.js +1 -0
  168. data/public/js/sh_lang/sh_xorg.min.js +1 -0
  169. data/public/js/sh_main.min.js +4 -0
  170. data/public/js/showoff.js +652 -0
  171. data/public/js/showoffcore.js +13 -0
  172. data/public/scripts/script.js +280 -0
  173. data/public/themes/ribbon/fonts/DroidSansMono.svg +626 -0
  174. data/public/themes/ribbon/fonts/DroidSansMono.ttf +0 -0
  175. data/public/themes/ribbon/fonts/PTSans.Bold.Italic.svg +728 -0
  176. data/public/themes/ribbon/fonts/PTSans.Bold.Italic.ttf +0 -0
  177. data/public/themes/ribbon/fonts/PTSans.Bold.Italic.woff +0 -0
  178. data/public/themes/ribbon/fonts/PTSans.Bold.svg +728 -0
  179. data/public/themes/ribbon/fonts/PTSans.Bold.ttf +0 -0
  180. data/public/themes/ribbon/fonts/PTSans.Bold.woff +0 -0
  181. data/public/themes/ribbon/fonts/PTSans.Italic.svg +728 -0
  182. data/public/themes/ribbon/fonts/PTSans.Italic.ttf +0 -0
  183. data/public/themes/ribbon/fonts/PTSans.Italic.woff +0 -0
  184. data/public/themes/ribbon/fonts/PTSans.Narrow.Bold.svg +728 -0
  185. data/public/themes/ribbon/fonts/PTSans.Narrow.Bold.ttf +0 -0
  186. data/public/themes/ribbon/fonts/PTSans.Narrow.Bold.woff +0 -0
  187. data/public/themes/ribbon/fonts/PTSans.Narrow.svg +728 -0
  188. data/public/themes/ribbon/fonts/PTSans.Narrow.ttf +0 -0
  189. data/public/themes/ribbon/fonts/PTSans.Narrow.woff +0 -0
  190. data/public/themes/ribbon/fonts/PTSans.svg +728 -0
  191. data/public/themes/ribbon/fonts/PTSans.ttf +0 -0
  192. data/public/themes/ribbon/fonts/PTSans.woff +0 -0
  193. data/public/themes/ribbon/fonts/TargetBlank.otf +0 -0
  194. data/public/themes/ribbon/fonts/TargetBlank.svg +14 -0
  195. data/public/themes/ribbon/images/grid.png +0 -0
  196. data/public/themes/ribbon/images/linen.png +0 -0
  197. data/public/themes/ribbon/images/ribbon.svg +4 -0
  198. data/public/themes/ribbon/styles/fonts.css +63 -0
  199. data/public/themes/ribbon/styles/print.css +68 -0
  200. data/public/themes/ribbon/styles/reset.css +42 -0
  201. data/public/themes/ribbon/styles/style.css +408 -0
  202. data/views/header.erb +11 -0
  203. data/views/index.erb +13 -0
  204. data/views/onepage.erb +39 -0
  205. data/views/presenter.erb +70 -0
  206. metadata +328 -0
@@ -0,0 +1,27 @@
1
+ require 'rake/testtask'
2
+
3
+ begin
4
+ require 'mg'
5
+ rescue LoadError
6
+ abort "Please `gem install mg`"
7
+ end
8
+
9
+ MG.new("showoff.gemspec")
10
+
11
+ #
12
+ # Tests
13
+ #
14
+
15
+ task :default => :test
16
+
17
+ desc "Run tests"
18
+ task :turn do
19
+ suffix = "-n #{ENV['TEST']}" if ENV['TEST']
20
+ sh "turn test/*_test.rb #{suffix}"
21
+ end
22
+
23
+ Rake::TestTask.new do |t|
24
+ t.libs << 'lib'
25
+ t.pattern = 'test/**/*_test.rb'
26
+ t.verbose = false
27
+ end
@@ -0,0 +1,189 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'showoff'
5
+ require 'showoff/version'
6
+ require 'rubygems'
7
+ require 'gli'
8
+
9
+ include GLI
10
+
11
+ version SHOWOFF_VERSION
12
+
13
+ desc 'Create new showoff presentation'
14
+ arg_name 'dir_name'
15
+ long_desc 'This command helps start a new showoff presentation by setting up the proper directory structure for you. It takes the directory name you would like showoff to create for you.'
16
+ command [:create,:init] do |c|
17
+
18
+ c.desc 'Don''t create sample slides'
19
+ c.switch [:n,:nosamples]
20
+
21
+ c.desc 'sample slide directory name'
22
+ c.default_value 'one'
23
+ c.flag [:d,:slidedir]
24
+
25
+ c.action do |global_options,options,args|
26
+ raise "dir_name is required" if args.empty?
27
+ ShowOffUtils.create(args[0],!options[:n],options[:d])
28
+ if !options[:n]
29
+ puts "done. run 'showoff serve' in #{options[:d]}/ dir to see slideshow"
30
+ else
31
+ puts "done. add slides, modify #{ShowOffUtils.presentation_config_file} and then run 'showoff serve' in #{dirname}/ dir to see slideshow"
32
+ end
33
+ end
34
+ end
35
+
36
+ desc 'Puts your showoff presentation into a gh-pages branch'
37
+ long_desc 'Generates a static version of your presentation into your gh-pages branch for publishing to GitHub Pages'
38
+ command :github do |c|
39
+ c.action do |global_options,options,args|
40
+ puts "Generating static content"
41
+ ShowOffUtils.github
42
+ puts "I've updated your 'gh-pages' branch with the static version of your presentation."
43
+ puts "Push it to GitHub to publish it. Probably something like:"
44
+ puts
45
+ puts " git push origin gh-pages"
46
+ puts
47
+ end
48
+ end
49
+
50
+ desc 'Serves the showoff presentation in the current directory'
51
+ desc 'Setup your presentation to serve on Heroku'
52
+ arg_name 'heroku_name'
53
+ long_desc 'Creates the Gemfile and config.ru file needed to push a showoff pres to heroku. It will then run ''heroku create'' for you to register the new project on heroku and add the remote for you. Then all you need to do is commit the new created files and run ''git push heroku'' to deploy.'
54
+ command :heroku do |c|
55
+
56
+ c.desc 'add password protection to your heroku site'
57
+ c.flag [:p,:password]
58
+
59
+ c.desc 'force overwrite of existing Gemfile/.gems and config.ru files if they exist'
60
+ c.switch [:f,:force]
61
+
62
+ c.desc 'Use older-style .gems file instead of bundler-style Gemfile'
63
+ c.switch [:g,:dotgems]
64
+
65
+ c.action do |global_options,options,args|
66
+ raise "heroku_name is required" if args.empty?
67
+ if ShowOffUtils.heroku(args[0],options[:f],options[:p],options[:g])
68
+ puts "herokuized. run something like this to launch your heroku presentation:
69
+
70
+ heroku create #{args[0]}"
71
+
72
+ if options[:g]
73
+ puts " git add .gems config.ru"
74
+ else
75
+ puts " bundle install"
76
+ puts " git add Gemfile Gemfile.lock config.ru"
77
+ end
78
+ puts " git commit -m 'herokuized'
79
+ git push heroku master
80
+ "
81
+
82
+ if options[:p]
83
+ puts "CAREFUL: you are commiting your access password - anyone with read access to the repo can access the preso\n\n"
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ desc 'Serves the showoff presentation in the specified (or current) directory'
90
+ arg_name "[pres_dir]"
91
+ default_value "."
92
+ command :serve do |c|
93
+
94
+ c.desc 'Show verbose messaging'
95
+ c.switch :verbose
96
+
97
+ c.desc 'Port on which to run'
98
+ c.default_value "9090"
99
+ c.flag [:p,:port]
100
+
101
+ c.desc 'Host or ip to run on'
102
+ c.default_value "localhost"
103
+ c.flag [:h,:host]
104
+
105
+ c.desc 'JSON file used to describe presentation'
106
+ c.default_value "showoff.json"
107
+ c.flag [:f, :pres_file]
108
+
109
+ c.action do |global_options,options,args|
110
+
111
+ url = "http://#{options[:h]}:#{options[:p].to_i}"
112
+ puts "
113
+ -------------------------
114
+
115
+ Your ShowOff presentation is now starting up.
116
+
117
+ To view it plainly, visit [ #{url} ]
118
+
119
+ To run it from presenter view, go to: [ #{url}/presenter ]
120
+
121
+ -------------------------
122
+
123
+ "
124
+ ShowOff.run! :host => options[:h], :port => options[:p].to_i, :pres_file => options[:f], :pres_dir => args[0], :verbose => options[:verbose]
125
+ end
126
+ end
127
+
128
+ desc 'Add a new slide at the end in a given dir'
129
+ arg_name '[title]'
130
+ long_desc 'Outputs or creates a new slide. With -d and -n, a new slide is created in the given dir, numbered to appear as the last slide in that dir (use -u to avoid numbering). Without those, outputs the slide markdown to stdout (useful for shelling out from your editor). You may also specify a source file to use for a code slide'
131
+ command [:add,:new] do |c|
132
+ c.desc 'Don''t number the slide, use the given name verbatim'
133
+ c.switch [:u,:nonumber]
134
+
135
+ c.desc 'Include code from the given file as the slide body'
136
+ c.arg_name 'path to file'
137
+ c.flag [:s,:source]
138
+
139
+ c.desc 'Slide Type/Style'
140
+ c.arg_name 'valid showoff style/type'
141
+ c.default_value 'title'
142
+ c.flag [:t,:type,:style]
143
+
144
+ c.desc 'Slide dir (where to put a new slide file)'
145
+ c.arg_name 'dir'
146
+ c.flag [:d,:dir]
147
+
148
+ c.desc 'Slide name (name of the new slide file)'
149
+ c.arg_name 'basename'
150
+ c.flag [:n,:name]
151
+
152
+ c.action do |global_options,options,args|
153
+ title = args.join(" ")
154
+ ShowOffUtils.add_slide(:dir => options[:d],
155
+ :name => options[:n],
156
+ :title => title,
157
+ :number => !options[:u],
158
+ :code => options[:s],
159
+ :type => options[:t])
160
+ end
161
+ end
162
+
163
+ desc 'Generate static version of presentation'
164
+ arg_name 'name'
165
+ long_desc 'Creates a static, one page version of the presentation as {name}.html'
166
+ command [:static] do |c|
167
+ c.action do |global_options,options,args|
168
+ ShowOff.do_static(args[0])
169
+ end
170
+ end
171
+
172
+ pre do |global,command,options,args|
173
+ # Pre logic here
174
+ # Return true to proceed; false to abourt and not call the
175
+ # chosen command
176
+ true
177
+ end
178
+
179
+ post do |global,command,options,args|
180
+ # Post logic here
181
+ end
182
+
183
+ on_error do |exception|
184
+ # Error logic here
185
+ # return false to skip default error handling
186
+ true
187
+ end
188
+
189
+ exit GLI.run(ARGV)
@@ -0,0 +1,67 @@
1
+ require 'parslet'
2
+
3
+ # For parsing commandline slide content.
4
+ class CommandlineParser < Parslet::Parser
5
+
6
+ rule(:prompt) do
7
+ str('$') | str('#') | str('>>')
8
+ end
9
+
10
+ rule(:text) do
11
+ match['[:print:]'].repeat
12
+ end
13
+
14
+ rule(:singleline_input) do
15
+ (str("\\\n").absent? >> match['[:print:]']).repeat
16
+ end
17
+
18
+ rule(:input) do
19
+ multiline_input | singleline_input
20
+ end
21
+
22
+ rule(:multiline_input) do
23
+
24
+ # some command \
25
+ # continued \
26
+ # \
27
+ # and stop
28
+ ( singleline_input >> str('\\') >> newline ).repeat(1) >> singleline_input
29
+ end
30
+
31
+ rule(:command) do
32
+
33
+ # $ some command
34
+ # some output
35
+ ( prompt.as(:prompt) >> space? >> input.as(:input) >> output? ).as(:command)
36
+ end
37
+
38
+ rule(:output) do
39
+
40
+ # output
41
+ prompt.absent? >> text
42
+ end
43
+
44
+ rule(:output?) do
45
+
46
+ #
47
+ # some text
48
+ # some text
49
+ #
50
+ # some text
51
+ ( newline >> ( ( output >> newline ).repeat >> output.maybe ).as(:output) ).maybe
52
+ end
53
+
54
+ rule(:commands) do
55
+ command.repeat
56
+ end
57
+
58
+ rule(:newline) do
59
+ str("\n") | str("\r\n")
60
+ end
61
+
62
+ rule(:space?) do
63
+ match['[:space:]'].repeat
64
+ end
65
+
66
+ root(:commands)
67
+ end
@@ -0,0 +1,515 @@
1
+ require 'rubygems'
2
+ require 'sinatra/base'
3
+ require 'json'
4
+ require 'nokogiri'
5
+ require 'fileutils'
6
+ require 'logger'
7
+
8
+ here = File.expand_path(File.dirname(__FILE__))
9
+ require "#{here}/showoff_utils"
10
+ require "#{here}/commandline_parser"
11
+
12
+ begin
13
+ require 'RMagick'
14
+ rescue LoadError
15
+ $stderr.puts 'image sizing disabled - install rmagick'
16
+ end
17
+
18
+ begin
19
+ require 'pdfkit'
20
+ rescue LoadError
21
+ $stderr.puts 'pdf generation disabled - install pdfkit'
22
+ end
23
+
24
+ require 'tilt'
25
+
26
+ class ShowOff < Sinatra::Application
27
+
28
+ attr_reader :cached_image_size
29
+
30
+ set :views, File.dirname(__FILE__) + '/../views'
31
+ set :public_folder, File.dirname(__FILE__) + '/../public'
32
+
33
+ set :verbose, false
34
+ set :pres_dir, '.'
35
+ set :pres_file, 'showoff.json'
36
+
37
+ def initialize(app=nil)
38
+ super(app)
39
+ @logger = Logger.new(STDOUT)
40
+ @logger.formatter = proc { |severity,datetime,progname,msg| "#{progname} #{msg}\n" }
41
+ @logger.level = settings.verbose ? Logger::DEBUG : Logger::WARN
42
+
43
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
44
+ @logger.debug(dir)
45
+
46
+ showoff_dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
47
+ settings.pres_dir ||= Dir.pwd
48
+ @root_path = "."
49
+
50
+ settings.pres_dir = File.expand_path(settings.pres_dir)
51
+ if (settings.pres_file)
52
+ ShowOffUtils.presentation_config_file = settings.pres_file
53
+ end
54
+ @cached_image_size = {}
55
+ @logger.debug settings.pres_dir
56
+ @pres_name = settings.pres_dir.split('/').pop
57
+ require_ruby_files
58
+
59
+ # Default asset path
60
+ @asset_path = "./"
61
+ end
62
+
63
+ def self.pres_dir_current
64
+ opt = {:pres_dir => Dir.pwd}
65
+ ShowOff.set opt
66
+ end
67
+
68
+ def require_ruby_files
69
+ Dir.glob("#{settings.pres_dir}/*.rb").map { |path| require path }
70
+ end
71
+
72
+ helpers do
73
+ def load_section_files(section)
74
+ section = File.join(settings.pres_dir, section)
75
+ files = if File.directory? section
76
+ Dir.glob("#{section}/**/*").sort
77
+ else
78
+ [section]
79
+ end
80
+ @logger.debug files
81
+ files
82
+ end
83
+
84
+ def css_files
85
+ Dir.glob("#{settings.pres_dir}/*.css").map { |path| File.basename(path) }
86
+ end
87
+
88
+ def js_files
89
+ Dir.glob("#{settings.pres_dir}/*.js").map { |path| File.basename(path) }
90
+ end
91
+
92
+
93
+ def preshow_files
94
+ Dir.glob("#{settings.pres_dir}/_preshow/*").map { |path| File.basename(path) }.to_json
95
+ end
96
+
97
+ # todo: move more behavior into this class
98
+ class Slide
99
+ attr_reader :classes, :text
100
+ def initialize classes = ""
101
+ @classes = ["content"] + classes.strip.chomp('>').split
102
+ @text = ""
103
+ end
104
+ def <<(s)
105
+ @text << s
106
+ @text << "\n"
107
+ end
108
+ def empty?
109
+ @text.strip == ""
110
+ end
111
+ end
112
+
113
+
114
+ def process_markdown(name, content, static=false, pdf=false)
115
+
116
+ # if there are no !SLIDE markers, then make every H1 define a new slide
117
+ unless content =~ /^\<?!SLIDE/m
118
+ content = content.gsub(/^# /m, "<!SLIDE>\n# ")
119
+ end
120
+
121
+ # todo: unit test
122
+ lines = content.split("\n")
123
+ @logger.debug "#{name}: #{lines.length} lines"
124
+ slides = []
125
+ slides << (slide = Slide.new)
126
+ until lines.empty?
127
+ line = lines.shift
128
+ if line =~ /^<?!SLIDE(.*)>?/
129
+ slides << (slide = Slide.new($1))
130
+ else
131
+ slide << line
132
+ end
133
+ end
134
+
135
+ slides.delete_if {|slide| slide.empty? }
136
+
137
+ final = ''
138
+ if slides.size > 1
139
+ seq = 1
140
+ end
141
+
142
+ idno = 1
143
+ slides.each do |slide|
144
+ md = ''
145
+ content_classes = slide.classes
146
+ # extract transition, defaulting to none
147
+ transition = 'none'
148
+ content_classes.delete_if { |x| x =~ /^transition=(.+)/ && transition = $1 }
149
+ # extract id, defaulting to none
150
+ id = "slide" + idno.to_s
151
+ content_classes.delete_if { |x| x =~ /^#([\w-]+)/ && id = $1 }
152
+ @logger.debug "id: #{id}" if id
153
+ @logger.debug "classes: #{content_classes.inspect}"
154
+ @logger.debug "transition: #{transition}"
155
+ # create html
156
+ md += "<div"
157
+ md += " id=\"#{id}\"" if id
158
+ md += " class=\"slide #{content_classes.join(' ')}\" data-transition=\"#{transition}\">\n"
159
+ md += "<div>\n"
160
+ md += "<section>\n"
161
+ #if seq
162
+ # md += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}/#{seq.to_s}\">\n"
163
+ # seq += 1
164
+ #else
165
+ # md += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}\">\n"
166
+ #end
167
+ sl = Tilt[:markdown].new { slide.text }.render
168
+ sl = update_image_paths(name, sl, static, pdf)
169
+ sl = sl.sub(/<h1>(.*?)<\/h1>/m) { |s|
170
+ src = $1
171
+ src = src.gsub(/\\n/m, '<br/>')
172
+ src = '<header><h2>' + src + '</h2></header>'
173
+ }
174
+ #sl = sl.gsub(/<code>(.*?)<\/code>/m) { |s|
175
+ # src = $1
176
+ # src = src.gsub(/\n/m, '</code><code>')
177
+ # src = '<code>' + src + '</code>'
178
+ # src = src.gsub('<code></code>', '')
179
+ #}
180
+ if (content_classes.find {|i| i == 'center'} ) then
181
+ sl = sl.sub(/<img (.*?)>/m) { |s|
182
+ src = $1
183
+ src = '<img ' + src + ' class="middle"/>'
184
+ }
185
+ end
186
+ md += sl
187
+ md += "\n</section>\n"
188
+ md += "</div>\n"
189
+ md += "</div>\n"
190
+ final += update_commandline_code(md)
191
+ final = update_p_classes(final)
192
+ idno += 1
193
+ end
194
+ final
195
+ end
196
+
197
+ # find any lines that start with a <p>.(something) and turn them into <p class="something">
198
+ def update_p_classes(markdown)
199
+ markdown.gsub(/<p>\.(.*?) /, '<p class="\1">')
200
+ end
201
+
202
+ def update_image_paths(path, slide, static=false, pdf=false)
203
+ paths = path.split('/')
204
+ paths.pop
205
+ path = paths.join('/')
206
+ replacement_prefix = static ?
207
+ ( pdf ? %(img src="file://#{settings.pres_dir}/#{path}) : %(img src="./file/#{path}) ) :
208
+ %(img src="#{@asset_path}image/#{path})
209
+ slide.gsub(/img src=\"([^\/].*?)\"/) do |s|
210
+ img_path = File.join(path, $1)
211
+ w, h = get_image_size(img_path)
212
+ src = %(#{replacement_prefix}/#{$1}")
213
+ if w && h
214
+ src << %( width="#{w}" height="#{h}")
215
+ end
216
+ src
217
+ end
218
+ end
219
+
220
+ if defined?(Magick)
221
+ def get_image_size(path)
222
+ if !cached_image_size.key?(path)
223
+ #img = Magick::Image.ping(File.join(@asset_path, path)).first
224
+ img = Magick::Image.ping(path).first
225
+ # don't set a size for svgs so they can expand to fit their container
226
+ if img.mime_type == 'image/svg+xml'
227
+ cached_image_size[path] = [nil, nil]
228
+ else
229
+ cached_image_size[path] = [img.columns, img.rows]
230
+ end
231
+ end
232
+ cached_image_size[path]
233
+ end
234
+ else
235
+ def get_image_size(path)
236
+ end
237
+ end
238
+
239
+ def update_commandline_code(slide)
240
+ html = Nokogiri::XML.parse(slide)
241
+ parser = CommandlineParser.new
242
+
243
+ html.css('pre').each do |pre|
244
+ pre.css('code').each do |code|
245
+ out = code.text
246
+ lines = out.split("\n")
247
+ if lines.first.strip[0, 3] == '@@@'
248
+ lang = lines.shift.gsub('@@@', '').strip
249
+ pre.set_attribute('class', 'sh_' + lang.downcase)
250
+ code.content = lines.join("\n")
251
+ end
252
+ end
253
+ end
254
+
255
+ html.css('.commandline > pre > code').each do |code|
256
+ out = code.text
257
+ code.content = ''
258
+ tree = parser.parse(out)
259
+ transform = Parslet::Transform.new do
260
+ rule(:prompt => simple(:prompt), :input => simple(:input), :output => simple(:output)) do
261
+ command = Nokogiri::XML::Node.new('code', html)
262
+ command.set_attribute('class', 'command')
263
+ command.content = "#{prompt} #{input}"
264
+ code << command
265
+
266
+ # Add newline after the input so that users can
267
+ # advance faster than the typewriter effect
268
+ # and still keep inputs on separate lines.
269
+ code << "\n"
270
+
271
+ unless output.to_s.empty?
272
+
273
+ result = Nokogiri::XML::Node.new('code', html)
274
+ result.set_attribute('class', 'result')
275
+ result.content = output
276
+ code << result
277
+ end
278
+ end
279
+ end
280
+ transform.apply(tree)
281
+ end
282
+ html.root.to_s
283
+ end
284
+
285
+ def get_slides_html(static=false, pdf=false)
286
+ sections = ShowOffUtils.showoff_sections(settings.pres_dir, @logger)
287
+ files = []
288
+ if sections
289
+ data = ''
290
+ sections.each do |section|
291
+ if section =~ /^#/
292
+ name = section.each_line.first.gsub(/^#*/,'').strip
293
+ data << process_markdown(name, "<!SLIDE subsection>\n" + section, static, pdf)
294
+ else
295
+ files = []
296
+ files << load_section_files(section)
297
+ files = files.flatten
298
+ files = files.select { |f| f =~ /.md$/ }
299
+ files.each do |f|
300
+ fname = f.gsub(settings.pres_dir + '/', '').gsub('.md', '')
301
+ data << process_markdown(fname, File.read(f), static, pdf)
302
+ end
303
+ end
304
+ end
305
+ end
306
+ data
307
+ end
308
+
309
+ def inline_css(csses, pre = nil)
310
+ css_content = '<style type="text/css">'
311
+ csses.each do |css_file|
312
+ if pre
313
+ css_file = File.join(File.dirname(__FILE__), '..', pre, css_file)
314
+ else
315
+ css_file = File.join(settings.pres_dir, css_file)
316
+ end
317
+ css_content += File.read(css_file)
318
+ end
319
+ css_content += '</style>'
320
+ css_content
321
+ end
322
+
323
+ def inline_js(jses, pre = nil)
324
+ js_content = '<script type="text/javascript">'
325
+ jses.each do |js_file|
326
+ if pre
327
+ js_file = File.join(File.dirname(__FILE__), '..', pre, js_file)
328
+ else
329
+ js_file = File.join(settings.pres_dir, js_file)
330
+ end
331
+ js_content += File.read(js_file)
332
+ end
333
+ js_content += '</script>'
334
+ js_content
335
+ end
336
+
337
+ def inline_all_js(jses_directory)
338
+ inline_js(Dir.entries(File.join(File.dirname(__FILE__), '..', jses_directory)).find_all{|filename| filename.length > 2 }, jses_directory)
339
+ end
340
+
341
+ def index(static=false)
342
+ #if static
343
+ @title = ShowOffUtils.showoff_title
344
+ @slides = get_slides_html(static)
345
+ @asset_path = "./"
346
+ #end
347
+ erb :index
348
+ end
349
+
350
+ def presenter
351
+ erb :presenter
352
+ end
353
+
354
+ def clean_link(href)
355
+ if href && href[0, 1] == '/'
356
+ href = href[1, href.size]
357
+ end
358
+ href
359
+ end
360
+
361
+ def assets_needed
362
+ assets = ["index", "slides"]
363
+
364
+ index = erb :index
365
+ html = Nokogiri::XML.parse(index)
366
+ html.css('head link').each do |link|
367
+ href = clean_link(link['href'])
368
+ assets << href if href
369
+ end
370
+ html.css('head script').each do |link|
371
+ href = clean_link(link['src'])
372
+ assets << href if href
373
+ end
374
+
375
+ slides = get_slides_html
376
+ html = Nokogiri::XML.parse("<slides>" + slides + "</slides>")
377
+ html.css('img').each do |link|
378
+ href = clean_link(link['src'])
379
+ assets << href if href
380
+ end
381
+
382
+ css = Dir.glob("#{settings.public_folder}/**/*.css").map { |path| path.gsub(settings.public_folder + '/', '') }
383
+ assets << css
384
+
385
+ js = Dir.glob("#{settings.public_folder}/**/*.js").map { |path| path.gsub(settings.public_folder + '/', '') }
386
+ assets << js
387
+
388
+ assets.uniq.join("\n")
389
+ end
390
+
391
+ def slides(static=false)
392
+ get_slides_html(static)
393
+ end
394
+
395
+ def onepage(static=false)
396
+ @slides = get_slides_html(static)
397
+ erb :onepage
398
+ end
399
+
400
+ def pdf(static=true)
401
+ @slides = get_slides_html(static, true)
402
+ @no_js = false
403
+ html = erb :onepage
404
+ # TODO make a random filename
405
+
406
+ # PDFKit.new takes the HTML and any options for wkhtmltopdf
407
+ # run `wkhtmltopdf --extended-help` for a full list of options
408
+ kit = PDFKit.new(html, ShowOffUtils.showoff_pdf_options(settings.pres_dir))
409
+
410
+ # Save the PDF to a file
411
+ file = kit.to_file('/tmp/preso.pdf')
412
+ end
413
+
414
+ end
415
+
416
+
417
+ def self.do_static(what)
418
+ what = "index" if !what
419
+
420
+ # Nasty hack to get the actual ShowOff module
421
+ showoff = ShowOff.new
422
+ while !showoff.is_a?(ShowOff)
423
+ showoff = showoff.instance_variable_get(:@app)
424
+ end
425
+ name = showoff.instance_variable_get(:@pres_name)
426
+ path = showoff.instance_variable_get(:@root_path)
427
+ logger = showoff.instance_variable_get(:@logger)
428
+ data = showoff.send(what, true)
429
+ if data.is_a?(File)
430
+ FileUtils.cp(data.path, "#{name}.pdf")
431
+ else
432
+ out = File.expand_path("#{path}/static")
433
+ # First make a directory
434
+ FileUtils.makedirs(out)
435
+ # Then write the html
436
+ file = File.new("#{out}/index.html", "w")
437
+ file.puts(data)
438
+ file.close
439
+ # Now copy all the js and css
440
+ my_path = File.join( File.dirname(__FILE__), '..', 'public')
441
+ ["js", "css"].each { |dir|
442
+ FileUtils.copy_entry("#{my_path}/#{dir}", "#{out}/#{dir}")
443
+ }
444
+ # And copy the directory
445
+ Dir.glob("#{my_path}/#{name}/*").each { |subpath|
446
+ base = File.basename(subpath)
447
+ next if "static" == base
448
+ next unless File.directory?(subpath) || base.match(/\.(css|js)$/)
449
+ FileUtils.copy_entry(subpath, "#{out}/#{base}")
450
+ }
451
+
452
+ # Set up file dir
453
+ file_dir = File.join(out, 'file')
454
+ FileUtils.makedirs(file_dir)
455
+ pres_dir = showoff.settings.pres_dir
456
+
457
+ # ..., copy all user-defined styles and javascript files
458
+ Dir.glob("#{pres_dir}/*.{css,js}").each { |path|
459
+ FileUtils.copy(path, File.join(file_dir, File.basename(path)))
460
+ }
461
+
462
+ # ... and copy all needed image files
463
+ data.scan(/img src=\".\/file\/(.*?)\"/).flatten.each do |path|
464
+ dir = File.dirname(path)
465
+ FileUtils.makedirs(File.join(file_dir, dir))
466
+ FileUtils.copy(File.join(pres_dir, path), File.join(file_dir, path))
467
+ end
468
+ # copy images from css too
469
+ Dir.glob("#{pres_dir}/*.css").each do |css_path|
470
+ File.open(css_path) do |file|
471
+ data = file.read
472
+ data.scan(/url\((.*)\)/).flatten.each do |path|
473
+ logger.debug path
474
+ dir = File.dirname(path)
475
+ FileUtils.makedirs(File.join(file_dir, dir))
476
+ FileUtils.copy(File.join(pres_dir, path), File.join(file_dir, path))
477
+ end
478
+ end
479
+ end
480
+ end
481
+ end
482
+
483
+ def eval_ruby code
484
+ eval(code).to_s
485
+ rescue => e
486
+ e.message
487
+ end
488
+
489
+ get '/eval_ruby' do
490
+ return eval_ruby(params[:code]) if ENV['SHOWOFF_EVAL_RUBY']
491
+
492
+ return "Ruby Evaluation is off. To turn it on set ENV['SHOWOFF_EVAL_RUBY']"
493
+ end
494
+
495
+ get %r{(?:image|file)/(.*)} do
496
+ path = params[:captures].first
497
+ full_path = File.join(settings.pres_dir, path)
498
+ send_file full_path
499
+ end
500
+
501
+ get %r{/(.*)} do
502
+ @title = ShowOffUtils.showoff_title
503
+ what = params[:captures].first
504
+ what = 'index' if "" == what
505
+ @asset_path = (env['SCRIPT_NAME'] || '').gsub(/\/?$/, '/').gsub(/^\//, '')
506
+ if (what != "favicon.ico")
507
+ data = send(what)
508
+ if data.is_a?(File)
509
+ send_file data.path
510
+ else
511
+ data
512
+ end
513
+ end
514
+ end
515
+ end