zlide 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +47 -0
  5. data/Rakefile +8 -0
  6. data/bin/zlide +9 -0
  7. data/lib/zlide.rb +9 -0
  8. data/lib/zlide/cli/base.rb +40 -0
  9. data/lib/zlide/deck.rb +56 -0
  10. data/lib/zlide/layouts/deckjs.haml +44 -0
  11. data/lib/zlide/redcarpet/renderers.rb +5 -0
  12. data/lib/zlide/redcarpet/renderers/html.rb +35 -0
  13. data/lib/zlide/redcarpet/renderers/mock.rb +31 -0
  14. data/lib/zlide/redcarpet/renderers/pdf.rb +214 -0
  15. data/lib/zlide/server/base.rb +45 -0
  16. data/lib/zlide/templates/README.md.tt +5 -0
  17. data/lib/zlide/templates/config/deck.yml.tt +1 -0
  18. data/lib/zlide/templates/stylesheets/deck-theme.css +214 -0
  19. data/lib/zlide/templates/stylesheets/highlight-theme.css +127 -0
  20. data/lib/zlide/templates/stylesheets/slides.css +10 -0
  21. data/lib/zlide/version.rb +3 -0
  22. data/spec/spec_helper.rb +3 -0
  23. data/spec/zlide/deck_spec.rb +32 -0
  24. data/spec/zlide/server/base_spec.rb +23 -0
  25. data/vendor/javascripts/deck.js +1 -0
  26. data/vendor/javascripts/deck/deck.core.js +498 -0
  27. data/vendor/javascripts/deck/deck.edit.js +33 -0
  28. data/vendor/javascripts/deck/deck.goto.js +170 -0
  29. data/vendor/javascripts/deck/deck.hash.js +142 -0
  30. data/vendor/javascripts/deck/deck.menu.js +187 -0
  31. data/vendor/javascripts/deck/deck.navigation.js +92 -0
  32. data/vendor/javascripts/deck/deck.scale.js +170 -0
  33. data/vendor/javascripts/deck/deck.status.js +95 -0
  34. data/vendor/javascripts/highlight.js +1 -0
  35. data/vendor/javascripts/jquery-1.7.2.min.js +4 -0
  36. data/vendor/javascripts/modernizr.custom.js +4 -0
  37. data/vendor/stylesheets/deck.css +4 -0
  38. data/vendor/stylesheets/deck/deck.core.css +407 -0
  39. data/vendor/stylesheets/deck/deck.goto.css +41 -0
  40. data/vendor/stylesheets/deck/deck.hash.css +13 -0
  41. data/vendor/stylesheets/deck/deck.menu.css +47 -0
  42. data/vendor/stylesheets/deck/deck.navigation.css +43 -0
  43. data/vendor/stylesheets/deck/deck.scale.css +28 -0
  44. data/vendor/stylesheets/deck/deck.status.css +18 -0
  45. data/vendor/stylesheets/deck/transition/fade.css +43 -0
  46. data/vendor/stylesheets/deck/transition/fade.scss +69 -0
  47. data/vendor/stylesheets/deck/transition/horizontal-slide.css +76 -0
  48. data/vendor/stylesheets/deck/transition/horizontal-slide.scss +90 -0
  49. data/vendor/stylesheets/deck/transition/vertical-slide.css +94 -0
  50. data/vendor/stylesheets/deck/transition/vertical-slide.scss +112 -0
  51. data/vendor/stylesheets/test.css +3 -0
  52. data/zlide.gemspec +27 -0
  53. metadata +229 -0
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ slides
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in zlide.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 David Roetzel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,47 @@
1
+ # zlide
2
+
3
+ zlide is a tool to build web-based presentations from markdown files.
4
+
5
+ It currently uses deck.js for handling of slides in the browser.
6
+
7
+ ## Alternatives
8
+
9
+ zlide is currently a work in progress and you are probably better served by one of the many alternatives.
10
+
11
+ If all you want is to generate and dynamically serve up deck.js presentations from markdown files, have a look at deck.rb (https://github.com/alexch/deck.rb).
12
+
13
+ If you want a maximum of flexibility check out the excellent slideshow gem: http://slideshow.rubyforge.org/
14
+
15
+ ## Installation
16
+
17
+ Install via rubygems:
18
+
19
+ $ gem install zlide
20
+
21
+ ## Usage
22
+
23
+ Create a new slide deck:
24
+
25
+ $ zlide new <deck_name>
26
+
27
+ This will scaffold a directory called 'deck_name' with a few files and directories to get you started.
28
+
29
+ Change to the newly created directory, put one or more markdown files in the slides/ folder and you are good to go.
30
+
31
+ Serve up your presentation:
32
+
33
+ $ zlide serve
34
+
35
+ This will start a web server on port 4567. Just navigate to http://localhost:4567 in your web browser.
36
+
37
+ Compile a pdf handout:
38
+
39
+ $ zlide pdf
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "zlide"
4
+
5
+ if File.exist?('config/deck.yml')
6
+ CONFIG = YAML.load(File.read('config/deck.yml'))
7
+ end
8
+
9
+ Zlide::CLI::Base.start
@@ -0,0 +1,9 @@
1
+ require "zlide/version"
2
+ require "zlide/deck"
3
+ require "zlide/server/base"
4
+ require "zlide/cli/base"
5
+ require "zlide/redcarpet/renderers"
6
+
7
+ module Zlide
8
+ # Your code goes here...
9
+ end
@@ -0,0 +1,40 @@
1
+ require 'thor'
2
+
3
+ module Zlide
4
+ module CLI
5
+ class Base < Thor
6
+ include Thor::Actions
7
+
8
+ def self.source_root
9
+ File.expand_path("../../templates", __FILE__)
10
+ end
11
+
12
+ desc "new", "create a new slide deck"
13
+ def new(name)
14
+ empty_directory(name)
15
+ self.destination_root = name
16
+ ["slides", "public", "stylesheets", "config"].each do |subdirectory|
17
+ empty_directory(subdirectory)
18
+ end
19
+ @name = name
20
+ template "config/deck.yml.tt"
21
+ template "README.md.tt"
22
+ copy_file "stylesheets/deck-theme.css"
23
+ copy_file "stylesheets/highlight-theme.css"
24
+ copy_file "stylesheets/slides.css"
25
+ end
26
+
27
+ desc "serve", "serve your slides via http"
28
+ def serve
29
+ Zlide::Server::Base.run!
30
+ end
31
+
32
+ desc "pdf", "create a pdf version of your slides"
33
+ def pdf
34
+ deck = Zlide::Deck.new
35
+ deck.to_pdf
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,56 @@
1
+ require 'haml'
2
+ require 'yaml'
3
+
4
+ module Zlide
5
+
6
+ class Deck
7
+
8
+ def initialize
9
+ files = Dir.glob('slides/*.md')
10
+ @slides = Array.new
11
+ files.sort.each do |f|
12
+ slides = File.read(f).split(/(^!SLIDE.*$)/)[1..-1].each_slice(2).map(&:join)
13
+ slides.each do |slide|
14
+ @slides << {:file => f, :content => slide}
15
+ end
16
+ end
17
+ end
18
+
19
+ def slide(index)
20
+ @slides[index][:content]
21
+ end
22
+
23
+ def update_slide(index, content)
24
+ @slides[index][:content] = content
25
+ file = @slides[index][:file]
26
+ file_slides = @slides.select{|s| s[:file] == file}.map{|s| s[:content]}
27
+ File.open("#{file}.tmp", "w") {|f| f.write(file_slides.join)}
28
+ File.rename("#{file}.tmp", file)
29
+ end
30
+
31
+ def to_html
32
+ slides = markdown_parser(Zlide::Redcarpet::Renderers::HTML).render(full_markdown)
33
+ layout = File.read(File.join(File.dirname(__FILE__), "layouts", "deckjs.haml"))
34
+ layout_renderer = Haml::Engine.new(layout)
35
+ layout_renderer.render { slides }
36
+ end
37
+
38
+ def to_pdf
39
+ pdf = Zlide::Redcarpet::Renderers::PDF.new
40
+ markdown_parser(pdf).render(full_markdown)
41
+ pdf.write_file
42
+ end
43
+
44
+ private
45
+
46
+ def full_markdown
47
+ @slides.map{|s| s[:content]}.join
48
+ end
49
+
50
+ def markdown_parser(renderer)
51
+ ::Redcarpet::Markdown.new(renderer, :fenced_code_blocks => true, :tables => true)
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,44 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{:charset => "utf-8"}/
5
+ %meta{:content => "IE=edge,chrome=1", "http-equiv" => "X-UA-Compatible"}/
6
+ %meta{:content => "width=1024, user-scalable=no", :name => "viewport"}/
7
+ %title= CONFIG['title']
8
+ / Required stylesheet
9
+ %link{:href => "/stylesheets/deck.css", :rel => "stylesheet"}/
10
+ %link{:href => "/stylesheets/slides.css", :rel => "stylesheet"}/
11
+ / Required Modernizr file
12
+ %script{:src => "/javascripts/modernizr.custom.js"}
13
+ %body.deck-container
14
+ / Begin slides. Just make elements with a class of slide.
15
+ = yield
16
+ / End slides.
17
+ / Begin extension snippets. Add or remove as needed.
18
+ / deck.navigation snippet
19
+ %a.deck-prev-link{:href => "#", :title => "Previous"} ←
20
+ %a.deck-next-link{:href => "#", :title => "Next"} →
21
+ / deck.status snippet
22
+ %p.deck-status
23
+ %span.deck-status-current
24
+ \/
25
+ %span.deck-status-total
26
+ / deck.goto snippet
27
+ %form.goto-form{:action => ".", :method => "get"}
28
+ %label{:for => "goto-slide"} Go to slide:
29
+ %input#goto-slide{:list => "goto-datalist", :name => "slidenum", :type => "text"}/
30
+ %datalist#goto-datalist
31
+ %input{:type => "submit", :value => "Go"}/
32
+ / deck.hash snippet
33
+ %a.deck-permalink{:href => ".", :title => "Permalink to this slide"} #
34
+ / End extension snippets.
35
+ / Required JS files.
36
+ %script{:src => "/javascripts/jquery-1.7.2.min.js"}
37
+ %script{:src => "/javascripts/deck.js"}
38
+ %script{:src => "/javascripts/highlight.js"}
39
+ / Initialize the deck. You can put this in an external file if desired.
40
+ :javascript
41
+ $(function() {
42
+ $.deck('.slide');
43
+ $('pre code').each(function(i, e) {hljs.highlightBlock(e)});
44
+ });
@@ -0,0 +1,5 @@
1
+ require "redcarpet"
2
+
3
+ require "zlide/redcarpet/renderers/mock"
4
+ require "zlide/redcarpet/renderers/html"
5
+ require "zlide/redcarpet/renderers/pdf"
@@ -0,0 +1,35 @@
1
+ module Zlide
2
+ module Redcarpet
3
+ module Renderers
4
+
5
+ class HTML < ::Redcarpet::Render::HTML
6
+
7
+ def initialize(options = {})
8
+ @slide_open = false
9
+ super(options)
10
+ end
11
+
12
+ def paragraph(text)
13
+ result = ""
14
+ if text =~ /^!SLIDE/
15
+ result += "</section>\n" if @slide_open
16
+ result += "<section class=\"slide\">\n"
17
+ @slide_open = true
18
+ else
19
+ result += "<p>#{text}</p>\n"
20
+ end
21
+ result
22
+ end
23
+
24
+ def doc_footer
25
+ if @slide_open
26
+ @slide_open = false
27
+ "</section>"
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ module Zlide
2
+ module Redcarpet
3
+ module Renderers
4
+
5
+ class Mock < ::Redcarpet::Render::HTML #Base
6
+
7
+ def block_html(text)
8
+ puts "block html: #{text}"
9
+ end
10
+
11
+ def paragraph(text)
12
+ if text =~ /!SLIDE/
13
+ puts "p: #{text}"
14
+ else
15
+ puts super(text)
16
+ end
17
+ end
18
+
19
+ def header(text, level)
20
+ puts "header#{level}: #{text}"
21
+ end
22
+
23
+ def entity(text)
24
+ puts "ENTITY: #{text}"
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,214 @@
1
+ # encoding: utf-8
2
+ require 'prawn'
3
+ require 'prawn/measurement_extensions'
4
+
5
+ module Zlide
6
+ module Redcarpet
7
+ module Renderers
8
+
9
+ class PDF < ::Redcarpet::Render::Base
10
+
11
+ class ListData < ::Array ; end
12
+ class TableData < ::Array ; end
13
+
14
+ def initialize
15
+ @pdf = Prawn::Document.new(
16
+ :page_size => "A4",
17
+ :page_layout => :landscape,
18
+ :info => {:Title => CONFIG['title']}
19
+ )
20
+ @pdf.define_grid(:rows => 2, :columns => 2, :gutter => 1.cm)
21
+ @current_row = 0
22
+ @current_column = 0
23
+ @command_queue = []
24
+ @current_box = @pdf.grid(0,0)
25
+ @slide_open = false
26
+ @list_items = []
27
+ @table_cells = []
28
+ @table_rows = []
29
+ @table_cache = []
30
+ super
31
+ end
32
+
33
+ def header(text, level)
34
+ text_size = 17 - level
35
+ @command_queue << Proc.new do
36
+ @pdf.text text, :size => text_size, :style => :bold, :inline_format => true
37
+ if level <= 2
38
+ @pdf.line_width 0.5
39
+ @pdf.horizontal_rule
40
+ @pdf.stroke
41
+ end
42
+ @pdf.move_down text_size
43
+ end
44
+ ""
45
+ end
46
+
47
+ def list(content, type)
48
+ data = ListData.new
49
+ counter = 1
50
+ number_of_items = content.count('.')
51
+ @list_items.pop(number_of_items).each do |item|
52
+ if item.is_a? String
53
+ case type
54
+ when :unordered
55
+ data << ["•", item]
56
+ when :ordered
57
+ data << ["#{counter}.", item]
58
+ counter += 1
59
+ end
60
+ else
61
+ data << [nil, item]
62
+ @command_queue.delete(item)
63
+ end
64
+ end
65
+ index = @table_cache.size
66
+ @command_queue << data
67
+ @table_cache << data
68
+ "__table_#{index}"
69
+ end
70
+
71
+ def list_item(text, type)
72
+ table = nil
73
+ matches = text.scan(/\n__table_\d+/)
74
+ if matches.any?
75
+ table_marker = matches.first
76
+ table = @table_cache[table_marker.sub("\n__table_", '').to_i]
77
+ text.sub!(table_marker, '')
78
+ end
79
+ @list_items << text
80
+ @list_items << table if table
81
+ table ? ".." : "."
82
+ end
83
+
84
+ def table(header, body)
85
+ data = TableData.new
86
+ body_rows = @table_rows.pop(body.count('.'))
87
+ header_rows = @table_rows.pop(body.count('.'))
88
+ data.concat header_rows.map{|row| row.map{|column| tag(:b, column)}}
89
+ data.concat body_rows
90
+ @command_queue << data
91
+ ""
92
+ end
93
+
94
+ def table_row(content)
95
+ @table_rows << @table_cells.pop(content.count('.'))
96
+ "."
97
+ end
98
+
99
+ def table_cell(content, alignment)
100
+ @table_cells << content
101
+ "."
102
+ end
103
+
104
+ def emphasis(text)
105
+ tag :i, text
106
+ end
107
+
108
+ def double_emphasis(text)
109
+ tag :b, text
110
+ end
111
+
112
+ def strikethrough(text)
113
+ tag :strikethrough, text
114
+ end
115
+
116
+ def link(href, title, content)
117
+ "<link href='#{href}'>#{content}</link>"
118
+ end
119
+
120
+ def image(href, title, alt_text)
121
+ @command_queue << Proc.new do
122
+ @pdf.image File.join('public', href), :fit => [@pdf.bounds.width, @pdf.cursor - 5]
123
+ end
124
+ ""
125
+ end
126
+
127
+ def paragraph(text)
128
+ if text =~ /^!SLIDE/
129
+ if @slide_open
130
+ next_box
131
+ else
132
+ @slide_open = true
133
+ end
134
+ else
135
+ @command_queue << Proc.new { @pdf.text(text, :inline_format => true) }
136
+ end
137
+ ""
138
+ end
139
+
140
+ def block_code(code, language)
141
+ code.gsub!(/^(\s+)/m) { |m| "\xC2\xA0" * m.size }
142
+ code_block = Proc.new do
143
+ font = @pdf.font.name
144
+ @pdf.bounding_box([5, @pdf.cursor], :width => @pdf.bounds.width - 10) do
145
+ @pdf.move_down 5
146
+ @pdf.font "Courier"
147
+ @pdf.indent(5) { @pdf.text code, :size => 9 }
148
+ @pdf.line_width 0.1
149
+ @pdf.join_style :round
150
+ @pdf.stroke_bounds
151
+ end
152
+ @pdf.font font
153
+ end
154
+ @command_queue << code_block
155
+ ""
156
+ end
157
+
158
+ def write_file
159
+ @pdf.render_file("#{CONFIG['title']}.pdf")
160
+ end
161
+
162
+ private
163
+
164
+ def next_box
165
+ @current_box.bounding_box do
166
+ @pdf.line_width 0.5
167
+ @pdf.stroke_bounds
168
+ @pdf.bounding_box([5,@pdf.bounds.top - 5], :width => @pdf.bounds.width - 10, :height => @pdf.bounds.height - 10) do
169
+ @command_queue.each do |c|
170
+ if c.is_a? Proc
171
+ c.call
172
+ elsif c.is_a? ListData
173
+ cell_style = {:borders => [], :padding => 2}
174
+ data = c.map do |row|
175
+ row.map do |cell|
176
+ if cell.is_a? Array
177
+ @pdf.make_table cell, :cell_style => cell_style.merge(:inline_format => true), :column_widths => [25, @pdf.bounds.width - 55]
178
+ else
179
+ cell
180
+ end
181
+ end
182
+ end
183
+ @pdf.table data, :cell_style => cell_style, :column_widths => [25, @pdf.bounds.width - 30] do |t|
184
+ t.cells.grep(Prawn::Table::Cell::Text).each {|text| text.style({:inline_format => true})}
185
+ end
186
+ elsif c.is_a? TableData
187
+ @pdf.table c, :cell_style => {:inline_format => true, :border_width => 0.5}
188
+ end
189
+ end
190
+ end
191
+ end
192
+ @command_queue.clear
193
+ if @current_row == 1 and @current_column == 1
194
+ @pdf.start_new_page
195
+ @current_row = 0
196
+ @current_column = 0
197
+ else
198
+ @current_column += 1
199
+ if @current_column > 1
200
+ @current_row += 1
201
+ @current_column = 0
202
+ end
203
+ end
204
+ @current_box = @pdf.grid(@current_row, @current_column)
205
+ end
206
+
207
+ def tag(type, content)
208
+ "<#{type}>#{content}</#{type}>"
209
+ end
210
+ end
211
+
212
+ end
213
+ end
214
+ end