Skip to content
This repository

Templating + Fixes for Static and PDF Generation #149

Merged
merged 16 commits into from over 2 years ago

3 participants

Martin Grund Mani Tadayon Gonçalo Silva
Martin Grund

Hi,

basically this group of commits does a few things.

First, it implements a way to provide the user with layout templates if he wants and make the pretty configurable . Templates can be applied either per default or per slide. Please have a look at the updated readme for a usage instruction.

Second, I fixed a a few bugs that relate to static HTML and PDF generation. This includes, rewriting URLs used in CSS to point to static file:// URIs, forcing wkthmltopdf to use the print media styles instead of the screen media styles. In addition sytnax highlighting now works both in PDF and static mode.

It would be great if you could review my changes.

You can find a simple presentation of how to use the templates, made with showoff here - http://grundprinzip.github.com/Showoff-Templates

Thanks
Martin

Martin Grund grundprinzip Fixing print CSS
When printing the CSS file paths need to be rewritten
to match the absolute URIs used in PDFKit. In addition, it
is required to configure the print media style or 
the output will be buggy. 

In print mode, the position of the slide needs to be 
set explicitly to "relavtive" or more explict styling 
in the slide container will fail in print mode.
96d510d
Martin Grund grundprinzip Templates
* allow templates to be configurable
* allow default template to be not present
* parsing of slide options
04dd4eb
Martin Grund grundprinzip Templates for Statics and PDFs
* fixing positioning of a slide when using the onepage.css
ce08379
Martin Grund grundprinzip Documentation for Templates a838d8c
Martin Grund grundprinzip Proper formating for documentation bad2a58
Martin Grund grundprinzip Documentation and Replacements
Replaced the ###CONTENT### string with something 
that does not break in Markdown :). Furthermore now
multiple replacements can be used in the slides, the 
documentation is in the README.
ea6046a
Martin Grund grundprinzip Fixing syntax highlighting in static mode and PDF output 00f52a9
Martin Grund grundprinzip Removing old init stuff f6a82a4
Martin Grund grundprinzip Avoiding error if no @languages specified e900701
Martin Grund grundprinzip Fixing bad logger variable in static mode 2b76bc2
Martin Grund

This would close #143 and #144 as well.

Martin Grund grundprinzip referenced this pull request
Closed

Giant Merge #150

Martin Grund

I merged the recent changes from schacon/master to keep the changes compatible with the current development.

Mani Tadayon

Hey, this is just what I was looking for! I hope this gets merged in soon.

Gonçalo Silva
Collaborator

I love this change. I'm not so sure about the ~~~ SYNTAX ~~~, but I don't have a better suggestion either.

I'll merge this and tweak the README a little bit. This is an advanced feature and I believe it's introduced too soon.

Gonçalo Silva goncalossilva merged commit 580c025 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 16 unique commits by 1 author.

Nov 12, 2011
Martin Grund grundprinzip Fixing print CSS
When printing the CSS file paths need to be rewritten
to match the absolute URIs used in PDFKit. In addition, it
is required to configure the print media style or 
the output will be buggy. 

In print mode, the position of the slide needs to be 
set explicitly to "relavtive" or more explict styling 
in the slide container will fail in print mode.
96d510d
Nov 13, 2011
Martin Grund grundprinzip Templates
* allow templates to be configurable
* allow default template to be not present
* parsing of slide options
04dd4eb
Martin Grund grundprinzip Templates for Statics and PDFs
* fixing positioning of a slide when using the onepage.css
ce08379
Martin Grund grundprinzip Documentation for Templates a838d8c
Martin Grund grundprinzip Proper formating for documentation bad2a58
Martin Grund grundprinzip Documentation and Replacements
Replaced the ###CONTENT### string with something 
that does not break in Markdown :). Furthermore now
multiple replacements can be used in the slides, the 
documentation is in the README.
ea6046a
Martin Grund grundprinzip Fixing syntax highlighting in static mode and PDF output 00f52a9
Martin Grund grundprinzip Removing old init stuff f6a82a4
Martin Grund grundprinzip Avoiding error if no @languages specified e900701
Nov 14, 2011
Martin Grund grundprinzip Fixing bad logger variable in static mode 2b76bc2
Martin Grund grundprinzip Fixing failing tests e685821
Martin Grund grundprinzip Added example to example slides as required by CONTRIB.txt 2f42a88
Nov 18, 2011
Martin Grund grundprinzip Merging recent changes from schacon/master 049e3b0
Nov 19, 2011
Martin Grund grundprinzip Merging schacon/master fbb96ab
Nov 21, 2011
Martin Grund grundprinzip Merge branch 'master' into templating
Conflicts:
	lib/showoff.rb
