mattmatt-showoff 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +280 -0
  3. data/Rakefile +15 -0
  4. data/bin/showoff +115 -0
  5. data/lib/princely.rb +84 -0
  6. data/lib/showoff.rb +346 -0
  7. data/lib/showoff_utils.rb +259 -0
  8. data/public/css/fg.menu.css +114 -0
  9. data/public/css/onepage.css +44 -0
  10. data/public/css/pdf.css +12 -0
  11. data/public/css/reset.css +53 -0
  12. data/public/css/sh_style.css +66 -0
  13. data/public/css/showoff.css +258 -0
  14. data/public/css/spinner_bar.gif +0 -0
  15. data/public/css/theme/images/ui-bg_diagonals-small_100_f0efea_40x40.png +0 -0
  16. data/public/css/theme/images/ui-bg_flat_35_f0f0f0_40x100.png +0 -0
  17. data/public/css/theme/images/ui-bg_glass_55_fcf0ba_1x400.png +0 -0
  18. data/public/css/theme/images/ui-bg_glow-ball_25_2e2e28_600x600.png +0 -0
  19. data/public/css/theme/images/ui-bg_highlight-soft_100_f0efea_1x100.png +0 -0
  20. data/public/css/theme/images/ui-bg_highlight-soft_25_327E04_1x100.png +0 -0
  21. data/public/css/theme/images/ui-bg_highlight-soft_25_5A9D1A_1x100.png +0 -0
  22. data/public/css/theme/images/ui-bg_highlight-soft_95_ffedad_1x100.png +0 -0
  23. data/public/css/theme/images/ui-bg_inset-soft_22_3b3b35_1x100.png +0 -0
  24. data/public/css/theme/images/ui-icons_808080_256x240.png +0 -0
  25. data/public/css/theme/images/ui-icons_8DC262_256x240.png +0 -0
  26. data/public/css/theme/images/ui-icons_cd0a0a_256x240.png +0 -0
  27. data/public/css/theme/images/ui-icons_e7e6e4_256x240.png +0 -0
  28. data/public/css/theme/images/ui-icons_eeeeee_256x240.png +0 -0
  29. data/public/css/theme/images/ui-icons_ffffff_256x240.png +0 -0
  30. data/public/css/theme/ui.accordion.css +9 -0
  31. data/public/css/theme/ui.all.css +2 -0
  32. data/public/css/theme/ui.base.css +9 -0
  33. data/public/css/theme/ui.core.css +37 -0
  34. data/public/css/theme/ui.datepicker.css +62 -0
  35. data/public/css/theme/ui.dialog.css +13 -0
  36. data/public/css/theme/ui.progressbar.css +4 -0
  37. data/public/css/theme/ui.resizable.css +13 -0
  38. data/public/css/theme/ui.slider.css +17 -0
  39. data/public/css/theme/ui.tabs.css +9 -0
  40. data/public/css/theme/ui.theme.css +245 -0
  41. data/public/favicon.ico +0 -0
  42. data/public/js/fg.menu.js +645 -0
  43. data/public/js/jTypeWriter.js +26 -0
  44. data/public/js/jquery-1.4.min.js +151 -0
  45. data/public/js/jquery-print.js +109 -0
  46. data/public/js/jquery.batchImageLoad.js +56 -0
  47. data/public/js/jquery.cookie.js +96 -0
  48. data/public/js/jquery.cycle.all.js +1284 -0
  49. data/public/js/jquery.doubletap-0.1.js +105 -0
  50. data/public/js/jquery.uuid.js +24 -0
  51. data/public/js/jquery.ws-0.3pre.js +201 -0
  52. data/public/js/onepage.js +5 -0
  53. data/public/js/sh_lang/sh_bison.min.js +1 -0
  54. data/public/js/sh_lang/sh_c.min.js +1 -0
  55. data/public/js/sh_lang/sh_caml.min.js +1 -0
  56. data/public/js/sh_lang/sh_changelog.min.js +1 -0
  57. data/public/js/sh_lang/sh_cpp.min.js +1 -0
  58. data/public/js/sh_lang/sh_csharp.min.js +1 -0
  59. data/public/js/sh_lang/sh_css.min.js +1 -0
  60. data/public/js/sh_lang/sh_desktop.min.js +1 -0
  61. data/public/js/sh_lang/sh_diff.min.js +1 -0
  62. data/public/js/sh_lang/sh_flex.min.js +1 -0
  63. data/public/js/sh_lang/sh_glsl.min.js +1 -0
  64. data/public/js/sh_lang/sh_haxe.min.js +1 -0
  65. data/public/js/sh_lang/sh_html.min.js +1 -0
  66. data/public/js/sh_lang/sh_java.min.js +1 -0
  67. data/public/js/sh_lang/sh_javascript.min.js +1 -0
  68. data/public/js/sh_lang/sh_javascript_dom.min.js +1 -0
  69. data/public/js/sh_lang/sh_latex.min.js +1 -0
  70. data/public/js/sh_lang/sh_ldap.min.js +1 -0
  71. data/public/js/sh_lang/sh_log.min.js +1 -0
  72. data/public/js/sh_lang/sh_lsm.min.js +1 -0
  73. data/public/js/sh_lang/sh_m4.min.js +1 -0
  74. data/public/js/sh_lang/sh_makefile.min.js +1 -0
  75. data/public/js/sh_lang/sh_oracle.min.js +1 -0
  76. data/public/js/sh_lang/sh_pascal.min.js +1 -0
  77. data/public/js/sh_lang/sh_perl.min.js +1 -0
  78. data/public/js/sh_lang/sh_php.min.js +1 -0
  79. data/public/js/sh_lang/sh_prolog.min.js +1 -0
  80. data/public/js/sh_lang/sh_properties.min.js +1 -0
  81. data/public/js/sh_lang/sh_python.min.js +1 -0
  82. data/public/js/sh_lang/sh_ruby.min.js +1 -0
  83. data/public/js/sh_lang/sh_scala.min.js +1 -0
  84. data/public/js/sh_lang/sh_sh.min.js +1 -0
  85. data/public/js/sh_lang/sh_slang.min.js +1 -0
  86. data/public/js/sh_lang/sh_sml.min.js +1 -0
  87. data/public/js/sh_lang/sh_spec.min.js +1 -0
  88. data/public/js/sh_lang/sh_sql.min.js +1 -0
  89. data/public/js/sh_lang/sh_tcl.min.js +1 -0
  90. data/public/js/sh_lang/sh_xml.min.js +1 -0
  91. data/public/js/sh_lang/sh_xorg.min.js +1 -0
  92. data/public/js/sh_main.min.js +4 -0
  93. data/public/js/showoff.js +441 -0
  94. data/views/index.erb +73 -0
  95. data/views/onepage.erb +36 -0
  96. metadata +198 -0
