slide-em-up 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
6
+ <title><%= meta.title %></title>
7
+ <% (theme.css + meta.css).each do |css| %>
8
+ <link rel="stylesheet" href="<%= css%>">
9
+ <% end %>
10
+ <link rel="stylesheet" href="css/pygments/solarized.css">
11
+ </head>
12
+ <body data-duration="<%= meta.duration %>">
13
+ <section>
14
+ <header class="slide">
15
+ <h1><%= meta.title %></h1>
16
+ </header>
17
+ </section>
18
+ <% sections.each do |section| %>
19
+ <section>
20
+ <header class="slide">
21
+ <h1><%= section.title %></h1>
22
+ </header>
23
+ <% section.slides.each do |slide| %>
24
+ <section class="slide <%= slide.classes %>">
25
+ <%= slide.html %>
26
+ </section>
27
+ <% end %>
28
+ </section>
29
+ <% end %>
30
+ <% (theme.js + meta.js).each do |js| %>
31
+ <script src="<%= js %>"></script>
32
+ <% end %>
33
+ <script>slideshow = new SlideShow();</script>
34
+ </body>
35
+ </html>
@@ -0,0 +1,420 @@
1
+ /**
2
+ * CSSS javascript code
3
+ * @author Lea Verou (http://leaverou.me)
4
+ * @version 2.0
5
+ */
6
+
7
+ /**
8
+ * Make the environment a bit friendlier
9
+ */
10
+ function $(expr, con) {
11
+ return (con || document).querySelector(expr);
12
+ }
13
+ function $$(expr, con) {
14
+ return [].slice.call((con || document).querySelectorAll(expr));
15
+ }
16
+
17
+ (function(body){
18
+
19
+ // Cache <title> element, we may need it for slides that don't have titles
20
+ var documentTitle = document.title + '';
21
+
22
+ var self = window.SlideShow = function(container, slide) {
23
+ var me = this;
24
+
25
+ // Set instance
26
+ if(!window.slideshow) {
27
+ window.slideshow = this;
28
+ }
29
+
30
+ container = container || body;
31
+
32
+ // Current slide
33
+ this.slide = slide || 0;
34
+
35
+ // Current .delayed item in the slide
36
+ this.item = 0;
37
+
38
+ // Do we need to add a timer?
39
+ this.duration = container.getAttribute('data-duration');
40
+
41
+ if(this.duration > 0) {
42
+ var timer = document.createElement('div'),
43
+ declaration = 'transition: ' + this.duration * 60 + 's linear; ';
44
+
45
+ timer.id = 'timer';
46
+ timer.setAttribute('style', '-moz-' + declaration + '-webkit-' + declaration + '-o-' + declaration + '-ms-' + declaration + declaration);
47
+ container.appendChild(timer);
48
+
49
+ setTimeout(function() {
50
+ timer.className = 'end';
51
+ }, 1);
52
+ }
53
+
54
+ var delayed = $$('ol li', container);
55
+ for (var i=0; i<delayed.length; i++) {
56
+ delayed[i].className = "delayed";
57
+ }
58
+
59
+ // Get the slide elements into an array
60
+ this.slides = Array.prototype.slice.apply($$('.slide', container));
61
+
62
+ for(var i=0; i<this.slides.length; i++) {
63
+ var slide = this.slides[i]; // to speed up references
64
+
65
+ // Asign ids to slides that don't have one
66
+ if(!slide.id) {
67
+ slide.id = 'slide' + (i+1);
68
+ }
69
+
70
+ // Set data-title attribute to the title of the slide
71
+ if(!slide.title) {
72
+ // no title attribute, fetch title from heading(s)
73
+ var heading = $('hgroup', slide) || $('h1,h2,h3,h4,h5,h6', slide);
74
+
75
+ if(heading && heading.textContent.trim()) {
76
+ slide.setAttribute('data-title', heading.textContent);
77
+ }
78
+ }
79
+ else {
80
+ // The title attribute is set, use that
81
+ slide.setAttribute('data-title', slide.title);
82
+ slide.removeAttribute('title');
83
+ }
84
+ }
85
+
86
+ // If there's already a hash, update current slide number...
87
+ this.goto(location.hash.substr(1) || 0);
88
+
89
+ // ...and keep doing so every time the hash changes
90
+ this.onhashchange = function() {
91
+ me.goto(location.hash.substr(1) || 0);
92
+ };
93
+ window.addEventListener('hashchange', this.onhashchange, false);
94
+
95
+ me.startEventSourceHandler('/remote/sub/events');
96
+
97
+ if(window.name === 'projector') {
98
+ document.body.classList.add('projector');
99
+ }
100
+
101
+ // Adjust the font-size when the window is resized
102
+ addEventListener('resize', function() {
103
+ me.adjustFontSize();
104
+ }, false);
105
+
106
+ // In some browsers DOMContentLoaded is too early, so try again onload
107
+ addEventListener('load', function() {
108
+ me.adjustFontSize();
109
+ }, false);
110
+
111
+ /**
112
+ Keyboard navigation
113
+ Home : First slide
114
+ End : Last slide
115
+ Space/Up/Right arrow : Next item/slide
116
+ Ctrl + Space/Up/Right arrow : Next slide
117
+ Down/Left arrow : Previous item/slide
118
+ Ctrl + Down/Left arrow : Previous slide
119
+ (Shift instead of Ctrl works too)
120
+ */
121
+ document.addEventListener('keydown', function(evt) {
122
+ if(evt.target === body || evt.target === body.parentNode || evt.altKey) {
123
+ if(evt.keyCode >= 32 && evt.keyCode <= 40) {
124
+ evt.preventDefault();
125
+ }
126
+
127
+ switch(evt.keyCode) {
128
+ case 33: //page up
129
+ me.previous();
130
+ break;
131
+ case 34: //page down
132
+ me.next();
133
+ break;
134
+ case 35: // end
135
+ me.end();
136
+ break;
137
+ case 36: // home
138
+ me.start();
139
+ break;
140
+ case 37: // <-
141
+ case 38: // up arrow
142
+ me.previous(evt.ctrlKey || evt.shiftKey);
143
+ break;
144
+ case 32: // space
145
+ case 39: // ->
146
+ case 40: // down arrow
147
+ me.next(evt.ctrlKey || evt.shiftKey);
148
+ break;
149
+ }
150
+ }
151
+ }, false);
152
+
153
+ // Rudimentary style[scoped] polyfill
154
+ var scoped = $$('style[scoped]', container);
155
+
156
+ for(var i=scoped.length; i--;) {
157
+ var style = scoped[i],
158
+ rulez = style.sheet.cssRules,
159
+ parentid = style.parentNode.id || self.getSlide(style).id;
160
+
161
+ for(var j=rulez.length; j--;) {
162
+ var cssText = rulez[j].cssText.replace(/^|,/g, function($0) { return '#' + parentid + ' ' + $0 });
163
+
164
+ style.sheet.deleteRule(0);
165
+ style.sheet.insertRule(cssText, 0);
166
+ }
167
+ }
168
+ }
169
+
170
+ self.prototype = {
171
+ start: function() {
172
+ this.goto(0);
173
+ },
174
+
175
+ end: function() {
176
+ this.goto(this.slides.length - 1);
177
+ },
178
+
179
+ /**
180
+ @param hard {Boolean} Whether to advance to the next slide (true) or
181
+ just the next step (which could very well be showing a list item)
182
+ */
183
+ next: function(hard) {
184
+ if(!hard && this.items.length) {
185
+ // If there's no current, then just mark the first one as such
186
+ if(!this.item) {
187
+ this.items[this.item++].classList.add('current');
188
+ }
189
+ // Add .current to current item if it exists, otherwise advance to next slide
190
+ else if(this.item < this.items.length) {
191
+ classes = this.items[this.item - 1].classList; // to speed up lookups
192
+
193
+ classes.remove('current');
194
+ classes.add('displayed');
195
+
196
+ this.items[this.item++].classList.add('current');
197
+ }
198
+ else {
199
+ this.item = 0;
200
+ this.next(true);
201
+ }
202
+ }
203
+ else {
204
+ this.goto(this.slide + 1);
205
+
206
+ this.item = 0;
207
+
208
+ // Mark all items as not displayed, if there are any
209
+ if(this.items.length) {
210
+ for (var i=0; i<this.items.length; i++) {
211
+ if(this.items[i].classList) {
212
+ this.items[i].classList.remove('displayed');
213
+ this.items[i].classList.remove('current');
214
+ }
215
+ }
216
+ }
217
+ }
218
+ },
219
+
220
+ previous: function(hard) {
221
+ if(!hard && this.item > 0) {
222
+ var classes = this.items[this.item - 1].classList; // to speed up lookups
223
+
224
+ classes.remove('current');
225
+
226
+ if(this.item > 1) {
227
+ classes = this.items[--this.item - 1].classList;
228
+
229
+ classes.remove('displayed');
230
+ classes.add('current');
231
+ }
232
+ else {
233
+ this.item = 0;
234
+ }
235
+ }
236
+ else {
237
+
238
+ this.goto(this.slide - 1);
239
+
240
+ this.item = this.items.length;
241
+
242
+ // Mark all items as displayed, if there are any
243
+ if(this.items.length) {
244
+ for (var i=0; i<this.items.length; i++) {
245
+ if(this.items[i].classList) {
246
+ this.items[i].classList.add('displayed');
247
+ }
248
+ }
249
+
250
+ // Mark the last one as current
251
+ var lastItem = this.items[this.items.length - 1];
252
+
253
+ lastItem.classList.remove('displayed');
254
+ lastItem.classList.add('current');
255
+ }
256
+ }
257
+ },
258
+
259
+ /**
260
+ Go to an aribtary slide
261
+ @param which {String|Integer} Which slide (identifier or slide number)
262
+ */
263
+ goto: function(which) {
264
+ var slide;
265
+
266
+ // We have to remove it to prevent multiple calls to goto messing up
267
+ // our current item (and there's no point either, so we save on performance)
268
+ window.removeEventListener('hashchange', this.onhashchange, false);
269
+
270
+ if(which + 0 === which && which in this.slides) { // Argument is a valid slide number
271
+ this.slide = which;
272
+
273
+ slide = this.slides[this.slide];
274
+ location.hash = '#' + slide.id;
275
+ }
276
+ else if(which + '' === which) { // Argument is a slide id
277
+ slide = document.getElementById(which);
278
+
279
+ if(slide) {
280
+ this.slide = this.slides.indexOf(slide);
281
+ location.hash = '#' + which;
282
+ }
283
+ }
284
+
285
+ if(slide) { // Slide actually changed, perform any other tasks needed
286
+ document.title = slide.getAttribute('data-title') || documentTitle;
287
+
288
+ this.adjustFontSize();
289
+
290
+ // Update items collection
291
+ this.items = $$('.delayed, .delayed-children > *', this.slides[this.slide]);
292
+ this.item = 0;
293
+
294
+ // Tell other windows
295
+ if(this.projector && this.projector.slideshow && this.projector.slideshow.slide != this.slide) {
296
+ this.projector.slideshow.goto(this.slide);
297
+ }
298
+
299
+ if(window.opener && opener.slideshow && opener.slideshow.slide != this.slide) {
300
+ opener.slideshow.goto(this.slide);
301
+ }
302
+
303
+ // Update next/previous
304
+ for (var i=this.slides.length; i--;) {
305
+ this.slides[i].classList.remove('previous');
306
+ this.slides[i].classList.remove('next');
307
+ }
308
+
309
+ this.slides.previous = this.slides[this.slide-1];
310
+ this.slides.next = this.slides[this.slide+1];
311
+
312
+ this.slides.previous && this.slides.previous.classList.add('previous');
313
+ this.slides.next && this.slides.next.classList.add('next');
314
+ }
315
+
316
+ // If you attach the listener immediately again then it will catch the event
317
+ // We have to do it asynchronously
318
+ var me = this;
319
+ setTimeout(function() {
320
+ window.addEventListener('hashchange', me.onhashchange, false);
321
+ }, 1000);
322
+ },
323
+
324
+ adjustFontSize: function() {
325
+ // Cache long lookup chains, for performance
326
+ var bodyStyle = body.style,
327
+ scrollRoot = document[document.documentElement.scrollHeight? 'documentElement' : 'body'],
328
+ innerHeight = window.innerHeight,
329
+ innerWidth = window.innerWidth,
330
+ slide = this.slides[this.slide];
331
+
332
+ // Clear previous styles
333
+ bodyStyle.fontSize = '';
334
+
335
+ if(body.classList.contains('show-thumbnails')
336
+ || slide.classList.contains('dont-resize')) {
337
+ return;
338
+ }
339
+
340
+ for(
341
+ var percent = 100;
342
+ (scrollRoot.scrollHeight > innerHeight || scrollRoot.scrollWidth > innerWidth) && percent >= 35;
343
+ percent-=5
344
+ ) {
345
+ bodyStyle.fontSize = percent + '%';
346
+ }
347
+
348
+ // Individual slide
349
+
350
+ if(slide.clientHeight && slide.clientWidth) {
351
+ // Strange FF bug: scrollHeight doesn't work properly with overflow:hidden
352
+ var previousStyle = slide.getAttribute('style');
353
+ slide.style.overflow = 'auto';
354
+
355
+ for(
356
+ ;
357
+ (slide.scrollHeight > slide.clientHeight || slide.scrollWidth > slide.clientWidth) && percent >= 35;
358
+ percent--
359
+ ) {
360
+ bodyStyle.fontSize = percent + '%';
361
+ }
362
+
363
+ slide.setAttribute('style', previousStyle);
364
+ }
365
+ },
366
+
367
+ // Is the element on the current slide?
368
+ onCurrent: function(element) {
369
+ var slide = self.getSlide(element);
370
+
371
+ if(slide) {
372
+ return '#' + slide.id === location.hash;
373
+ }
374
+
375
+ return false;
376
+ },
377
+
378
+ startEventSourceHandler: function(uri) {
379
+ if (window['EventSource'] == undefined) return ;
380
+
381
+ var source = new EventSource(uri);
382
+ var me = this;
383
+
384
+ source.onmessage = function(e) {
385
+ switch(e.data){
386
+ case 'next':
387
+ me.next();
388
+ break;
389
+ case 'prev':
390
+ me.previous();
391
+ break;
392
+ case 'up':
393
+ me.end();
394
+ break;
395
+ case 'down':
396
+ me.start();
397
+ break;
398
+ default:
399
+ console.log(e);
400
+ };
401
+ };
402
+ }
403
+ };
404
+
405
+ /**********************************************
406
+ * Static methods
407
+ **********************************************/
408
+
409
+ // Helper method for plugins
410
+ self.getSlide = function(element) {
411
+ var slide = element;
412
+
413
+ while (slide && slide.classList && !slide.classList.contains('slide')) {
414
+ slide = slide.parentNode;
415
+ }
416
+
417
+ return slide;
418
+ }
419
+
420
+ })(document.body);