deckrb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +63 -0
- data/bin/deck +59 -0
- data/deck/GPL-license.txt +278 -0
- data/deck/MIT-license.txt +21 -0
- data/deck/README.md +57 -0
- data/deck/core/deck.core.css +404 -0
- data/deck/core/deck.core.html +39 -0
- data/deck/core/deck.core.js +498 -0
- data/deck/core/deck.core.scss +447 -0
- data/deck/extensions/goto/deck.goto.css +41 -0
- data/deck/extensions/goto/deck.goto.html +7 -0
- data/deck/extensions/goto/deck.goto.js +134 -0
- data/deck/extensions/goto/deck.goto.scss +46 -0
- data/deck/extensions/hash/deck.hash.css +13 -0
- data/deck/extensions/hash/deck.hash.html +2 -0
- data/deck/extensions/hash/deck.hash.js +129 -0
- data/deck/extensions/hash/deck.hash.scss +15 -0
- data/deck/extensions/menu/deck.menu.css +47 -0
- data/deck/extensions/menu/deck.menu.js +187 -0
- data/deck/extensions/menu/deck.menu.scss +58 -0
- data/deck/extensions/navigation/deck.navigation.css +43 -0
- data/deck/extensions/navigation/deck.navigation.html +3 -0
- data/deck/extensions/navigation/deck.navigation.js +91 -0
- data/deck/extensions/navigation/deck.navigation.scss +56 -0
- data/deck/extensions/scale/deck.scale.css +16 -0
- data/deck/extensions/scale/deck.scale.js +155 -0
- data/deck/extensions/scale/deck.scale.scss +17 -0
- data/deck/extensions/status/deck.status.css +18 -0
- data/deck/extensions/status/deck.status.html +6 -0
- data/deck/extensions/status/deck.status.js +95 -0
- data/deck/extensions/status/deck.status.scss +22 -0
- data/deck/extensions/theme-picker/deck.theme-picker.css +55 -0
- data/deck/extensions/theme-picker/deck.theme-picker.js +13 -0
- data/deck/introduction/index.html +221 -0
- data/deck/introduction/index.rb +101 -0
- data/deck/jquery-1.7.min.js +4 -0
- data/deck/modernizr.custom.js +4 -0
- data/deck/test/fixtures/complex.html +24 -0
- data/deck/test/fixtures/empty.html +19 -0
- data/deck/test/fixtures/iframe_simple.html +10 -0
- data/deck/test/fixtures/iframes.html +32 -0
- data/deck/test/fixtures/nesteds.html +36 -0
- data/deck/test/fixtures/standard.html +42 -0
- data/deck/test/index.html +39 -0
- data/deck/test/lib/jasmine-html.js +190 -0
- data/deck/test/lib/jasmine-jquery.js +288 -0
- data/deck/test/lib/jasmine.css +166 -0
- data/deck/test/lib/jasmine.js +2477 -0
- data/deck/test/settings.js +3 -0
- data/deck/test/spec.core.js +434 -0
- data/deck/test/spec.goto.js +119 -0
- data/deck/test/spec.hash.js +81 -0
- data/deck/test/spec.menu.js +66 -0
- data/deck/test/spec.navigation.js +51 -0
- data/deck/test/spec.scale.js +57 -0
- data/deck/test/spec.status.js +58 -0
- data/deck/themes/style/neon.css +114 -0
- data/deck/themes/style/neon.scss +139 -0
- data/deck/themes/style/swiss.css +75 -0
- data/deck/themes/style/swiss.scss +91 -0
- data/deck/themes/style/web-2.0.css +205 -0
- data/deck/themes/style/web-2.0.scss +236 -0
- data/deck/themes/transition/fade.css +44 -0
- data/deck/themes/transition/fade.scss +70 -0
- data/deck/themes/transition/horizontal-slide.css +79 -0
- data/deck/themes/transition/horizontal-slide.scss +94 -0
- data/deck/themes/transition/vertical-slide.css +97 -0
- data/deck/themes/transition/vertical-slide.scss +116 -0
- data/lib/deck.rb +7 -0
- data/lib/deck/app.rb +16 -0
- data/lib/deck/deck.rb +166 -0
- data/lib/deck/rack_static_patch.rb +13 -0
- data/lib/deck/slide.rb +120 -0
- data/lib/deck/version.rb +3 -0
- data/spec/deck_spec.rb +98 -0
- data/spec/javascripts/support/jasmine_config.rb +23 -0
- data/spec/javascripts/support/jasmine_runner.rb +32 -0
- data/spec/slide_spec.rb +285 -0
- data/spec/spec_helper.rb +7 -0
- metadata +172 -0
data/lib/deck.rb
ADDED
data/lib/deck/app.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Deck
|
2
|
+
class App
|
3
|
+
def initialize root, files
|
4
|
+
@root, @files = root, files
|
5
|
+
end
|
6
|
+
|
7
|
+
def call env
|
8
|
+
slides = []
|
9
|
+
@files.each do |file|
|
10
|
+
slides += Slide.from_file "#{@root}/#{file}"
|
11
|
+
end
|
12
|
+
deck = Deck.new :slides => slides
|
13
|
+
[200, {}, deck.to_pretty]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/deck/deck.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'erector'
|
2
|
+
require 'redcarpet'
|
3
|
+
|
4
|
+
require "deck/slide"
|
5
|
+
|
6
|
+
module Deck
|
7
|
+
class Deck < Erector::Widgets::Page
|
8
|
+
needs :title => "deck.rb presentation",
|
9
|
+
:description => nil,
|
10
|
+
:author => nil
|
11
|
+
|
12
|
+
needs :slides => nil
|
13
|
+
|
14
|
+
def page_title
|
15
|
+
@title
|
16
|
+
end
|
17
|
+
|
18
|
+
# todo: promote into Text
|
19
|
+
# todo: support numbers a la 'Ӓ'
|
20
|
+
def entity entity_id
|
21
|
+
raw("&#{entity_id};")
|
22
|
+
end
|
23
|
+
|
24
|
+
# left over from deck.js' introduction/index.html
|
25
|
+
|
26
|
+
# <!DOCTYPE html>
|
27
|
+
# <!--[if lt IE 7]> <html class="no-js ie6" lang="en"> <![endif]-->
|
28
|
+
# <!--[if IE 7]> <html class="no-js ie7" lang="en"> <![endif]-->
|
29
|
+
# <!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
|
30
|
+
# <!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
|
31
|
+
|
32
|
+
# todo: promote into Page
|
33
|
+
def stylesheet src, attributes = {}
|
34
|
+
link({:rel => "stylesheet", :href => src}.merge(attributes))
|
35
|
+
end
|
36
|
+
|
37
|
+
def head_content
|
38
|
+
super
|
39
|
+
meta 'charset' => 'utf-8'
|
40
|
+
meta 'http-equiv'=>"X-UA-Compatible", 'content'=>"IE=edge,chrome=1"
|
41
|
+
meta :name=>"viewport", :content=>"width=1024, user-scalable=no"
|
42
|
+
meta :name => "description", :content=> @description if @description
|
43
|
+
meta :name => "author", :content=> @author if @author
|
44
|
+
|
45
|
+
# <!-- Core and extension CSS files -->
|
46
|
+
stylesheet "deck/core/deck.core.css"
|
47
|
+
|
48
|
+
stylesheet "deck/extensions/goto/deck.goto.css"
|
49
|
+
stylesheet "deck/extensions/menu/deck.menu.css"
|
50
|
+
stylesheet "deck/extensions/navigation/deck.navigation.css"
|
51
|
+
stylesheet "deck/extensions/status/deck.status.css"
|
52
|
+
stylesheet "deck/extensions/hash/deck.hash.css"
|
53
|
+
stylesheet "deck/extensions/scale/deck.scale.css"
|
54
|
+
|
55
|
+
stylesheet "deck/extensions/theme-picker/deck.theme-picker.css"
|
56
|
+
|
57
|
+
# <!-- Theme CSS files (menu swaps these out) -->
|
58
|
+
stylesheet "deck/themes/style/web-2.0.css", :id=>"style-theme-link"
|
59
|
+
stylesheet "deck/themes/transition/horizontal-slide.css", :id => "transition-theme-link"
|
60
|
+
|
61
|
+
script :src=>"deck/modernizr.custom.js"
|
62
|
+
end
|
63
|
+
|
64
|
+
def scripts
|
65
|
+
# comment 'Grab CDN jQuery, with a protocol relative URL; fall back to local if offline'
|
66
|
+
# script :src => '//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js'
|
67
|
+
script :src => './deck/jquery-1.7.min.js'
|
68
|
+
|
69
|
+
comment 'Deck Core and extensions'
|
70
|
+
script :type => "text/javascript", :src => 'deck/core/deck.core.js'
|
71
|
+
|
72
|
+
script :type => "text/javascript", :src => 'deck/extensions/hash/deck.hash.js'
|
73
|
+
script :type => "text/javascript", :src => 'deck/extensions/menu/deck.menu.js'
|
74
|
+
script :type => "text/javascript", :src => 'deck/extensions/goto/deck.goto.js'
|
75
|
+
script :type => "text/javascript", :src => 'deck/extensions/status/deck.status.js'
|
76
|
+
script :type => "text/javascript", :src => 'deck/extensions/navigation/deck.navigation.js'
|
77
|
+
script :type => "text/javascript", :src => 'deck/extensions/scale/deck.scale.js'
|
78
|
+
|
79
|
+
|
80
|
+
# fire up deck.js
|
81
|
+
script "$(function(){$.deck('.slide');});"
|
82
|
+
|
83
|
+
script :type => "text/javascript", :src => 'deck/extensions/theme-picker/deck.theme-picker.js'
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
def body_attributes
|
88
|
+
{:class=>"deck-container"}
|
89
|
+
end
|
90
|
+
|
91
|
+
def body_content
|
92
|
+
slides
|
93
|
+
slide_navigation
|
94
|
+
deck_status
|
95
|
+
goto_slide
|
96
|
+
permalink
|
97
|
+
scripts
|
98
|
+
end
|
99
|
+
|
100
|
+
def slide slide_id
|
101
|
+
# todo: use Slide object, but without markdown
|
102
|
+
# slide = Slide.new(:slide_id => slide_id)
|
103
|
+
section.slide :id => slide_id do
|
104
|
+
yield
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def slides
|
109
|
+
if @slides
|
110
|
+
@slides.each do |slide|
|
111
|
+
widget slide
|
112
|
+
end
|
113
|
+
else
|
114
|
+
default_slide
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def default_slide
|
119
|
+
slide 'readme' do
|
120
|
+
h2 "deck.rb"
|
121
|
+
ul {
|
122
|
+
li "based on deck.js"
|
123
|
+
li "create a subclass of Deck (see introduction.rb)"
|
124
|
+
li "run erector to build it"
|
125
|
+
}
|
126
|
+
pre "erector --to-html ./deck.rb # generates deck.html"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def slide_navigation
|
131
|
+
a :href => '#', :class => 'deck-prev-link', :title => 'Previous' do
|
132
|
+
character 8592
|
133
|
+
end
|
134
|
+
a :href => '#', :class => 'deck-next-link', :title => 'Next' do
|
135
|
+
character 8594
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def deck_status
|
140
|
+
p :class => 'deck-status' do
|
141
|
+
span :class => 'deck-status-current' do
|
142
|
+
end
|
143
|
+
text '/'
|
144
|
+
span :class => 'deck-status-total' do
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def goto_slide
|
150
|
+
form :action => '.', :method => 'get', :class => 'goto-form' do
|
151
|
+
label :for => 'goto-slide' do
|
152
|
+
text 'Go to slide:'
|
153
|
+
end
|
154
|
+
input :type => 'text', :name => 'slidenum', :id => 'goto-slide', :list => 'goto-datalist'
|
155
|
+
datalist :id => 'goto-datalist' do
|
156
|
+
end
|
157
|
+
input :type => 'submit', :value => 'Go'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def permalink
|
162
|
+
a "#", :href => '.', :title => 'Permalink to this slide', :class => 'deck-permalink'
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Fix Rack bug https://github.com/rack/rack/issues/301
|
2
|
+
module Rack
|
3
|
+
class Static
|
4
|
+
def initialize(app, options={})
|
5
|
+
@app = app
|
6
|
+
@urls = options[:urls] || ["/favicon.ico"]
|
7
|
+
@index = options[:index]
|
8
|
+
root = options[:root] || Dir.pwd
|
9
|
+
cache_control = options[:cache_control]
|
10
|
+
@file_server = Rack::File.new(root, cache_control)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/deck/slide.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# based on work by Alex and others in Showoff
|
2
|
+
require 'redcarpet'
|
3
|
+
|
4
|
+
|
5
|
+
module Deck
|
6
|
+
class Slide < Erector::Widget
|
7
|
+
|
8
|
+
# todo: test this method
|
9
|
+
def self.from_file markdown_file
|
10
|
+
split File.read(markdown_file)
|
11
|
+
end
|
12
|
+
|
13
|
+
# given a chunk of Markdown text, splits it into an array of Slide objects
|
14
|
+
def self.split content
|
15
|
+
unless content =~ /^\<?!SLIDE/m
|
16
|
+
content = content.gsub(/^# /m, "<!SLIDE>\n# ")
|
17
|
+
end
|
18
|
+
|
19
|
+
lines = content.split("\n")
|
20
|
+
slides = []
|
21
|
+
slides << (slide = Slide.new)
|
22
|
+
until lines.empty?
|
23
|
+
line = lines.shift
|
24
|
+
if line =~ /^<?!SLIDE(.*)>?/
|
25
|
+
slides << (slide = Slide.new(:classes => $1))
|
26
|
+
|
27
|
+
elsif line =~ /^# / and !slide.empty?
|
28
|
+
# every H1 defines a new slide, unless there's a !SLIDE before it
|
29
|
+
slides << (slide = Slide.new)
|
30
|
+
slide << line
|
31
|
+
|
32
|
+
else
|
33
|
+
slide << line
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
slides.delete_if {|slide| slide.empty? }
|
38
|
+
|
39
|
+
slides
|
40
|
+
end
|
41
|
+
|
42
|
+
####
|
43
|
+
|
44
|
+
attr_reader :classes, :markdown_text
|
45
|
+
|
46
|
+
needs :classes => nil, :markdown_text => nil, :slide_id => nil
|
47
|
+
|
48
|
+
|
49
|
+
def initialize options = {}
|
50
|
+
super options
|
51
|
+
|
52
|
+
@classes = process_classes
|
53
|
+
@markdown_text = ""
|
54
|
+
end
|
55
|
+
|
56
|
+
def process_classes
|
57
|
+
["slide"] + case @classes
|
58
|
+
when NilClass
|
59
|
+
[]
|
60
|
+
when String
|
61
|
+
@classes.strip.chomp('>').split
|
62
|
+
when Array
|
63
|
+
@classes
|
64
|
+
else
|
65
|
+
raise "can't deal with :classes => #{@classes.inspect}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def markdown
|
70
|
+
@@markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML,
|
71
|
+
:no_intra_emphasis => true,
|
72
|
+
:tables => true,
|
73
|
+
:fenced_code_blocks => true,
|
74
|
+
:no_intra_emphasis => true,
|
75
|
+
:autolink => true,
|
76
|
+
:strikethrough => true,
|
77
|
+
:lax_html_blocks => false,
|
78
|
+
:space_after_headers => true,
|
79
|
+
:superscript => false
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
def <<(s)
|
84
|
+
@markdown_text << s
|
85
|
+
@markdown_text << "\n"
|
86
|
+
end
|
87
|
+
|
88
|
+
def empty?
|
89
|
+
@markdown_text.strip == ""
|
90
|
+
end
|
91
|
+
|
92
|
+
def slide_id
|
93
|
+
@slide_id ||= begin
|
94
|
+
lines = @markdown_text.split("\n")
|
95
|
+
raise "an empty slide has no id" if lines.empty?
|
96
|
+
lines.first.gsub(/^#*/, '').strip.downcase.gsub(/[^\w\s]/, '').gsub(/\s/, '_')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def header_only?
|
101
|
+
markdown_text.strip =~ /^# / and markdown_text.strip.split("\n").size == 1
|
102
|
+
end
|
103
|
+
|
104
|
+
def massaged_markdown_text
|
105
|
+
unless header_only?
|
106
|
+
"##{markdown_text.strip}"
|
107
|
+
else
|
108
|
+
markdown_text
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def content
|
113
|
+
section :class => @classes, :id => slide_id do
|
114
|
+
text "\n" # markdown HTML should be left-aligned, in case of PRE blocks and other quirks
|
115
|
+
html = markdown.render(massaged_markdown_text)
|
116
|
+
rawtext html
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/deck/version.rb
ADDED
data/spec/deck_spec.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
here = File.expand_path File.dirname(__FILE__)
|
2
|
+
require "#{here}/spec_helper"
|
3
|
+
|
4
|
+
require "deck/deck"
|
5
|
+
|
6
|
+
module Deck
|
7
|
+
describe Deck do
|
8
|
+
def temp_dir options = {:remove => true}
|
9
|
+
@temp_dir ||= begin
|
10
|
+
require 'tmpdir'
|
11
|
+
require 'fileutils'
|
12
|
+
called_from = File.basename caller.first.split(':').first, ".rb"
|
13
|
+
path = File.join(Dir::tmpdir, "#{called_from}_#{Time.now.to_i}_#{rand(1000)}")
|
14
|
+
Dir.mkdir(path)
|
15
|
+
at_exit {FileUtils.rm_rf(path) if File.exists?(path)} if options[:remove]
|
16
|
+
path
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_html nokogiri_node
|
21
|
+
nokogiri_node.serialize(:save_with => 0).chomp
|
22
|
+
end
|
23
|
+
|
24
|
+
def as_html_page snippet
|
25
|
+
snippet =~ /<html/ ? snippet : "<html>#{snippet}</html>"
|
26
|
+
end
|
27
|
+
|
28
|
+
def deck_widget options = {}
|
29
|
+
@deck_widget ||= Deck.new options
|
30
|
+
end
|
31
|
+
|
32
|
+
def doc
|
33
|
+
@doc ||= begin
|
34
|
+
@html = deck_widget.to_html
|
35
|
+
Nokogiri.parse(as_html_page(@html))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def write name, contents = "contents of #{name}"
|
40
|
+
path = "#{temp_dir}/#{name}"
|
41
|
+
File.open(path, "w") do |f|
|
42
|
+
f.write contents
|
43
|
+
end
|
44
|
+
File.new path
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#write" do
|
48
|
+
before do
|
49
|
+
@hello = write "hello.txt"
|
50
|
+
@goodbye = write "goodbye.txt", "farewell"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "uses temp_dir" do
|
54
|
+
File.dirname(@hello).should == temp_dir
|
55
|
+
File.dirname(@goodbye).should == temp_dir
|
56
|
+
end
|
57
|
+
|
58
|
+
it "writes a default value" do
|
59
|
+
File.read(@hello).should == "contents of hello.txt"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "writes a given value" do
|
63
|
+
# since write returns a File instance, we can call read on it
|
64
|
+
@goodbye.read.should == "farewell"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "writes a file" do
|
68
|
+
file = write("hello.md", "# hello")
|
69
|
+
assert { File.read(file) == "# hello" }
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
it "renders a basic deck.js HTML page" do
|
76
|
+
assert { doc }
|
77
|
+
assert { @html.include? '<link href="deck/core/deck.core.css" rel="stylesheet" />' }
|
78
|
+
end
|
79
|
+
|
80
|
+
it "contains a single dummy slide" do
|
81
|
+
assert { doc.css('section.slide').size == 1 }
|
82
|
+
end
|
83
|
+
|
84
|
+
it "renders a markdown file with one slide" do
|
85
|
+
file = write("hello.md", "# hello")
|
86
|
+
|
87
|
+
deck_widget :slides => Slide.split(File.read file)
|
88
|
+
assert { doc.css('section.slide').size == 1 }
|
89
|
+
slide = doc.css('section.slide').first
|
90
|
+
assert { slide["id"] == "hello" }
|
91
|
+
assert { to_html(slide) == "<section class=\"slide\" id=\"hello\">" +
|
92
|
+
"<h1>hello</h1>\n" +
|
93
|
+
"</section>"
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Jasmine
|
2
|
+
class Config
|
3
|
+
|
4
|
+
# Add your overrides or custom config code here
|
5
|
+
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
# Note - this is necessary for rspec2, which has removed the backtrace
|
11
|
+
module Jasmine
|
12
|
+
class SpecBuilder
|
13
|
+
def declare_spec(parent, spec)
|
14
|
+
me = self
|
15
|
+
example_name = spec["name"]
|
16
|
+
@spec_ids << spec["id"]
|
17
|
+
backtrace = @example_locations[parent.description + " " + example_name]
|
18
|
+
parent.it example_name, {} do
|
19
|
+
me.report_spec(spec["id"])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|