842c00b
Martin Grund grundprinzip Updating deprecated usage of options to settings 4030a46
This page is out of date. Refresh to see the latest.
97 README.rdoc
Source Rendered
@@ -84,7 +84,23 @@ the following contents:
84 84
85 85 That represents two slides, the first contains just a large title, and the
86 86 second is faded into view showing the title and three bullets that are then
87   -incrementally shown. In order for ShowOff to see those slides, your
  87 +incrementally shown. In addition you can configure a certain template for
  88 +this slide overriding the default one:
  89 +
  90 + !SLIDE[tpl=title]
  91 +
  92 + # My Presentation #
  93 +
  94 + !SLIDE bullets incremental
  95 +
  96 + # Bullet Points #
  97 +
  98 + * first point
  99 + * second point
  100 + * third point
  101 +
  102 +
  103 +In order for ShowOff to see those slides, your
88 104 <tt>showoff.json</tt> file needs to look something like this:
89 105
90 106 {
@@ -95,19 +111,20 @@ incrementally shown. In order for ShowOff to see those slides, your
95 111 ]
96 112 }
97 113
98   -If you have multiple sections in your talk, you can make this json array
99   -include all the sections you want to show in which order you want to show
100   -them.
  114 +If you have multiple sections in your talk, you can make this json
  115 +array include all the sections you want to show in which order you
  116 +want to show them. Template configuration is done in
  117 +<tt>showoff.json</tt> as well.
