eggplant 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +66 -0
  4. data/README.md +30 -0
  5. data/Rakefile +8 -0
  6. data/bin/eggplant +5 -0
  7. data/eggplant.gemspec +34 -0
  8. data/lib/eggplant.rb +8 -0
  9. data/lib/eggplant/assets/css/eggplant/main.css +77 -0
  10. data/lib/eggplant/assets/css/vendor/deck.core.css +393 -0
  11. data/lib/eggplant/assets/css/vendor/extensions/deck.codemirror.css +89 -0
  12. data/lib/eggplant/assets/css/vendor/extensions/deck.goto.css +41 -0
  13. data/lib/eggplant/assets/css/vendor/extensions/deck.hash.css +13 -0
  14. data/lib/eggplant/assets/css/vendor/extensions/deck.menu.css +24 -0
  15. data/lib/eggplant/assets/css/vendor/extensions/deck.navigation.css +43 -0
  16. data/lib/eggplant/assets/css/vendor/extensions/deck.scale.css +16 -0
  17. data/lib/eggplant/assets/css/vendor/extensions/deck.status.css +14 -0
  18. data/lib/eggplant/assets/css/vendor/extensions/deck.subslides.css +9 -0
  19. data/lib/eggplant/assets/css/vendor/themes/neon.css +114 -0
  20. data/lib/eggplant/assets/css/vendor/themes/swiss.css +75 -0
  21. data/lib/eggplant/assets/css/vendor/themes/web-2.0.css +187 -0
  22. data/lib/eggplant/assets/css/vendor/transitions/fade.css +43 -0
  23. data/lib/eggplant/assets/css/vendor/transitions/horizontal-slide.css +79 -0
  24. data/lib/eggplant/assets/css/vendor/transitions/vertical-slide.css +97 -0
  25. data/lib/eggplant/assets/js/eggplant/main.js +38 -0
  26. data/lib/eggplant/assets/js/eggplant/remote.js +0 -0
  27. data/lib/eggplant/assets/js/vendor/coffee-script.js +8 -0
  28. data/lib/eggplant/assets/js/vendor/deck.core.js +457 -0
  29. data/lib/eggplant/assets/js/vendor/extensions/deck.goto.js +118 -0
  30. data/lib/eggplant/assets/js/vendor/extensions/deck.hash.js +113 -0
  31. data/lib/eggplant/assets/js/vendor/extensions/deck.menu.js +127 -0
  32. data/lib/eggplant/assets/js/vendor/extensions/deck.navigation.js +83 -0
  33. data/lib/eggplant/assets/js/vendor/extensions/deck.scale.js +155 -0
  34. data/lib/eggplant/assets/js/vendor/extensions/deck.status.js +42 -0
  35. data/lib/eggplant/assets/js/vendor/extensions/deck.subslides.js +50 -0
  36. data/lib/eggplant/assets/js/vendor/jquery.js +9046 -0
  37. data/lib/eggplant/assets/js/vendor/less.js +2769 -0
  38. data/lib/eggplant/assets/js/vendor/modernizr.js +1116 -0
  39. data/lib/eggplant/cli.rb +113 -0
  40. data/lib/eggplant/markdown.rb +76 -0
  41. data/lib/eggplant/server.rb +168 -0
  42. data/lib/eggplant/slides.rb +65 -0
  43. data/lib/eggplant/version.rb +3 -0
  44. data/lib/eggplant/views/remote.slim +17 -0
  45. data/lib/eggplant/views/show.slim +47 -0
  46. metadata +281 -0
