gibier 0.8.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.
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
+ }