101 118
102   -Instead of a hash, you can use a plain string as an entry in the `sections`
103   -section of `showoff.json`.
104 119
105   -And if that plain string starts with '#' then it is interpreted not as a
106   -filename, but as markdown. This is used for inserting interstitial slides
107   -or notes -- for instance, Alex Chaffee's
108   -[Ruby Notes](http://github.com/alexch/ruby_notes)
109   -uses it to insert lab instructions between lecture slide sections, which may
110   -vary from venue to venue.
  120 +Instead of a hash, you can use a plain string as an entry in the
  121 +`sections` section of `showoff.json`. And if that plain string starts
  122 +with '#' then it is interpreted not as a filename, but as
  123 +markdown. This is used for inserting interstitial slides or notes --
  124 +for instance, Alex Chaffee's [Ruby
  125 +Notes](http://github.com/alexch/ruby_notes) uses it to insert lab
  126 +instructions between lecture slide sections, which may vary from venue
  127 +to venue.
111 128
112 129 If you want to keep the ability to emit an HTML document from your
113 130 Markdown source file -- say, for a TextMate preview or a GitHub rendering
@@ -447,6 +464,62 @@ You'll then need to install a version of wkhtmltopdf available at the {wkhtmltop
447 464
448 465 Then restart showoff, and navigate to <tt>/pdf</tt> (e.g. http://localhost/pdf) of your presentation and a PDF will be generated with the browser.
449 466
  467 += ShowOff Templates
  468 +
  469 +Templates can come handy if you need more than what you can achieve
  470 +via CSS. To configure templates you'll have to specify them in the
  471 +<tt>showoff.json</tt> by adding an entry called "templates". This
  472 +entry is an object where you can specify as many templates as you
  473 +want. The default template is marked with the "default" key.
  474 +
  475 + {
  476 + "name": "Something",
  477 + "description": "Example Presentation",
  478 + "templates" : {
  479 + "default" : "tpl1.tpl",
  480 + "special" : "tpl2.tpl"
  481 + },
  482 + "sections": [
  483 + {"section":"one"}
  484 + ]
  485 + }
  486 +
  487 +If the "default" key is not given, no template will be used for the
  488 +default slide. If you want to apply a certain layout to a slide you
  489 +have to specify it in the slide header:
  490 +
  491 + !SLIDE[tpl=special]
  492 + # Header
  493 +
  494 +== Template Commands
  495 +
  496 +You can place content anywhere in your template, but you have to
  497 +explicitly mark the location using a special command:
  498 +
  499 +[~~~CONTENT~~~] is replaced by the slide content
  500 +
  501 +[~~~CURRENT_SLIDE~~~] is replaced by the current slide number
  502 +
  503 +[~~~NUM_SLIDES~~~] is replaced by the total number of slides
  504 +
  505 +[~~~CONFIG:*~~~] is replaced by any value (*) from the
  506 + <tt>showoff.json</tt> configuration. This can be used to
  507 + specify an author, venue etc. A simple example would be
  508 + <tt>~~~CONFIG:author~~~</tt>
  509 +
  510 +
  511 +The usage of these replacements is not limited to templates, but
  512 +anywhere in your slides.
  513 +
  514 +== Template Hints
  515 +
  516 +You can basically put everything you want into templates, but you
  517 +should make sure that the CSS is applied fine. The best way to apply a
  518 +custom layout is to create a container that uses absolute positioning
  519 +and has width and height set to 100% which are then derived from the
  520 +parent slide element.
  521 +
  522 +
450 523 = Completion
451 524
452 525 == ZSH completion
5 example/four/01slide.md
Source Rendered
... ... @@ -0,0 +1,5 @@
  1 +!SLIDE[tpl=special]
  2 +
  3 +# A Template #
  4 +
  5 +Really? How many slides? -- ~~~NUM_SLIDES~~~
7 example/showoff.json
... ... @@ -1,9 +1,14 @@
1 1 {
2 2 "name": "Something",
3 3 "description": "Example Presentation",
  4 + "author": "Foo Bar John",
  5 + "templates" : {
  6 + "special" : "simple.tpl"
  7 + },
4 8 "sections": [
5 9 {"section":"one"},
6 10 {"section":"two"},
7   - {"section":"three"}
  11 + {"section":"three"},
  12 + {"section":"four"}
8 13 ]
9 14 }
2  example/simple.tpl
... ... @@ -0,0 +1,2 @@
  1 +<div class="border">~~~CONFIG:author~~~@~~~CURRENT_SLIDE~~~</div>
  2 +<div class="main">~~~CONTENT~~~<div>
117 lib/showoff.rb
@@ -33,6 +33,9 @@ class ShowOff < Sinatra::Application
33 33 set :verbose, false
34 34 set :pres_dir, '.'
35 35 set :pres_file, 'showoff.json'
  36 + set :page_size, "Letter"
  37 + set :pres_template, nil
  38 + set :showoff_config, nil
36 39
37 40 def initialize(app=nil)
38 41 super(app)
@@ -51,6 +54,21 @@ def initialize(app=nil)
51 54 if (settings.pres_file)
52 55 ShowOffUtils.presentation_config_file = settings.pres_file
53 56 end
  57 +
  58 + # Load configuration for page size and template from the
  59 + # configuration JSON file
  60 + if File.exists?(ShowOffUtils.presentation_config_file)
  61 + showoff_json = JSON.parse(File.read(ShowOffUtils.presentation_config_file))
  62 + settings.showoff_config = showoff_json
  63 +
  64 + # Set options for template and page size
  65 + settings.page_size = showoff_json["page-size"] || "Letter"
  66 + settings.pres_template = showoff_json["templates"]
  67 + end
  68 +
  69 +
  70 + @logger.debug settings.pres_template
  71 +
54 72 @cached_image_size = {}
55 73 @logger.debug settings.pres_dir
56 74 @pres_name = settings.pres_dir.split('/').pop
@@ -96,9 +114,21 @@ def preshow_files
96 114
97 115 # todo: move more behavior into this class
98 116 class Slide
99   - attr_reader :classes, :text
100   - def initialize classes = ""
101   - @classes = ["content"] + classes.strip.chomp('>').split
  117 + attr_reader :classes, :text, :tpl
  118 + def initialize( context = "")
  119 +
  120 + @tpl = "default"
  121 + @classes = ["content"]
  122 +
  123 + # Parse the context string for options and content classes
  124 + if context and context.match(/(\[(.*?)\])?(.*)/)
  125 +
  126 + options = ShowOffUtils.parse_options($2)
  127 + @tpl = options["tpl"] if options["tpl"]
  128 + @classes += $3.strip.chomp('>').split if $3
  129 +
  130 + end
  131 +
102 132 @text = ""
103 133 end
104 134 def <<(s)
@@ -126,7 +156,8 @@ def process_markdown(name, content, static=false, pdf=false)
126 156 until lines.empty?
127 157 line = lines.shift
128 158 if line =~ /^<?!SLIDE(.*)>?/
129   - slides << (slide = Slide.new($1))
  159 + ctx = $1 ? $1.strip : $1
  160 + slides << (slide = Slide.new(ctx))
130 161 else
131 162 slide << line
132 163 end
@@ -139,6 +170,7 @@ def process_markdown(name, content, static=false, pdf=false)
139 170 seq = 1
140 171 end
141 172 slides.each do |slide|
  173 + @slide_count += 1
142 174 md = ''
143 175 content_classes = slide.classes
144 176
@@ -151,27 +183,70 @@ def process_markdown(name, content, static=false, pdf=false)
151 183 @logger.debug "id: #{id}" if id
152 184 @logger.debug "classes: #{content_classes.inspect}"
153 185 @logger.debug "transition: #{transition}"
  186 + @logger.debug "tpl: #{slide.tpl} " if slide.tpl
154 187 # create html
155 188 md += "<div"
156 189 md += " id=\"#{id}\"" if id
157 190 md += " class=\"slide\" data-transition=\"#{transition}\">"
  191 +
  192 +
  193 + template = "~~~CONTENT~~~"
  194 + # Template handling
  195 + if settings.pres_template
  196 + # We allow specifying a new template even when default is
  197 + # not given.
  198 + if settings.pres_template.include?(slide.tpl) and
  199 + File.exists?(settings.pres_template[slide.tpl])
  200 + template = File.open(settings.pres_template[slide.tpl], "r").read()
  201 + end
  202 + end
  203 +
  204 + # Extract the content of the slide
  205 + content = ""
158 206 if seq
159   - md += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}/#{seq.to_s}\">\n"
160   - seq += 1
  207 + content += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}/#{seq.to_s}\">\n"
