gibier 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/bin/console +14 -0
  3. data/bin/gibier +5 -0
  4. data/bin/setup +8 -0
  5. data/lib/gibier.rb +8 -0
  6. data/lib/gibier/cli.rb +95 -0
  7. data/lib/gibier/render.rb +234 -0
  8. data/lib/gibier/slide_helper.rb +25 -0
  9. data/lib/gibier/slide_loader.rb +77 -0
  10. data/lib/gibier/version.rb +3 -0
  11. data/template/app.rb +25 -0
  12. data/template/gh_pages/index.haml +13 -0
  13. data/template/gh_pages/slide.haml +18 -0
  14. data/template/project/Gemfile +8 -0
  15. data/template/project/app/page_base.rb +46 -0
  16. data/template/project/app/slide.rb +204 -0
  17. data/template/project/app/track_field.rb +43 -0
  18. data/template/project/assets/css/application.css +219 -0
  19. data/template/project/assets/css/highlight.css +101 -0
  20. data/template/project/assets/fonts/Inconsolata-Regular.ttf +0 -0
  21. data/template/project/assets/images/flag.png +0 -0
  22. data/template/project/assets/images/hunter.png +0 -0
  23. data/template/project/assets/images/rabbit.png +0 -0
  24. data/template/project/assets/images/turtle.png +0 -0
  25. data/template/project/assets/images/usagitokame.png +0 -0
  26. data/template/project/config.ru +35 -0
  27. data/template/project/data/sample/css/custom.css +0 -0
  28. data/template/project/data/sample/images/gibier.jpg +0 -0
  29. data/template/project/data/sample/images/youchan.jpg +0 -0
  30. data/template/project/data/sample/script.md +7 -0
  31. data/template/project/data/sample/slide.md +59 -0
  32. data/template/project/server.rb +76 -0
  33. data/template/project/views/index.haml +13 -0
  34. data/template/project/views/script.haml +10 -0
  35. data/template/project/views/slide.haml +15 -0
  36. data/template/script.rb +24 -0
  37. metadata +320 -0