@@ -0,0 +1,113 @@
1
+ require 'thor'
2
+ require 'eggplant/server'
3
+ require 'em-websocket'
4
+ require 'thin'
5
+ require 'json'
6
+
7
+ module Eggplant
8
+
9
+ class Channel < EventMachine::Channel
10
+ # Add items to the channel, which are pushed out to all subscribers except `name`
11
+ def push_except(name, *items)
12
+ items = items.dup
13
+ EM.schedule do
14
+ items.each do |i|
15
+ @subs.each do |key, value|
16
+ if key != name
17
+ value.call i
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ class CLI < Thor
26
+ include Thor::Actions
27
+
28
+ desc "new", "Create new presentation"
29
+ def new path='.'
30
+ path = File.expand_path path
31
+ puts "Created new presentation in #{path}"
32
+ end
33
+
34
+ desc "build", "Build presentation"
35
+ method_option :ui, :type => :array, :default => ['goto', 'status', 'nav']
36
+ def build html_file='./index.html'
37
+ path = File.expand_path '.'
38
+ html_file = File.expand_path html_file
39
+ puts "Build presentation in #{path}"
40
+ Eggplant::Server.configure path, options[:ui]
41
+ File.unlink html_file if File.exists?(html_file)
42
+ Eggplant::Server.build_html html_file
43
+ end
44
+
45
+ desc "show", "Run presentation server"
46
+ method_option :port, :type => :numeric, :aliases => '-p', :default => 4242
47
+ method_option :remote_port, :type => :numeric, :aliases => '-r', :default => 4444
48
+ method_option :mode, :type => :string, :aliases => '-m', :default => 'show'
49
+ method_option :ui, :type => :array, :default => ['goto']
50
+ def show
51
+ path = File.expand_path('.')
52
+ puts "Serving presentation from #{path}"
53
+
54
+ EventMachine.run do
55
+ # Server options
56
+ server_options = {
57
+ :port => options[:port],
58
+ :presentation_root => path,
59
+ :ui => options[:ui]
60
+ }
61
+ if options[:mode] == 'show'
62
+ server_options[:environment] = :production
63
+ end
64
+
65
+ # Eggplant::Server
66
+ Eggplant::Server.run! server_options
67
+
68
+ # Eggplant::WebSocket
69
+ @channel = Eggplant::Channel.new
70
+ @count = 0
71
+ @current_slide = 0
72
+
73
+ EventMachine::WebSocket.start(:host => '0.0.0.0', :port => options[:remote_port]) do |ws|
74
+ ws.onopen do
75
+ sid = @channel.subscribe do |msg|
76
+ ws.send msg
77
+ end
78
+ @count += 1
79
+ @channel.push({:type => 'join', :total_clients => @count, :current_slide => @current_slide}.to_json)
80
+
81
+ ws.onmessage do |msg|
82
+ message = JSON.parse msg
83
+ if message['type'] == 'goto'
84
+ @current_slide = message['idx']
85
+ end
86
+ @channel.push_except sid, "#{msg}"
87
+ end
88
+
89
+ ws.onclose do
90
+ @channel.unsubscribe sid
91
+ @count -= 1
92
+ @channel.push({:type => 'leave', :total_clients => @count}.to_json)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ desc "heroku", "Prepare for serving from heroku"
100
+ method_option :ui, :type => :array, :default => ['goto', 'status', 'nav']
101
+ def heroku
102
+ path = File.expand_path '.'
103
+ config_path = File.join path, 'config.ru'
104
+ gemfile_path = File.join path, 'Gemfile'
105
+ File.unlink(config_path) if File.exists?(config_path)
106
+ File.open(config_path, 'w') do |f|
107
+ f << "require 'eggplant/server'\n"
108
+ f << "Eggplant::Server.configure File.expand_path('.'), #{options[:ui].inspect}\n"
109
+ f << "run Eggplant::Server\n"
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,76 @@
1
+ #require 'cgi'
2
+ require 'albino'
3
+
4
+ module Eggplant
5
+ class Markdown
6
+ def initialize(markdown)
7
+ @markdown = markdown
8
+ end
9
+
10
+ def to_html
11
+ highlight!
12
+ #CGI.unescapeHTML
13
+ doc.search('body > *').to_html
14
+ end
15
+
16
+ private
17
+
18
+ def doc
19
+ @doc ||= Nokogiri::HTML(markup)
20
+ end
21
+
22
+ def highlight!
23
+ doc.search('div.code').each do |div|
24
+ lexer = div['rel'] || :javascript
25
+
26
+ lexted_text = Albino.new(div.text, lexer).to_s
27
+
28
+ highlighted = Nokogiri::HTML(lexted_text).at('div')
29
+
30
+ klasses = highlighted['class'].split(/\s+/)
31
+ klasses << lexer
32
+ klasses << 'code'
33
+ klasses << 'highlight'
34
+ highlighted['class'] = klasses.join(' ')
35
+
36
+ div.replace(highlighted)
37
+ end
38
+ end
39
+
40
+ # def markup
41
+ # @markup ||= begin
42
+ # t = RDiscount.new(@markdown.dup).to_html
43
+ # t.gsub!(/^(?:<p>)?@@@(?:<\/p>)?$/, '</div>')
44
+ # t.gsub!(/^(?:<p>)?@@@\s*([\w\+]+)(?:<\/p>)?$/, '<div class="code" rel="\1">')
45
+ # t
46
+ # end
47
+ # end
48
+
49
+ # def highlight!
50
+ # doc.search('code[data-mode]').each do |code|
51
+ # if ['javascript', 'coffeescript'].include?(code['data-mode'])
52
+ # code['data-exec'] = 'true'
53
+ # end
54
+ # if code['data-mode'] == 'html'
55
+ # code['data-mode'] = 'htmlmixed'
56
+ # end
57
+ # end
58
+ # end
59
+
60
+ def markup
61
+ @markup ||= begin
62
+ contents = []
63
+ markdown = @markdown.dup.gsub(/^@@@$/, '~@@@').gsub(/^\s*@@@\s*([\w\+]+)?$([^~]*)\n~@{3}$/) do |content|
64
+ i = contents.size
65
+ contents << $2 + "\n"
66
+ '<div class="code" rel="' + $1 + '">%@' + i.to_s + '@%</div>'
67
+ end
68
+ t = RDiscount.new(markdown).to_html
69
+ contents.size.times do |i|
70
+ t.gsub!("%@#{i}@%", contents[i])
71
+ end
72
+ t.gsub(/<p>\.(.*?)[\s\n]/, '<p class="\1">')
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,168 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require 'sinatra/base'
5
+ require 'slim'
6
+
7
+ require 'sinatra/assetpack'
8
+
9
+ require 'eggplant'
10
+
11
+ module Sinatra::AssetPack
12
+ class NoneEngine < Engine
13
+ def js(str, options={})
14
+ str
15
+ end
16
+ end
17
+
18
+ Compressor.register :js, :none, NoneEngine
19
+ end
20
+
21
+ module Eggplant
22
+ class Server < Sinatra::Base
23
+
24
+ set :root, File.dirname(__FILE__)
25
+
26
+ register Sinatra::AssetPack
27
+
28
+ assets do
29
+ serve '/js', from: 'assets/js'
30
+ serve '/css', from: 'assets/css'
31
+
32
+ js :eggplant, '/js/eggplant.js',
33
+ [
34
+ '/js/vendor/modernizr.js',
35
+ '/js/vendor/jquery.js',
36
+ # Deck
37
+ '/js/vendor/deck.core.js',
38
+ '/js/vendor/extensions/*.js',
39
+ '/js/eggplant/main.js'
40
+ ]
41
+
42
+ js :eggplant_processors, '/js/eggplant_processors.js',
43
+ [
44
+ #'/js/vendor/coffee-script.js',
45
+ '/js/vendor/less.js'
46
+ ]
47
+
48
+ js :eggplant_remote, '/js/eggplant_remote.js',
49
+ [
50
+ '/js/vendor/jquery.js',
51
+ '/js/eggplant/remote.js'
52
+ ]
53
+
54
+ css :eggplant, '/css/eggplant.css',
55
+ [
56
+ '/css/vendor/deck.core.css',
57
+ '/css/vendor/transitions/horizontal-slide.css',
58
+ '/css/vendor/extensions/*.css',
59
+ '/css/eggplant/main.css'
60
+ ]
61
+
62
+ js_compression :none, :no_fallback => true
63
+ end
64
+
65
+ get '/' do
66
+ slim :show
67
+ end
68
+
69
+ get '/remote' do
70
+ slim :remote
71
+ end
72
+
73
+ get '/images/:filename' do |filename|
74
+ path = File.join(settings.presentation_root, 'images', filename)
75
+ send_file_if_exists path
76
+ end
77
+
78
+ get '/assets/:filename' do |filename|
79
+ path = File.join(settings.presentation_root, 'assets', filename)
80
+ send_file_if_exists path
81
+ end
82
+
83
+ get '/slides.:format' do |format|
84
+ path = asset_path(format)
85
+ send_file_if_exists path
86
+ end
87
+
88
+ get '/download.:format' do |format|
89
+
90
+ end
91
+
92
+ def index
93
+ slim :show
94
+ end
95
+
96
+ def self.configure path, ui=['goto']
97
+ set :environment, :production
98
+ set :presentation_root, path
99
+ set :ui, ui
100
+ end
101
+
102
+ def self.build_html path
103
+ showtime = Eggplant::Server.new
104
+ while !showtime.is_a?(Eggplant::Server)
105
+ showtime = showtime.instance_variable_get(:@app)
106
+ end
107
+ html = showtime.send('index')
108
+ File.open(path, 'w') do |f|
109
+ f << html
110
+ end
111
+ end
112
+
113
+ helpers do
114
+ def render_slides
115
+ slides.to_html
116
+ end
117
+
118
+ def slides
119
+ @slides ||= Eggplant::Slides.new settings.presentation_root
120
+ end
121
+
122
+ def send_file_if_exists path
123
+ if File.exists?(path)
124
+ send_file path
125
+ else
126
+ not_found
127
+ end
128
+ end
129
+
130
+ def asset_path format
131
+ File.join(settings.presentation_root, "slides.#{format}")
132
+ end
133
+
134
+ def remote_enabled?
135
+ false
136
+ end
137
+
138
+ def has_js?
139
+ File.exists?(asset_path('js'))
140
+ end
141
+
142
+ def has_coffee?
143
+ File.exists?(asset_path('coffee'))
144
+ end
145
+
146
+ def has_css?
147
+ File.exists?(asset_path('css'))
148
+ end
149
+
150
+ def has_less?
151
+ File.exists?(asset_path('less'))
152
+ end
153
+
154
+ def show_status?
155
+ settings.ui.include?('status')
156
+ end
157
+
158
+ def show_goto?
159
+ settings.ui.include?('goto')
160
+ end
161
+
162
+ def show_navigation?
163
+ settings.ui.include?('nav')
164
+ end
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,65 @@
1
+ module Eggplant
2
+ class Slides
3
+
4
+ def initialize path, options={}
5
+ @slides = []
6
+ @title = nil
7
+ Dir[path+'/**/*.md'].each do |file|
8
+ if !(file =~ /README.md$/)
9
+ process file, File.read(file)
10
+ end
11
+ end
12
+ end
13
+
14
+ def size
15
+ @slides.size
16
+ end
17
+
18
+ def to_html
19
+ @slides.map do |slide|
20
+ render_slide slide[:body], slide[:options]
21
+ end.join "\n"
22
+ end
23
+
24
+ private
25
+
26
+ def process filename, content
27
+ # if there are no !SLIDE markers, then make the file define one slide
28
+ unless content =~ /^!SLIDE/m
29
+ content = "!SLIDE\n" + content
30
+ end
31
+
32
+ title = filename.split('/').pop.gsub(/\.md$/, '')
33
+ slides = content.split(/^!SLIDE/)
34
+ slides.delete('')
35
+ seq = 1 if slides.size > 1
36
+ slides.each do |slide|
37
+ slide, options = parse_options(title, slide.split("\n"))
38
+ @slides << {:body => slide, :options => options}
39
+ end
40
+ end
41
+
42
+ def render_slide body, options
43
+ body = Eggplant::Markdown.new(body).to_html
44
+ <<-HTML
45
+ <div class="slide #{options[:class]}"
46
+ data-title="#{options[:title]}">
47
+ #{body}
48
+ </div>
49
+ HTML
50
+ end
51
+
52
+ def parse_options title, lines
53
+ options = lines.shift.strip.scan(/[a-z]*="[^"]*"/i) rescue []
54
+ options_hash = {}
55
+ options.each do |option|
56
+ key, value = option.split('=')
57
+ options_hash[key.to_sym] = value.gsub('"', '')
58
+ end
59
+ options_hash[:title] ||= title
60
+ options_hash[:class] ||= ''
61
+ return lines.join("\n"), options_hash
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,3 @@
1
+ module Eggplant
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,17 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title ShowThem Remote
5
+ meta name="socket-port" content="4343"
6
+ == js :showtime_remote
7
+ == css :showtime_remote
8
+ body
9
+ nav
10
+ button#beginning
11
+ button#previous
12
+ button#next
13
+ #timer
14
+ time
15
+ .controls
16
+ button#start
17
+ button#reset