161 208 else
162   - md += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}\">\n"
  209 + content += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}\">\n"
163 210 end
164 211 sl = Tilt[:markdown].new { slide.text }.render
165 212 sl = update_image_paths(name, sl, static, pdf)
166   - md += sl
167   - md += "</div>\n"
  213 + content += sl
  214 + content += "</div>\n"
  215 +
  216 + # Apply the template to the slide and replace the key with
  217 + # content of the slide
  218 + md += process_content_for_replacements(template.gsub(/~~~CONTENT~~~/, content), @slide_count)
  219 +
  220 + # Apply other configuration
  221 +
168 222 md += "</div>\n"
169 223 final += update_commandline_code(md)
170 224 final = update_p_classes(final)
  225 +
  226 + if seq
  227 + seq += 1
  228 + end
171 229 end
172 230 final
173 231 end
174 232
  233 + # This method processes the content of the slide and replaces
  234 + # content markers with their actual value information
  235 + def process_content_for_replacements(content, seq)
  236 + result = content.gsub("~~~CURRENT_SLIDE~~~", seq.to_s)
  237 + # Now check for any kind of options
  238 + content.scan(/(~~~CONFIG:(.*?)~~~)/).each do |match|
  239 + result.gsub!(match[0], settings.showoff_config[match[1]]) if settings.showoff_config.key?(match[1])
  240 + end
  241 +
  242 + result
  243 + end
  244 +
  245 + def process_content_for_all_slides(content, num_slides)
  246 + content.gsub("~~~NUM_SLIDES~~~", num_slides.to_s)
  247 + end
  248 +
  249 +
175 250 # find any lines that start with a <p>.(something) and turn them into <p class="something">
176 251 def update_p_classes(markdown)
177 252 markdown.gsub(/<p>\.(.*?) /, '<p class="\1">')
@@ -260,6 +335,7 @@ def update_commandline_code(slide)
260 335 end
261 336
262 337 def get_slides_html(static=false, pdf=false)
  338 + @slide_count = 0
263 339 sections = ShowOffUtils.showoff_sections(settings.pres_dir, @logger)
264 340 files = []
265 341 if sections
@@ -280,7 +356,7 @@ def get_slides_html(static=false, pdf=false)
280 356 end
281 357 end
282 358 end
283   - data
  359 + process_content_for_all_slides(data, @slide_count)
