slide-em-up 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Binary file
@@ -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/native.css">
11
+ </head>
12
+ <body data-duration="60">
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>var slideshow = new SlideShow();</script>
34
+ </body>
35
+ </html>
@@ -0,0 +1,116 @@
1
+ /*
2
+ * classList.js: Implements a cross-browser element.classList getter.
3
+ * 2010-09-06
4
+ *
5
+ * By Eli Grey, http://eligrey.com
6
+ * Public Domain.
7
+ * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8
+ */
9
+
10
+ "use strict";
11
+
12
+ if (typeof Element !== "undefined") {
13
+
14
+ (function () {
15
+
16
+ var
17
+ classListProp = "classList"
18
+ , protoProp = "prototype"
19
+ , elemCtrProto = Element[protoProp]
20
+ , objCtr = Object
21
+ ;
22
+ if (!objCtr.hasOwnProperty.call(elemCtrProto, classListProp)) {
23
+ var
24
+ strTrim = String[protoProp].trim || function () {
25
+ return this.replace(/^\s+|\s+$/g, "");
26
+ }
27
+ , arrIndexOf = Array[protoProp].indexOf || function (item) {
28
+ for (var i = 0, len = this.length; i < len; i++) {
29
+ if (i in this && this[i] === item) {
30
+ return i;
31
+ }
32
+ }
33
+ return -1;
34
+ }
35
+ , checkTokenAndGetIndex = function (classList, token) {
36
+ if (token === "") {
37
+ throw "SYNTAX_ERR";
38
+ }
39
+ if (/\s/.test(token)) {
40
+ throw "INVALID_CHARACTER_ERR";
41
+ }
42
+ return arrIndexOf.call(classList, token);
43
+ }
44
+ , ClassList = function (elem) {
45
+ var
46
+ trimmedClasses = strTrim.call(elem.className)
47
+ , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
48
+ ;
49
+ for (var i = 0, len = classes.length; i < len; i++) {
50
+ this.push(classes[i]);
51
+ }
52
+ this.updateClassName = function () {
53
+ elem.className = this.toString();
54
+ };
55
+ }
56
+ , classListProto = ClassList[protoProp] = []
57
+ , classListGetter = function () {
58
+ return new ClassList(this);
59
+ }
60
+ ;
61
+ classListProto.item = function (i) {
62
+ return this[i] || null;
63
+ };
64
+ classListProto.contains = function (token) {
65
+ token += "";
66
+ return checkTokenAndGetIndex(this, token) !== -1;
67
+ };
68
+ classListProto.add = function (token) {
69
+ token += "";
70
+ if (checkTokenAndGetIndex(this, token) === -1) {
71
+ this.push(token);
72
+ this.updateClassName();
73
+ }
74
+ };
75
+ classListProto.remove = function (token) {
76
+ token += "";
77
+ var index = checkTokenAndGetIndex(this, token);
78
+ if (index !== -1) {
79
+ this.splice(index, 1);
80
+ this.updateClassName();
81
+ }
82
+ };
83
+ classListProto.toggle = function (token) {
84
+ token += "";
85
+ if (checkTokenAndGetIndex(this, token) === -1) {
86
+ this.add(token);
87
+ } else {
88
+ this.remove(token);
89
+ }
90
+ };
91
+ classListProto.toString = function () {
92
+ return this.join(" ");
93
+ };
94
+
95
+ if (objCtr.defineProperty) {
96
+ var classListDescriptor = {
97
+ get: classListGetter
98
+ , enumerable: true
99
+ , configurable: true
100
+ };
101
+ try {
102
+ objCtr.defineProperty(elemCtrProto, classListProp, classListDescriptor);
103
+ } catch (ex) { // IE 8 doesn't support enumerable:true
104
+ if (ex.number === -0x7FF5EC54) {
105
+ classListDescriptor.enumerable = false;
106
+ objCtr.defineProperty(elemCtrProto, classListProp, classListDescriptor);
107
+ }
108
+ }
109
+ } else if (objCtr[protoProp].__defineGetter__) {
110
+ elemCtrProto.__defineGetter__(classListProp, classListGetter);
111
+ }
112
+ }
113
+
114
+ }());
115
+
116
+ }
@@ -0,0 +1,483 @@
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) { if(con && !con.querySelector) console.trace();
11
+ return (con || document).querySelector(expr); }
12
+ function $$(expr, con) { return [].slice.call((con || document).querySelectorAll(expr)); }
13
+
14
+ (function(head, body){
15
+
16
+ // Check for classList support and include the polyfill if it's not supported
17
+ if(!('classList' in body)) {
18
+ var thisScript = $('script[src$="slideshow.js"]'),
19
+ script = document.createElement('script');
20
+ script.src = thisScript.src.replace(/\bslideshow\.js/, 'classList.js');
21
+ thisScript.parentNode.insertBefore(script, thisScript);
22
+ }
23
+
24
+ // Cache <title> element, we may need it for slides that don't have titles
25
+ var documentTitle = document.title + '';
26
+
27
+ var self = window.SlideShow = function(container, slide) {
28
+ var me = this;
29
+
30
+ // Set instance
31
+ if(!window.slideshow) {
32
+ window.slideshow = this;
33
+ }
34
+
35
+ container = container || body;
36
+
37
+ // Current slide
38
+ this.slide = slide || 0;
39
+
40
+ // Current .delayed item in the slide
41
+ this.item = 0;
42
+
43
+ // Do we need to add a timer?
44
+ this.duration = container.getAttribute('data-duration');
45
+
46
+ if(this.duration > 0) {
47
+ var timer = document.createElement('div'),
48
+ declaration = 'transition: ' + this.duration * 60 + 's linear; ';
49
+
50
+ timer.id = 'timer';
51
+ timer.setAttribute('style', '-moz-' + declaration + '-webkit-' + declaration + '-o-' + declaration + '-ms-' + declaration + declaration);
52
+ container.appendChild(timer);
53
+
54
+ setTimeout(function() {
55
+ timer.className = 'end';
56
+ }, 1);
57
+ }
58
+
59
+ var delayed = $$('ol li', container);
60
+ for (var i=0; i<delayed.length; i++) {
61
+ delayed[i].className = "delayed";
62
+ }
63
+
64
+ // Get the slide elements into an array
65
+ this.slides = Array.prototype.slice.apply($$('.slide', container));
66
+
67
+ for(var i=0; i<this.slides.length; i++) {
68
+ var slide = this.slides[i]; // to speed up references
69
+
70
+ // Asign ids to slides that don't have one
71
+ if(!slide.id) {
72
+ slide.id = 'slide' + (i+1);
73
+ }
74
+
75
+ // Set data-title attribute to the title of the slide
76
+ if(!slide.title) {
77
+ // no title attribute, fetch title from heading(s)
78
+ var heading = $('hgroup', slide) || $('h1,h2,h3,h4,h5,h6', slide);
79
+
80
+ if(heading && heading.textContent.trim()) {
81
+ slide.setAttribute('data-title', heading.textContent);
82
+ }
83
+ }
84
+ else {
85
+ // The title attribute is set, use that
86
+ slide.setAttribute('data-title', slide.title);
87
+ slide.removeAttribute('title');
88
+ }
89
+ }
90
+
91
+ // If there's already a hash, update current slide number...
92
+ this.goto(location.hash.substr(1) || 0);
93
+
94
+ // ...and keep doing so every time the hash changes
95
+ this.onhashchange = function() {
96
+ me.goto(location.hash.substr(1) || 0);
97
+ };
98
+ window.addEventListener('hashchange', this.onhashchange, false);
99
+
100
+ if(window.name === 'projector') {
101
+ document.body.classList.add('projector');
102
+ }
103
+
104
+ // Adjust the font-size when the window is resized
105
+ addEventListener('resize', function() {
106
+ me.adjustFontSize();
107
+ }, false);
108
+
109
+ // In some browsers DOMContentLoaded is too early, so try again onload
110
+ addEventListener('load', function() {
111
+ me.adjustFontSize();
112
+ }, false);
113
+
114
+ /**
115
+ Keyboard navigation
116
+ Ctrl+G : Go to slide...
117
+ Ctrl+H : Show thumbnails and go to slide
118
+ Ctrl+P : Presenter view
119
+ (Shift instead of Ctrl works too)
120
+ */
121
+ document.addEventListener('keyup', function(evt) {
122
+ if(evt.ctrlKey || evt.shiftKey) {
123
+ switch(evt.keyCode) {
124
+ case 71: // G
125
+ var slide = prompt('Which slide?');
126
+ me.goto(+slide? slide - 1 : slide);
127
+ break;
128
+ case 72: // H
129
+ if(body.classList.contains('show-thumbnails')) {
130
+ body.classList.remove('show-thumbnails');
131
+ body.classList.remove('headers-only');
132
+ }
133
+ else {
134
+ body.classList.add('show-thumbnails');
135
+
136
+ if(!evt.shiftKey || !evt.ctrlKey) {
137
+ body.classList.add('headers-only');
138
+ }
139
+
140
+ body.addEventListener('click', function(evt) {
141
+ var slide = evt.target;
142
+
143
+ while(slide && !slide.classList.contains('slide')) {
144
+ slide = slide.parentNode;
145
+ }
146
+
147
+ if(slide) {
148
+ me.goto(slide.id);
149
+ setTimeout(function() { me.adjustFontSize(); }, 1000); // for Opera
150
+ }
151
+
152
+ body.classList.remove('show-thumbnails');
153
+ body.classList.remove('headers-only');
154
+ }, false);
155
+ }
156
+ break;
157
+ case 74: // J
158
+ if(body.classList.contains('hide-elements')) {
159
+ body.classList.remove('hide-elements');
160
+ }
161
+ else {
162
+ body.classList.add('hide-elements');
163
+ }
164
+ break;
165
+ case 80: // P
166
+ // Open new window for attendee view
167
+ me.projector = open(location, 'projector');
168
+
169
+ // Get the focus back
170
+ window.focus();
171
+
172
+ // Switch this one to presenter view
173
+ body.classList.add('presenter');
174
+ }
175
+ }
176
+ }, false);
177
+
178
+ /**
179
+ Keyboard navigation
180
+ Home : First slide
181
+ End : Last slide
182
+ Space/Up/Right arrow : Next item/slide
183
+ Ctrl + Space/Up/Right arrow : Next slide
184
+ Down/Left arrow : Previous item/slide
185
+ Ctrl + Down/Left arrow : Previous slide
186
+ (Shift instead of Ctrl works too)
187
+ */
188
+ document.addEventListener('keydown', function(evt) {
189
+ if(evt.target === body || evt.target === body.parentNode || evt.altKey) {
190
+ if(evt.keyCode >= 32 && evt.keyCode <= 40) {
191
+ evt.preventDefault();
192
+ }
193
+
194
+ switch(evt.keyCode) {
195
+ case 33: //page up
196
+ me.previous();
197
+ break;
198
+ case 34: //page down
199
+ me.next();
200
+ break;
201
+ case 35: // end
202
+ me.end();
203
+ break;
204
+ case 36: // home
205
+ me.start();
206
+ break;
207
+ case 37: // <-
208
+ case 38: // up arrow
209
+ me.previous(evt.ctrlKey || evt.shiftKey);
210
+ break;
211
+ case 32: // space
212
+ case 39: // ->
213
+ case 40: // down arrow
214
+ me.next(evt.ctrlKey || evt.shiftKey);
215
+ break;
216
+ }
217
+ }
218
+ }, false);
219
+
220
+ if (window['EventSource'] != undefined) {
221
+ var source = new EventSource('/remote/sub/events');
222
+ source.onmessage = function(e) {
223
+ switch(e.data){
224
+ case 'next':
225
+ me.next();
226
+ break;
227
+ case 'prev':
228
+ me.prev();
229
+ break;
230
+ case 'up':
231
+ me.next(true);
232
+ break;
233
+ case 'down':
234
+ me.prev(true);
235
+ break;
236
+ default:
237
+ console.log(e);
238
+ };
239
+ };
240
+ }
241
+
242
+ // Rudimentary style[scoped] polyfill
243
+ var scoped = $$('style[scoped]', container);
244
+
245
+ for(var i=scoped.length; i--;) {
246
+ var style = scoped[i],
247
+ rulez = style.sheet.cssRules,
248
+ parentid = style.parentNode.id || self.getSlide(style).id;
249
+
250
+ for(var j=rulez.length; j--;) {
251
+ var cssText = rulez[j].cssText.replace(/^|,/g, function($0) { return '#' + parentid + ' ' + $0 });
252
+
253
+ style.sheet.deleteRule(0);
254
+ style.sheet.insertRule(cssText, 0);
255
+ }
256
+ }
257
+ }
258
+
259
+ self.prototype = {
260
+ start: function() {
261
+ this.goto(0);
262
+ },
263
+
264
+ end: function() {
265
+ this.goto(this.slides.length - 1);
266
+ },
267
+
268
+ /**
269
+ @param hard {Boolean} Whether to advance to the next slide (true) or
270
+ just the next step (which could very well be showing a list item)
271
+ */
272
+ next: function(hard) {
273
+ if(!hard && this.items.length) {
274
+ // If there's no current, then just mark the first one as such
275
+ if(!this.item) {
276
+ this.items[this.item++].classList.add('current');
277
+ }
278
+ // Add .current to current item if it exists, otherwise advance to next slide
279
+ else if(this.item < this.items.length) {
280
+ classes = this.items[this.item - 1].classList; // to speed up lookups
281
+
282
+ classes.remove('current');
283
+ classes.add('displayed');
284
+
285
+ this.items[this.item++].classList.add('current');
286
+ }
287
+ else {
288
+ this.item = 0;
289
+ this.next(true);
290
+ }
291
+ }
292
+ else {
293
+ this.goto(this.slide + 1);
294
+
295
+ this.item = 0;
296
+
297
+ // Mark all items as not displayed, if there are any
298
+ if(this.items.length) {
299
+ for (var i=0; i<this.items.length; i++) {
300
+ if(this.items[i].classList) {
301
+ this.items[i].classList.remove('displayed');
302
+ this.items[i].classList.remove('current');
303
+ }
304
+ }
305
+ }
306
+ }
307
+ },
308
+
309
+ previous: function(hard) {
310
+ if(!hard && this.item > 0) {
311
+ var classes = this.items[this.item - 1].classList; // to speed up lookups
312
+
313
+ classes.remove('current');
314
+
315
+ if(this.item > 1) {
316
+ classes = this.items[--this.item - 1].classList;
317
+
318
+ classes.remove('displayed');
319
+ classes.add('current');
320
+ }
321
+ else {
322
+ this.item = 0;
323
+ }
324
+ }
325
+ else {
326
+
327
+ this.goto(this.slide - 1);
328
+
329
+ this.item = this.items.length;
330
+
331
+ // Mark all items as displayed, if there are any
332
+ if(this.items.length) {
333
+ for (var i=0; i<this.items.length; i++) {
334
+ if(this.items[i].classList) {
335
+ this.items[i].classList.add('displayed');
336
+ }
337
+ }
338
+
339
+ // Mark the last one as current
340
+ var lastItem = this.items[this.items.length - 1];
341
+
342
+ lastItem.classList.remove('displayed');
343
+ lastItem.classList.add('current');
344
+ }
345
+ }
346
+ },
347
+
348
+ /**
349
+ Go to an aribtary slide
350
+ @param which {String|Integer} Which slide (identifier or slide number)
351
+ */
352
+ goto: function(which) {
353
+ var slide;
354
+
355
+ // We have to remove it to prevent multiple calls to goto messing up
356
+ // our current item (and there's no point either, so we save on performance)
357
+ window.removeEventListener('hashchange', this.onhashchange, false);
358
+
359
+ if(which + 0 === which && which in this.slides) { // Argument is a valid slide number
360
+ this.slide = which;
361
+
362
+ slide = this.slides[this.slide];
363
+ location.hash = '#' + slide.id;
364
+ }
365
+ else if(which + '' === which) { // Argument is a slide id
366
+ slide = document.getElementById(which);
367
+
368
+ if(slide) {
369
+ this.slide = this.slides.indexOf(slide);
370
+ location.hash = '#' + which;
371
+ }
372
+ }
373
+
374
+ if(slide) { // Slide actually changed, perform any other tasks needed
375
+ document.title = slide.getAttribute('data-title') || documentTitle;
376
+
377
+ this.adjustFontSize();
378
+
379
+ // Update items collection
380
+ this.items = $$('.delayed, .delayed-children > *', this.slides[this.slide]);
381
+ this.item = 0;
382
+
383
+ // Tell other windows
384
+ if(this.projector && this.projector.slideshow && this.projector.slideshow.slide != this.slide) {
385
+ this.projector.slideshow.goto(this.slide);
386
+ }
387
+
388
+ if(window.opener && opener.slideshow && opener.slideshow.slide != this.slide) {
389
+ opener.slideshow.goto(this.slide);
390
+ }
391
+
392
+ // Update next/previous
393
+ for (var i=this.slides.length; i--;) {
394
+ this.slides[i].classList.remove('previous');
395
+ this.slides[i].classList.remove('next');
396
+ }
397
+
398
+ this.slides.previous = this.slides[this.slide-1];
399
+ this.slides.next = this.slides[this.slide+1];
400
+
401
+ this.slides.previous && this.slides.previous.classList.add('previous');
402
+ this.slides.next && this.slides.next.classList.add('next');
403
+ }
404
+
405
+ // If you attach the listener immediately again then it will catch the event
406
+ // We have to do it asynchronously
407
+ var me = this;
408
+ setTimeout(function() {
409
+ window.addEventListener('hashchange', me.onhashchange, false);
410
+ }, 1000);
411
+ },
412
+
413
+ adjustFontSize: function() {
414
+ // Cache long lookup chains, for performance
415
+ var bodyStyle = body.style,
416
+ scrollRoot = document[document.documentElement.scrollHeight? 'documentElement' : 'body'],
417
+ innerHeight = window.innerHeight,
418
+ innerWidth = window.innerWidth,
419
+ slide = this.slides[this.slide];
420
+
421
+ // Clear previous styles
422
+ bodyStyle.fontSize = '';
423
+
424
+ if(body.classList.contains('show-thumbnails')
425
+ || slide.classList.contains('dont-resize')) {
426
+ return;
427
+ }
428
+
429
+ for(
430
+ var percent = 100;
431
+ (scrollRoot.scrollHeight > innerHeight || scrollRoot.scrollWidth > innerWidth) && percent >= 35;
432
+ percent-=5
433
+ ) {
434
+ bodyStyle.fontSize = percent + '%';
435
+ }
436
+
437
+ // Individual slide
438
+
439
+ if(slide.clientHeight && slide.clientWidth) {
440
+ // Strange FF bug: scrollHeight doesn't work properly with overflow:hidden
441
+ var previousStyle = slide.getAttribute('style');
442
+ slide.style.overflow = 'auto';
443
+
444
+ for(
445
+ ;
446
+ (slide.scrollHeight > slide.clientHeight || slide.scrollWidth > slide.clientWidth) && percent >= 35;
447
+ percent--
448
+ ) {
449
+ bodyStyle.fontSize = percent + '%';
450
+ }
451
+
452
+ slide.setAttribute('style', previousStyle);
453
+ }
454
+ },
455
+
456
+ // Is the element on the current slide?
457
+ onCurrent: function(element) {
458
+ var slide = self.getSlide(element);
459
+
460
+ if(slide) {
461
+ return '#' + slide.id === location.hash;
462
+ }
463
+
464
+ return false;
465
+ }
466
+ };
467
+
468
+ /**********************************************
469
+ * Static methods
470
+ **********************************************/
471
+
472
+ // Helper method for plugins
473
+ self.getSlide = function(element) {
474
+ var slide = element;
475
+
476
+ while (slide && slide.classList && !slide.classList.contains('slide')) {
477
+ slide = slide.parentNode;
478
+ }
479
+
480
+ return slide;
481
+ }
482
+
483
+ })(document.head || document.getElementsByTagName('head')[0], document.body);