@@ -0,0 +1,3 @@
1
+ module Gibier
2
+ VERSION = "0.8.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ require 'slide'
2
+ require 'page_base'
3
+ require 'slides/#{name}/pages'
4
+ require 'browser/socket'
5
+ require 'browser/location'
6
+
7
+ Gibier.slide_name = '#{name}'
8
+ Gibier.gh_pages = `window.ghPages`
9
+ Gibier.assets_path = `window.assetsPath`
10
+
11
+ module App
12
+ def self.render(ws)
13
+ Hyalite.render(Hyalite.create_element(Gibier::Slide, {ws:ws}), $document['.gibier'])
14
+ end
15
+ end
16
+
17
+ $document.ready do
18
+ ws = Browser::Socket.new("ws://\#{$window.location.host}/push_notification/start/slide/#{name}") unless Gibier.gh_pages
19
+
20
+ $window.on(:resize) do
21
+ App.render(ws)
22
+ end
23
+
24
+ App.render(ws)
25
+ end
@@ -0,0 +1,13 @@
1
+ !!! 5
2
+ %html(lang="en" data-framework="hyalite")
3
+ %head
4
+ %meta{charset:"utf-8"}
5
+ %title Index of slides
6
+
7
+ %body
8
+ %h1 Index of slides
9
+ %section
10
+ %ul
11
+ - @slides.each do |slide|
12
+ %li
13
+ %a{href: slide[:name]}= "[#{slide[:name]}] \"#{slide[:title]}\""
@@ -0,0 +1,18 @@
1
+ !!!
2
+ %html(lang="en" data-framework="hyalite")
3
+ %head
4
+ %meta{charset:"utf-8"}
5
+ %title= @slide_name
6
+ %link{rel:"stylesheet", href:"../assets/css/application.css"}
7
+ %link{rel:"stylesheet", href:"../assets/#{@slide_name}/css/custom.css"}
8
+ %link{rel:"stylesheet", href:"../assets/css/highlight.css"}
9
+
10
+ %body
11
+ %section.gibier
12
+
13
+ :javascript
14
+ window.slideName = '#{@slide_name}'
15
+ window.ghPages = true;
16
+ window.assetsPath = '../assets/#{@slide_name}';
17
+
18
+ %script{type:"text/javascript", src:"../assets/#{@slide_name}/javascripts/application.js"}
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'gibier'
4
+ gem 'opal-browser'
5
+ gem 'opal-router', github: 'adambeynon/opal-router'
6
+ gem 'opal-haml', github: 'opal/opal-haml'
7
+ gem 'hyalite'
8
+
@@ -0,0 +1,46 @@
1
+ module Gibier
2
+ class PageBase
3
+ include Hyalite::Component
4
+ include Hyalite::Component::ShortHand
5
+
6
+ def initialize
7
+ @height = 0
8
+ end
9
+
10
+ # def component_did_mount
11
+ # every(0.05) do
12
+ # if @props[:visible]
13
+ # prev_height = @height
14
+ # el = $document.css(".#{page_class_name}")[0]
15
+ # if el
16
+ # height = el.height.to_i
17
+ # unless (height - prev_height).abs < 3
18
+ # @height = height
19
+ # set_state(top: "#{((700 - @height)/2).to_i}px")
20
+ # end
21
+ # end
22
+ # end
23
+ # end
24
+ # end
25
+
26
+ def page_class_name
27
+ "page_#{@props[:page_number]}"
28
+ end
29
+
30
+ def style
31
+ {top: @state[:top]}.merge(@props[:visible] ? {display: 'block'} : {display: 'none'})
32
+ end
33
+
34
+ def header
35
+ nil
36
+ end
37
+
38
+ def content
39
+ nil
40
+ end
41
+
42
+ def render
43
+ section({className: "page #{page_class_name}", style: style}, header, content)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,204 @@
1
+ require 'hyalite'
2
+ require 'opal-router'
3
+ require 'browser/interval'
4
+ require 'browser/location'
5
+ require 'track_field'
6
+
7
+ module Gibier
8
+ SLIDE_WIDTH = 960
9
+ SLIDE_HEIGHT = 720
10
+
11
+ def self.page_count
12
+ @page_count
13
+ end
14
+
15
+ def self.page_count=(count)
16
+ @page_count = count
17
+ end
18
+
19
+ def self.title
20
+ @title
21
+ end
22
+
23
+ def self.title=(title)
24
+ @title = title
25
+ end
26
+
27
+ def self.slide_name
28
+ @slide_name
29
+ end
30
+
31
+ def self.slide_name=(name)
32
+ @slide_name = name
33
+ end
34
+
35
+ def self.gh_pages
36
+ `window.ghPages`
37
+ end
38
+
39
+ def self.assets_path
40
+ `window.assetsPath`
41
+ end
42
+
43
+ def self.assets_path=(path)
44
+ @assets_path = path
45
+ end
46
+
47
+ class Slide
48
+ include Hyalite::Component
49
+ include Hyalite::Component::ShortHand
50
+
51
+ def pages(height)
52
+ case @state[:mode]
53
+ when :slide
54
+ Gibier.page_count.times.map do |i|
55
+ Object.const_get("Gibier::Page#{i}").el({visible: @state[:page_number] == i, page_number: i, slide_height: height})
56
+ end
57
+ when :print
58
+ Gibier.page_count.times.map do |i|
59
+ Object.const_get("Gibier::Page#{i}").el({visible: true, page_number: i, slide_height: height})
60
+ end
61
+ end
62
+ end
63
+
64
+ def initial_state
65
+ page_num = $window.location.uri.sub(/.*#([0-9]+)/, '\1').to_i
66
+
67
+ {
68
+ page_number: page_num,
69
+ mode: :slide,
70
+ start: nil,
71
+ footer_visible: false
72
+ }
73
+ end
74
+
75
+ def component_did_mount
76
+ $window.on(:keydown) do |evt|
77
+ handle_key_down(evt)
78
+ end
79
+
80
+ unless Gibier.gh_pages
81
+ @props[:ws].on(:message) do |msg|
82
+ (event, value) = msg.data.split(':')
83
+ case event
84
+ when 'keydown'
85
+ handle_key_down(value.to_i)
86
+ end
87
+ end
88
+ end
89
+
90
+ router = Router.new
91
+ router.route('/') { page_to(num) }
92
+ router.route(':page') {|params| set_state(page_number: params[:page].to_i) }
93
+ end
94
+
95
+ def page_to(num)
96
+ if Gibier.gh_pages
97
+ $window.location.assign("./##{num}")
98
+ else
99
+ $window.location.assign("/#{Gibier.slide_name}##{num}")
100
+ end
101
+ end
102
+
103
+ def handle_key_down(event)
104
+ keycode = event.code
105
+ case keycode
106
+ when 39,34
107
+ page_to(@state[:page_number] + 1) if @state[:page_number] < Gibier.page_count
108
+ when 37,33
109
+ page_to(@state[:page_number] - 1) if @state[:page_number] > 0
110
+ when 83,66
111
+ unless @state[:start]
112
+ set_state(start: Time.now)
113
+ else
114
+ set_state(start: nil)
115
+ end
116
+ when 80
117
+ if @state[:mode] == :slide
118
+ set_state(mode: :print)
119
+ else
120
+ set_state(mode: :slide)
121
+ end
122
+ when 70
123
+ set_state(footer_visible: !@state[:footer_visible])
124
+ if `event.native.ctrlKey`
125
+ fullscreen
126
+ end
127
+ when 116
128
+ if `event.native.shiftKey`
129
+ fullscreen
130
+ end
131
+ else
132
+ puts "keycode = #{keycode}"
133
+ end
134
+ end
135
+
136
+ def fullscreen
137
+ %x(
138
+ var element = document.getElementsByClassName('gibier')[0];
139
+ var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen;
140
+
141
+ if (requestMethod) {
142
+ requestMethod.call(element);
143
+ }
144
+ )
145
+ end
146
+
147
+ def duration
148
+ text = "10"
149
+ %x(
150
+ var el = document.getElementsByClassName('duration')[0];
151
+ if (el) {
152
+ text = el.innerText;
153
+ }
154
+ )
155
+
156
+ text.to_i * 60
157
+ end
158
+
159
+ def render
160
+ follow_height = $window.view.height / $window.view.width < SLIDE_HEIGHT / SLIDE_WIDTH
161
+ if follow_height
162
+ zoom = $window.view.height.to_f / SLIDE_HEIGHT * 0.98
163
+ else
164
+ zoom = $window.view.width.to_f / SLIDE_WIDTH * 0.98
165
+ end
166
+
167
+ top = ($window.view.height / zoom - SLIDE_HEIGHT) / 2
168
+ left = ($window.view.width / zoom - SLIDE_WIDTH) / 2
169
+
170
+ footer_style = @state[:page_number] == 1 || !@state[:footer_visible] ? {style: {display: 'none'}} : {}
171
+
172
+ case @state[:mode]
173
+ when :slide
174
+ div({className: 'background'},
175
+ div({
176
+ className: 'slide',
177
+ style: {zoom: zoom, top: "#{top}px", left: "#{left}px"},
178
+ onKeyDown: -> (event) { handle_key_down(event) }
179
+ },
180
+ pages(SLIDE_HEIGHT * zoom)
181
+ ),
182
+ Gibier::TrackField.el({total_time: duration, start: @state[:start], page_number: @state[:page_number], page_count: Gibier.page_count}),
183
+ section({className: 'footer'}.merge(footer_style),
184
+ p({className: 'title'}, Gibier.title),
185
+ p({className: 'powered-by'}, "Powered by ", span({className: "hyalite"}, "Hyalite"))
186
+ )
187
+ )
188
+ when :print
189
+ div({
190
+ className: 'print',
191
+ onKeyDown: -> (evt) { handle_key_down(evt) } },
192
+ pages(SLIDE_HEIGHT * zoom).map do |page|
193
+ top += SLIDE_HEIGHT * zoom
194
+ div({
195
+ className: 'wrap-page',
196
+ style: {zoom: zoom, top: "#{top}px", left: "#{left}px"},
197
+ }, page)
198
+ end
199
+ )
200
+ end
201
+ end
202
+ end
203
+ end
204
+
@@ -0,0 +1,43 @@
1
+ require 'browser/interval'
2
+
3
+ module Gibier
4
+ class TrackField
5
+ include Hyalite::Component
6
+ include Hyalite::Component::ShortHand
7
+
8
+ def initialize
9
+ super
10
+ @width = 0
11
+ end
12
+
13
+ def initial_state
14
+ { tic: false }
15
+ end
16
+
17
+ def component_did_mount
18
+ every(1) do
19
+ set_state(tic: !@state[:tic])
20
+ if el = $document.css(".track-field")[0]
21
+ @width = el.width.to_i
22
+ spend_time = @props[:start] ? Time.now - @props[:start] : 0
23
+ rabbit_pos = [(spend_time / @props[:total_time]), 1].min * (@width - 40) * 0.96 + @width * 0.02
24
+ set_state(rabbit_pos: rabbit_pos)
25
+ end
26
+ end
27
+ end
28
+
29
+ def render
30
+ turtle_pos = (@props[:page_number] / @props[:page_count]) * (@width - 40) * 0.96 + @width * 0.02
31
+ mstyle = {transform: "rotate(#{@state[:tic]?'-':''}7deg)", left: "#{@state[:rabbit_pos]}px"}
32
+ ystyle = {transform: "rotate(#{@state[:tic]?'':'-'}7deg)"}
33
+ ystyle.merge!(left: "#{turtle_pos}px") if @width > 0
34
+
35
+ div({className: 'track-field'},
36
+ @props[:start] ? [
37
+ div({className: 'flag', style: {right: "#{@width * 0.02}px"}}),
38
+ div({className: 'rabbit avatar', style: mstyle}),
39
+ div({className: 'turtle avatar', style: ystyle})
40
+ ] : nil)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,219 @@
1
+ html {
2
+ height: 100%;
3
+ }
4
+
5
+ body {
6
+ height: 100%;
7
+ margin: 0;
8
+ }
9
+
10
+ ::selection {
11
+ background: #770;
12
+ }
13
+
14
+ .gibier {
15
+ position: fixed;
16
+ top: 0;
17
+ width: 100%;
18
+ height: 100%;
19
+ font-size: 32px;
20
+ }
21
+
22
+ .gibier .background {
23
+ height: 100%;
24
+ background-color: #222;
25
+ color: #fff;
26
+ }
27
+
28
+ .slide {
29
+ position: absolute;
30
+ width: 960px;
31
+ height: 720px;
32
+ margin: 0 auto;
33
+ }
34
+
35
+ .wrap-page {
36
+ width: 960px;
37
+ height: 720px;
38
+ margin: 0 auto;
39
+ color: #000;
40
+ background-color: #fff;
41
+ margin-bottom: 60px;
42
+ page-break-after: always;
43
+ }
44
+
45
+ .print {
46
+ background: #fff;
47
+ background-color: #fff;
48
+ color: #000;
49
+ }
50
+
51
+ .slide .page {
52
+ top: 50%;
53
+ transform: translateY(-50%);
54
+ position: absolute;
55
+ width: 100%;
56
+ }
57
+
58
+ .page p.icon {
59
+ margin: 0;
60
+ position: absolute;
61
+ top: 5.7em;
62
+ left: 25em;
63
+ }
64
+
65
+ .page p.icon img {
66
+ width: 3em;
67
+ }
68
+
69
+ .page code {
70
+ font-size: 0.8em;
71
+ }
72
+
73
+
74
+ .page h3 img {
75
+ width: 1.2em;
76
+ background-size: contain;
77
+ background-repeat: no-repeat;
78
+ }
79
+
80
+ .page h4 img {
81
+ width: 80%;
82
+ margin: 0 auto;
83
+ display: block;
84
+ background-size: contain;
85
+ background-repeat: no-repeat;
86
+ }
87
+
88
+ .page img {
89
+ width: 1em;
90
+ background-size: contain;
91
+ background-repeat: no-repeat;
92
+ }
93
+
94
+ .page p.large {
95
+ text-align: center;
96
+ }
97
+
98
+ .page p.large img {
99
+ width: 80%;
100
+ }
101
+
102
+ h1 {
103
+ font-size: 1.6em;
104
+ text-align: center;
105
+ }
106
+
107
+ h2 {
108
+ font-size: 1.4em;
109
+ text-align: center;
110
+ }
111
+
112
+ h3 {
113
+ font-size: 1.2em;
114
+ text-align: center;
115
+ }
116
+
117
+ h4 {
118
+ font-size: 1.0em;
119
+ text-align: center;
120
+ }
121
+
122
+ .page ul ul {
123
+ font-size: 0.9em;
124
+ }
125
+
126
+ .page li {
127
+ padding-left: 16px;
128
+ }
129
+
130
+ .page .author {
131
+ text-align: right;
132
+ padding-right: 20%;
133
+ }
134
+
135
+ a:link {
136
+ color: #cee;
137
+ text-decoration: none;
138
+ }
139
+
140
+ a:visited {
141
+ color: #cee;
142
+ text-decoration: none;
143
+ }
144
+
145
+ a:hover {
146
+ color: #cca;
147
+ text-decoration: underline;
148
+ }
149
+
150
+ a:active {
151
+ color: #cee;
152
+ text-decoration: none;
153
+ }
154
+
155
+ .track-field {
156
+ position: absolute;
157
+ bottom: 1em;
158
+ width: 100%;
159
+ height: 70px;
160
+ }
161
+
162
+ .track-field .avatar {
163
+ position: absolute;
164
+ left: 2%;
165
+ width: 48px;
166
+ height: 48px;
167
+ background-size: contain;
168
+ border-radius: 4px;
169
+ }
170
+
171
+ .track-field .flag {
172
+ position: absolute;
173
+ width: 30px;
174
+ height: 60px;
175
+ background-image: url("../images/flag.png");
176
+ background-size: contain;
177
+ background-repeat: no-repeat;
178
+ z-index: 900;
179
+ }
180
+
181
+ .track-field .rabbit {
182
+ background-image: url("../images/hunter.png");
183
+ background-repeat: no-repeat;
184
+ z-index: 1000;
185
+ }
186
+
187
+ .track-field .turtle {
188
+ background-image: url("../images/usagitokame.png");
189
+ background-repeat: no-repeat;
190
+ z-index: 999;
191
+ top: 30px;
192
+ }
193
+
194
+ .footer .title {
195
+ position: absolute;
196
+ bottom: 4px;
197
+ left: 16px;
198
+ margin: 0;
199
+ font-size: 0.6em;
200
+ color: #888;
201
+ }
202
+
203
+ .footer .powered-by {
204
+ position: absolute;
205
+ bottom: 4px;
206
+ right: 16px;
207
+ margin: 0;
208
+ font-size: 0.6em;
209
+ color: #888;
210
+ }
211
+
212
+ .footer .powered-by .hyalite {
213
+ color: #ee8;
214
+ font-weight: bold;
215
+ }
216
+
217
+ .duration {
218
+ display: none;
219
+ }