284 360 end
285 361
286 362 def inline_css(csses, pre = nil)
@@ -319,6 +395,11 @@ def index(static=false)
319 395 if static
320 396 @title = ShowOffUtils.showoff_title
321 397 @slides = get_slides_html(static)
  398 +
  399 + # Identify which languages to bundle for highlighting
  400 + @languages = []
  401 + @languages += @slides.scan(/<pre class="(sh_.*?\w)"/).uniq.map{ |w| "sh_lang/#{w[0]}.min.js"}
  402 +
322 403 @asset_path = "./"
323 404 end
324 405 erb :index
@@ -377,9 +458,22 @@ def onepage(static=false)
377 458 def pdf(static=true)
378 459 @slides = get_slides_html(static, true)
379 460 @no_js = false
  461 +
  462 + # Identify which languages to bundle for highlighting
  463 + @languages = []
  464 + @languages += @slides.scan(/<pre class="(sh_.*?\w)"/).uniq.map{ |w| "/sh_lang/#{w[0]}.min.js"}
  465 +
380 466 html = erb :onepage
381 467 # TODO make a random filename
382 468
  469 + # Process inline css and js for included images
  470 + # The css uses relative paths for images and we prepend the file url
  471 + html.gsub!(/url\(([^\/].*?)\)/) do |s|
  472 + "url(file://#{settings.pres_dir}/#{$1})"
  473 + end
  474 +
  475 + # Todo fix javascript path
  476 +
383 477 # PDFKit.new takes the HTML and any options for wkhtmltopdf
384 478 # run `wkhtmltopdf --extended-help` for a full list of options
385 479 kit = PDFKit.new(html, ShowOffUtils.showoff_pdf_options)
@@ -403,6 +497,7 @@ def self.do_static(what)
403 497 path = showoff.instance_variable_get(:@root_path)
404 498 logger = showoff.instance_variable_get(:@logger)
405 499 data = showoff.send(what, true)
  500 +
406 501 if data.is_a?(File)
407 502 FileUtils.cp(data.path, "#{name}.pdf")
408 503 else
@@ -479,7 +574,9 @@ def eval_ruby code
479 574 @title = ShowOffUtils.showoff_title
480 575 what = params[:captures].first
481 576 what = 'index' if "" == what
  577 +
482 578 @asset_path = (env['SCRIPT_NAME'] || '').gsub(/\/?$/, '/').gsub(/^\//, '')
  579 +
483 580 if (what != "favicon.ico")
484 581 data = send(what)
485 582 if data.is_a?(File)
24 lib/showoff_utils.rb
... ... @@ -1,4 +1,28 @@
1 1 class ShowOffUtils
  2 +
  3 + # Helper method to parse a comma separated options string and stores
  4 + # the result in a dictionrary
  5 + #
  6 + # Example:
  7 + #
  8 + # "tpl=hpi,title=Over the rainbow"
  9 + #
  10 + # will be stored as
  11 + #
  12 + # { "tpl" => "hpi", "title" => "Over the rainbow" }
  13 + def self.parse_options(option_string="")
  14 + result = {}
  15 +
  16 + if option_string
  17 + option_string.split(",").each do |element|
  18 + pair = element.split("=")
  19 + result[pair[0]] = pair.size > 1 ? pair[1] : nil
  20 + end
  21 + end
  22 +
  23 + result
  24 + end
  25 +
2 26 def self.presentation_config_file
3 27 @presentation_config_file ||= 'showoff.json'
4 28 end
2  public/css/onepage.css
@@ -9,6 +9,7 @@
9 9 margin-left:auto;
10 10 margin-right:auto;
11 11 overflow:hidden;
  12 + position: relative;
12 13 border: 1px solid #333;
13 14 page-break-after: always
14 15 }
@@ -23,6 +24,7 @@
23 24 height: 600px;
24 25 overflow:hidden;
25 26 border: none;
  27 + position: relative;