@@ -0,0 +1,346 @@
1
+ require 'rubygems'
2
+ require 'sinatra/base'
3
+ require 'json'
4
+ require 'nokogiri'
5
+ require 'showoff_utils'
6
+ require 'princely'
7
+ require 'ftools'
8
+
9
+ begin
10
+ require 'RMagick'
11
+ rescue LoadError
12
+ puts 'image sizing disabled - install RMagick'
13
+ end
14
+
15
+ begin
16
+ require 'princely'
17
+ rescue LoadError
18
+ puts 'pdf generation disabled - install princely'
19
+ end
20
+
21
+ begin
22
+ require 'rdiscount'
23
+ rescue LoadError
24
+ require 'bluecloth'
25
+ Markdown = BlueCloth
26
+ end
27
+ require 'pp'
28
+
29
+ class ShowOff < Sinatra::Application
30
+
31
+ attr_reader :cached_image_size
32
+
33
+ set :views, File.dirname(__FILE__) + '/../views'
34
+ set :public, File.dirname(__FILE__) + '/../public'
35
+ set :pres_dir, 'example'
36
+
37
+ def initialize(app=nil)
38
+ super(app)
39
+ puts dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
40
+ if Dir.pwd == dir
41
+ options.pres_dir = dir + '/example'
42
+ @root_path = "."
43
+ else
44
+ options.pres_dir = Dir.pwd
45
+ @root_path = ".."
46
+ end
47
+ @cached_image_size = {}
48
+ puts options.pres_dir
49
+ @pres_name = options.pres_dir.split('/').pop
50
+ end
51
+
52
+ helpers do
53
+ def load_section_files(section)
54
+ section = File.join(options.pres_dir, section)
55
+ files = Dir.glob("#{section}/**/*").sort
56
+ pp files
57
+ files
58
+ end
59
+
60
+ def css_files
61
+ Dir.glob("#{options.pres_dir}/*.css").map { |path| File.basename(path) }
62
+ end
63
+
64
+ def js_files
65
+ Dir.glob("#{options.pres_dir}/*.js").map { |path| File.basename(path) }
66
+ end
67
+
68
+ def process_markdown(name, content, static=false)
69
+ slides = content.split(/^!SLIDE/)
70
+ slides.delete('')
71
+ final = ''
72
+ if slides.size > 1
73
+ seq = 1
74
+ end
75
+ slides.each do |slide|
76
+ md = ''
77
+ # extract content classes
78
+ lines = slide.split("\n")
79
+ content_classes = lines.shift.split rescue []
80
+ slide = lines.join("\n")
81
+ # add content class too
82
+ content_classes.unshift "content"
83
+ # extract transition, defaulting to none
84
+ transition = 'none'
85
+ content_classes.delete_if { |x| x =~ /^transition=(.+)/ && transition = $1 }
86
+ puts "classes: #{content_classes.inspect}"
87
+ puts "transition: #{transition}"
88
+ # create html
89
+ md += "<div class=\"slide\" data-transition=\"#{transition}\">"
90
+ if seq
91
+ md += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}/#{seq.to_s}\">\n"
92
+ seq += 1
93
+ else
94
+ md += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}\">\n"
95
+ end
96
+ sl = Markdown.new(slide).to_html
97
+ sl = update_image_paths(name, sl, static)
98
+ md += sl
99
+ md += "</div>\n"
100
+ md += "</div>\n"
101
+ final += update_commandline_code(md)
102
+ final = update_p_classes(final)
103
+ end
104
+ final
105
+ end
106
+
107
+ # find any lines that start with a <p>.(something) and turn them into <p class="something">
108
+ def update_p_classes(markdown)
109
+ markdown.gsub(/<p>\.(.*?) /, '<p class="\1">')
110
+ end
111
+
112
+ def update_image_paths(path, slide, static=false)
113
+ paths = path.split('/')
114
+ paths.pop
115
+ path = paths.join('/')
116
+ replacement_prefix = static ?
117
+ %(img src="file://#{options.pres_dir}/static/#{path}) :
118
+ %(img src="/image/#{path})
119
+ slide.gsub(/img src=\"(.*?)\"/) do |s|
120
+ img_path = File.join(path, $1)
121
+ w, h = get_image_size(img_path)
122
+ src = %(#{replacement_prefix}/#{$1}")
123
+ if w && h
124
+ src << %( width="#{w}" height="#{h}")
125
+ end
126
+ src
127
+ end
128
+ end
129
+
130
+ if defined?(Magick)
131
+ def get_image_size(path)
132
+ if !cached_image_size.key?(path)
133
+ img = Magick::Image.ping(path).first
134
+ cached_image_size[path] = [img.columns, img.rows]
135
+ end
136
+ cached_image_size[path]
137
+ end
138
+ else
139
+ def get_image_size(path)
140
+ end
141
+ end
142
+
143
+ def update_commandline_code(slide)
144
+ html = Nokogiri::XML.parse(slide)
145
+
146
+ html.css('pre').each do |pre|
147
+ pre.css('code').each do |code|
148
+ out = code.text
149
+ lines = out.split("\n")
150
+ if lines.first[0, 3] == '@@@'
151
+ lang = lines.shift.gsub('@@@', '').strip
152
+ pre.set_attribute('class', 'sh_' + lang)
153
+ code.content = lines.join("\n")
154
+ end
155
+ end
156
+ end
157
+
158
+ html.css('.commandline > pre > code').each do |code|
159
+ out = code.text
160
+ lines = out.split(/^\$(.*?)$/)
161
+ lines.delete('')
162
+ code.content = ''
163
+ while(lines.size > 0) do
164
+ command = lines.shift
165
+ result = lines.shift
166
+ c = Nokogiri::XML::Node.new('code', html)
167
+ c.set_attribute('class', 'command')
168
+ c.content = '$' + command
169
+ code << c
170
+ c = Nokogiri::XML::Node.new('code', html)
171
+ c.set_attribute('class', 'result')
172
+ c.content = result
173
+ code << c
174
+ end
175
+ end
176
+ html.root.to_s
177
+ end
178
+
179
+ def get_slides_html(static=false)
180
+ sections = ShowOffUtils.showoff_sections(options.pres_dir)
181
+ files = []
182
+ if sections
183
+ sections.each do |section|
184
+ files << load_section_files(section)
185
+ end
186
+ files = files.flatten
187
+ files = files.select { |f| f =~ /.md/ }
188
+ data = ''
189
+ files.each do |f|
190
+ fname = f.gsub(options.pres_dir + '/', '').gsub('.md', '')
191
+ data += process_markdown(fname, File.read(f),static)
192
+ end
193
+ end
194
+ data
195
+ end
196
+
197
+ def inline_css(csses, pre = nil)
198
+ css_content = '<style type="text/css">'
199
+ csses.each do |css_file|
200
+ if pre
201
+ css_file = File.join(File.dirname(__FILE__), '..', pre, css_file)
202
+ else
203
+ css_file = File.join(options.pres_dir, css_file)
204
+ end
205
+ css_content += File.read(css_file)
206
+ end
207
+ css_content += '</style>'
208
+ css_content
209
+ end
210
+
211
+ def inline_js(jses, pre = nil)
212
+ js_content = '<script type="text/javascript">'
213
+ jses.each do |js_file|
214
+ if pre
215
+ js_file = File.join(File.dirname(__FILE__), '..', pre, js_file)
216
+ else
217
+ js_file = File.join(options.pres_dir, js_file)
218
+ end
219
+ js_content += File.read(js_file)
220
+ end
221
+ js_content += '</script>'
222
+ js_content
223
+ end
224
+
225
+ def index(static=false)
226
+ if static
227
+ @slides = get_slides_html(static)
228
+ @asset_path = "."
229
+ end
230
+ erb :index
231
+ end
232
+
233
+ def clean_link(href)
234
+ if href && href[0, 1] == '/'
235
+ href = href[1, href.size]
236
+ end
237
+ href
238
+ end
239
+
240
+ def assets_needed
241
+ assets = ["index", "slides"]
242
+
243
+ index = erb :index
244
+ html = Nokogiri::XML.parse(index)
245
+ html.css('head link').each do |link|
246
+ href = clean_link(link['href'])
247
+ assets << href if href
248
+ end
249
+ html.css('head script').each do |link|
250
+ href = clean_link(link['src'])
251
+ assets << href if href
252
+ end
253
+
254
+ slides = get_slides_html
255
+ html = Nokogiri::XML.parse("<slides>" + slides + "</slides>")
256
+ html.css('img').each do |link|
257
+ href = clean_link(link['src'])
258
+ assets << href if href
259
+ end
260
+
261
+ assets.join("\n")
262
+ end
263
+
264
+ def slides(static=false)
265
+ get_slides_html(static)
266
+ end
267
+
268
+ def onepage(static=false)
269
+ @slides = get_slides_html(static)
270
+ erb :onepage
271
+ end
272
+
273
+ def pdf(static=false)
274
+ @slides = get_slides_html(static)
275
+ @no_js = true
276
+ html = erb :onepage
277
+ p = Princely.new
278
+ # TODO make a random filename
279
+ p.pdf_from_string_to_file(html, '/tmp/preso.pdf')
280
+ File.new('/tmp/preso.pdf')
281
+ end
282
+
283
+ end
284
+
285
+
286
+ def self.do_static(what)
287
+ what = "index" if !what
288
+
289
+ # Nasty hack to get the actual ShowOff module
290
+ showoff = ShowOff.new
291
+ while !showoff.is_a?(ShowOff)
292
+ showoff = showoff.instance_variable_get(:@app)
293
+ end
294
+ name = showoff.instance_variable_get(:@pres_name)
295
+ path = showoff.instance_variable_get(:@root_path)
296
+ data = showoff.send(what, true)
297
+ if data.is_a?(File)
298
+ File.cp(data.path, "#{name}.pdf")
299
+ else
300
+ out = "#{path}/#{name}/static"
301
+ # First make a directory
302
+ File.makedirs("#{out}")
303
+ # Then write the html
304
+ file = File.new("#{out}/index.html", "w")
305
+ file.puts(data)
306
+ file.close
307
+ # Now copy all the js and css
308
+ my_path = File.join( File.dirname(__FILE__), '..', 'public')
309
+ ["js", "css"].each { |dir|
310
+ FileUtils.copy_entry("#{my_path}/#{dir}", "#{out}/#{dir}")
311
+ }
312
+ # And copy the directory
313
+ Dir.glob("#{my_path}/#{name}/*").each { |subpath|
314
+ base = File.basename(subpath)
315
+ next if "static" == base
316
+ next unless File.directory?(subpath) || base.match(/\.(css|js)$/)
317
+ FileUtils.copy_entry(subpath, "#{out}/#{base}")
318
+ }
319
+ end
320
+ end
321
+
322
+
323
+
324
+ get %r{(?:image|file)/(.*)} do
325
+ path = params[:captures].first
326
+ full_path = File.join(options.pres_dir, path)
327
+ send_file full_path
328
+ end
329
+
330
+ get %r{/(.*)} do
331
+ @title = ShowOffUtils.showoff_title
332
+ what = params[:captures].first
333
+ what = 'index' if "" == what
334
+ if (what != "favicon.ico")
335
+ data = send(what)
336
+ if data.is_a?(File)
337
+ send_file data.path
338
+ else
339
+ data
340
+ end
341
+ end
342
+ end
343
+
344
+
345
+
346
+ end
@@ -0,0 +1,259 @@
1
+ class ShowOffUtils
2
+ SHOWOFF_JSON_FILE = 'showoff.json'
3
+
4
+ def self.create(dirname,create_samples,dir='one')
5
+ Dir.mkdir(dirname) if !File.exists?(dirname)
6
+ Dir.chdir(dirname) do
7
+ if create_samples
8
+ # create section
9
+ Dir.mkdir(dir)
10
+
11
+ # create markdown file
12
+ File.open("#{dir}/01_slide.md", 'w+') do |f|
13
+ f.puts make_slide("My Presentation")
14
+ f.puts make_slide("Bullet Points","bullets incremental",["first point","second point","third point"])
15
+ end
16
+ end
17
+
18
+ # create showoff.json
19
+ File.open(SHOWOFF_JSON_FILE, 'w+') do |f|
20
+ f.puts "{ \"name\": \"My Preso\", \"sections\": [ {\"section\":\"#{dir}\"} ]}"
21
+ end
22
+
23
+ if create_samples
24
+ puts "done. run 'showoff serve' in #{dirname}/ dir to see slideshow"
25
+ else
26
+ puts "done. add slides, modify #{SHOWOFF_JSON_FILE} and then run 'showoff serve' in #{dirname}/ dir to see slideshow"
27
+ end
28
+ end
29
+ end
30
+
31
+ def self.heroku(name)
32
+ if !File.exists?(SHOWOFF_JSON_FILE)
33
+ puts "fail. not a showoff directory"
34
+ return false
35
+ end
36
+ # create .gems file
37
+ File.open('.gems', 'w+') do |f|
38
+ f.puts "bluecloth"
39
+ f.puts "nokogiri"
40
+ f.puts "showoff"
41
+ f.puts "gli"
42
+ end if !File.exists?('.gems')
43
+
44
+ # create config.ru file
45
+ File.open('config.ru', 'w+') do |f|
46
+ f.puts 'require "showoff"'
47
+ f.puts 'run ShowOff.new'
48
+ end if !File.exists?('config.ru')
49
+
50
+ puts "herokuized. run something like this to launch your heroku presentation:
51
+
52
+ heroku create #{name}
53
+ git add .gems config.ru
54
+ git commit -m 'herokuized'
55
+ git push heroku master
56
+ "
57
+ end
58
+
59
+ # Makes a slide as a string.
60
+ # [title] title of the slide
61
+ # [classes] any "classes" to include, such as 'smaller', 'transition', etc.
62
+ # [content] slide content. Currently, if this is an array, it will make a bullet list. Otherwise
63
+ # the string value of this will be put in the slide as-is
64
+ def self.make_slide(title,classes="",content=nil)
65
+ slide = "!SLIDE #{classes}\n"
66
+ slide << "# #{title} #\n"
67
+ slide << "\n"
68
+ if content
69
+ if content.kind_of? Array
70
+ content.each { |x| slide << "* #{x.to_s}\n" }
71
+ else
72
+ slide << content.to_s
73
+ end
74
+ end
75
+ slide
76
+ end
77
+
78
+ TYPES = {
79
+ :default => lambda { |t,size,source,type| make_slide(t,"#{size} #{type}",source) },
80
+ 'title' => lambda { |t,size,dontcare| make_slide(t,size) },
81
+ 'bullets' => lambda { |t,size,dontcare| make_slide(t,"#{size} bullets incremental",["bullets","go","here"])},
82
+ 'smbullets' => lambda { |t,size,dontcare| make_slide(t,"#{size} smbullets incremental",["bullets","go","here","and","here"])},
83
+ 'code' => lambda { |t,size,src| make_slide(t,size,blank?(src) ? " @@@ Ruby\n code_here()" : src) },
84
+ 'commandline' => lambda { |t,size,dontcare| make_slide(t,"#{size} commandline"," $ command here\n output here")},
85
+ 'full-page' => lambda { |t,size,dontcare| make_slide(t,"#{size} full-page","![Image Description](image/ref.png)")},
86
+ }
87
+
88
+
89
+ # Adds a new slide to a given dir, giving it a number such that it falls after all slides
90
+ # in that dir.
91
+ # Options are:
92
+ # [:dir] - dir where we put the slide (if omitted, slide is output to $stdout)
93
+ # [:name] - name of the file, without the number prefix. (if omitted, a default is used)
94
+ # [:title] - title in the slide. If not specified the source file name is
95
+ # used. If THAT is not specified, uses the value of +:name+. If THAT is not
96
+ # specified, a suitable default is used
97
+ # [:code] - path to a source file to use as content (force :type to be 'code')
98
+ # [:number] - true if numbering should be done, false if not
99
+ # [:type] - the type of slide to create
100
+ def self.add_slide(options)
101
+
102
+ add_new_dir(options[:dir]) if options[:dir] && !File.exists?(options[:dir])
103
+
104
+ options[:type] = 'code' if options[:code]
105
+
106
+ title = determine_title(options[:title],options[:name],options[:code])
107
+
108
+ options[:name] = 'new_slide' if !options[:name]
109
+
110
+ size,source = determine_size_and_source(options[:code])
111
+ type = options[:type] || :default
112
+ slide = TYPES[type].call(title,size,source)
113
+
114
+ if options[:dir]
115
+ filename = determine_filename(options[:dir],options[:name],options[:number])
116
+ write_file(filename,slide)
117
+ else
118
+ puts slide
119
+ puts
120
+ end
121
+
122
+ end
123
+
124
+ # Adds the given directory to this presentation, appending it to
125
+ # the end of showoff.json as well
126
+ def self.add_new_dir(dir)
127
+ puts "Creating #{dir}..."
128
+ Dir.mkdir dir
129
+
130
+ showoff_json = JSON.parse(File.read(SHOWOFF_JSON_FILE))
131
+ showoff_json << { "section" => dir }
132
+ File.open(SHOWOFF_JSON_FILE,'w') do |file|
133
+ file.puts JSON.generate(showoff_json)
134
+ end
135
+ puts "#{SHOWOFF_JSON_FILE} updated"
136
+ end
137
+
138
+ def self.blank?(string)
139
+ string.nil? || string.strip.length == 0
140
+ end
141
+
142
+ def self.determine_size_and_source(code)
143
+ size = ""
144
+ source = ""
145
+ if code
146
+ source,lines,width = read_code(code)
147
+ size = adjust_size(lines,width)
148
+ end
149
+ [size,source]
150
+ end
151
+
152
+ def self.write_file(filename,slide)
153
+ File.open(filename,'w') do |file|
154
+ file.puts slide
155
+ end
156
+ puts "Wrote #{filename}"
157
+ end
158
+
159
+ def self.determine_filename(slide_dir,slide_name,number)
160
+ filename = "#{slide_dir}/#{slide_name}.md"
161
+ if number
162
+ max = find_next_number(slide_dir)
163
+ filename = "#{slide_dir}/#{max}_#{slide_name}.md"
164
+ end
165
+ filename
166
+ end
167
+
168
+ # Finds the next number in the given dir to
169
+ # name a slide as the last slide in the dir.
170
+ def self.find_next_number(slide_dir)
171
+ max = 0
172
+ Dir.open(slide_dir).each do |file|
173
+ if file =~ /(\d+).*\.md/
174
+ num = $1.to_i
175
+ max = num if num > max
176
+ end
177
+ end
178
+ max += 1
179
+ max = "0#{max}" if max < 10
180
+ max
181
+ end
182
+
183
+ def self.determine_title(title,slide_name,code)
184
+ if blank?(title)
185
+ title = slide_name
186
+ title = File.basename(code) if code
187
+ end
188
+ title = "Title here" if blank?(title)
189
+ title
190
+ end
191
+
192
+ # Determines a more optimal value for the size (e.g. small vs. smaller)
193
+ # based upon the size of the code being formatted.
194
+ def self.adjust_size(lines,width)
195
+ size = ""
196
+ # These values determined empircally
197
+ size = "small" if width > 50
198
+ size = "small" if lines > 15
199
+ size = "smaller" if width > 57
200
+ size = "smaller" if lines > 19
201
+ puts "warning, some lines are too long and the code may be cut off" if width > 65
202
+ puts "warning, your code is too long and the code may be cut off" if lines > 23
203
+ size
204
+ end
205
+
206
+ # Reads the code from the source file, returning
207
+ # the code, indented for markdown, as well as the number of lines
208
+ # and the width of the largest line
209
+ def self.read_code(source_file)
210
+ code = " @@@ #{lang(source_file)}\n"
211
+ lines = 0
212
+ width = 0
213
+ File.open(source_file) do |code_file|
214
+ code_file.readlines.each do |line|
215
+ code += " #{line}"
216
+ lines += 1
217
+ width = line.length if line.length > width
218
+ end
219
+ end
220
+ [code,lines,width]
221
+ end
222
+
223
+ def self.showoff_sections(dir = '.')
224
+ index = File.join(dir, ShowOffUtils::SHOWOFF_JSON_FILE )
225
+ order = nil
226
+ if File.exists?(index)
227
+ data = JSON.parse(File.read(index))
228
+ pp data
229
+ if data.is_a?(Hash)
230
+ order = data['sections']
231
+ else
232
+ order = data
233
+ end
234
+ order = order.map { |s| s['section'] }
235
+ end
236
+ order
237
+ end
238
+
239
+ def self.showoff_title(dir = '.')
240
+ index = File.join(dir, ShowOffUtils::SHOWOFF_JSON_FILE )
241
+ order = nil
242
+ if File.exists?(index)
243
+ data = JSON.parse(File.read(index))
244
+ data.is_a?(Hash) && data['name'] || "Presentation"
245
+ end
246
+ end
247
+
248
+ EXTENSIONS = {
249
+ 'pl' => 'perl',
250
+ 'rb' => 'ruby',
251
+ 'erl' => 'erlang',
252
+ # so not exhaustive, but probably good enough for now
253
+ }
254
+
255
+ def self.lang(source_file)
256
+ ext = File.extname(source_file).gsub(/^\./,'')
257
+ EXTENSIONS[ext] || ext
258
+ end
259
+ end