26 28 page-break-after: always
27 29 }
28 30 }
4 public/css/showoff.css
@@ -372,7 +372,7 @@ a.fg-button { float:left; }
372 372 }
373 373
374 374 .slide .center {
375   - width: 600px;
  375 + width: 800px;
376 376 height: 600px;
377 377 display: table-cell;
378 378 text-align: center;
@@ -381,7 +381,7 @@ a.fg-button { float:left; }
381 381
382 382 #preso, .slide {
383 383 background: #fff;
384   - width: 600px;
  384 + width: 800px;
385 385 height: 600px;
386 386 margin-left:auto;
387 387 margin-right:auto;
5 public/js/onepage.js
... ... @@ -1,5 +1,4 @@
1 1 function setupOnePage() {
2   - sh_highlightDocument('/js/sh_lang/', '.min.js')
3   -
  2 + sh_highlightDocument();
4 3 centerSlides($("#slides > .slide"))
5   -}
  4 +}
14 public/js/showoff.js
@@ -28,6 +28,8 @@ function setupPreso(load_slides, prefix) {
28 28 }
29 29 preso_started = true
30 30
  31 +
  32 + // Load slides fetches images
31 33 loadSlidesBool = load_slides
32 34 loadSlidesPrefix = prefix
33 35 loadSlides(loadSlidesBool, loadSlidesPrefix)
@@ -64,7 +66,7 @@ function loadSlides(load_slides, prefix) {
64 66
65 67 function initializePresentation(prefix) {
66 68 // unhide for height to work in static mode
67   - $("#slides").show();
  69 + $("#slides").show();
68 70
69 71 //center slides offscreen
70 72 centerSlides($('#slides > .slide'))
@@ -90,7 +92,11 @@ function initializePresentation(prefix) {
90 92 slidesLoaded = true
91 93 }
92 94 setupSlideParamsCheck();
93   - sh_highlightDocument(prefix+'/js/sh_lang/', '.min.js')
  95 + try {
  96 + sh_highlightDocument(prefix+'/js/sh_lang/', '.min.js')
  97 + } catch(e) {
  98 + sh_highlightDocument();
  99 + }
94 100 $("#preso").trigger("showoff:loaded");
95 101 }
96 102
@@ -101,7 +107,7 @@ function centerSlides(slides) {
101 107 }
102 108
103 109 function centerSlide(slide) {
104   - var slide_content = $(slide).children(".content").first()
  110 + var slide_content = $(slide).find(".content").first()
105 111 var height = slide_content.height()
106 112 var mar_top = (0.5 * parseFloat($(slide).height())) - (0.5 * parseFloat(height))
107 113 if (mar_top < 0) {
@@ -117,7 +123,7 @@ function setupMenu() {
117 123 var menu = new ListMenu()
118 124
119 125 slides.each(function(s, elem) {
120   - content = $(elem).children(".content")
  126 + content = $(elem).find(".content")
121 127 shortTxt = $(content).text().substr(0, 20)
122 128 path = $(content).attr('ref').split('/')
123 129 currSlide += 1
6 views/header.erb
@@ -25,6 +25,12 @@
25 25 <link type="text/css" href="<%= @asset_path %>css/theme/ui.all.css" media="screen" rel="stylesheet" />
26 26 <link type="text/css" href="<%= @asset_path %>css/sh_style.css" rel="stylesheet" >
27 27
  28 +<% if @languages %>
  29 +<% @languages.each do |l| %>
  30 + <script type="text/javascript" src="<%= @asset_path %>js/<%= l %>"></script>
  31 +<% end %>
  32 +<% end %>
  33 +
28 34 <% css_files.each do |css_file| %>
29 35 <link rel="stylesheet" href="<%= @asset_path %>file/<%= css_file %>" type="text/css"/>
30 36 <% end %>
4 views/onepage.erb
@@ -15,7 +15,7 @@
15 15 <%= inline_css(css_files) %>
16 16
17 17 <% if !@no_js %>
18   - <%= inline_js(['jquery-1.4.2.min.js', 'jquery-print.js', 'showoff.js', 'onepage.js', 'sh_main.min.js', 'core.js', 'showoffcore.js'], 'public/js') %>
  18 + <%= inline_js(['jquery-1.4.2.min.js', 'jquery-print.js', 'showoff.js', 'onepage.js', 'sh_main.min.js', 'core.js', 'showoffcore.js'] + @languages, 'public/js') %>
19 19 <script type="text/javascript">
20 20 $(document).ready(function() {
21 21 setupOnePage()
@@ -25,10 +25,8 @@
25 25 </head>
26 26
27 27 <body>
28   -
29 28 <div id="slides">
30 29 <%= @slides %>
31 30 </div>
32   -
33 31 </body>
34 32 </html>

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.