10
|
1 /**
|
|
2 * @authors Luke Mahe
|
|
3 * @authors Eric Bidelman
|
|
4 * @fileoverview TODO
|
|
5 */
|
|
6 document.cancelFullScreen = document.webkitCancelFullScreen ||
|
|
7 document.mozCancelFullScreen;
|
|
8
|
|
9 /**
|
|
10 * @constructor
|
|
11 */
|
|
12 function SlideDeck(el) {
|
|
13 this.curSlide_ = 0;
|
|
14 this.prevSlide_ = 0;
|
|
15 this.config_ = null;
|
|
16 this.container = el || document.querySelector('slides');
|
|
17 this.slides = [];
|
|
18 this.controller = null;
|
|
19
|
|
20 this.getCurrentSlideFromHash_();
|
|
21
|
|
22 // Call this explicitly. Modernizr.load won't be done until after DOM load.
|
|
23 this.onDomLoaded_.bind(this)();
|
|
24 }
|
|
25
|
|
26 /**
|
|
27 * @const
|
|
28 * @private
|
|
29 */
|
|
30 SlideDeck.prototype.SLIDE_CLASSES_ = [
|
|
31 'far-past', 'past', 'current', 'next', 'far-next'];
|
|
32
|
|
33 /**
|
|
34 * @const
|
|
35 * @private
|
|
36 */
|
|
37 SlideDeck.prototype.CSS_DIR_ = 'theme/css/';
|
|
38
|
|
39 /**
|
|
40 * @private
|
|
41 */
|
|
42 SlideDeck.prototype.getCurrentSlideFromHash_ = function() {
|
|
43 var slideNo = parseInt(document.location.hash.substr(1));
|
|
44
|
|
45 if (slideNo) {
|
|
46 this.curSlide_ = slideNo - 1;
|
|
47 } else {
|
|
48 this.curSlide_ = 0;
|
|
49 }
|
|
50 };
|
|
51
|
|
52 /**
|
|
53 * @param {number} slideNo
|
|
54 */
|
|
55 SlideDeck.prototype.loadSlide = function(slideNo) {
|
|
56 if (slideNo) {
|
|
57 this.curSlide_ = slideNo - 1;
|
|
58 this.updateSlides_();
|
|
59 }
|
|
60 };
|
|
61
|
|
62 /**
|
|
63 * @private
|
|
64 */
|
|
65 SlideDeck.prototype.onDomLoaded_ = function(e) {
|
|
66 document.body.classList.add('loaded'); // Add loaded class for templates to use.
|
|
67
|
|
68 this.slides = this.container.querySelectorAll('slide:not([hidden]):not(.backdrop)');
|
|
69
|
|
70 // If we're on a smartphone, apply special sauce.
|
|
71 if (Modernizr.mq('only screen and (max-device-width: 480px)')) {
|
|
72 // var style = document.createElement('link');
|
|
73 // style.rel = 'stylesheet';
|
|
74 // style.type = 'text/css';
|
|
75 // style.href = this.CSS_DIR_ + 'phone.css';
|
|
76 // document.querySelector('head').appendChild(style);
|
|
77
|
|
78 // No need for widescreen layout on a phone.
|
|
79 this.container.classList.remove('layout-widescreen');
|
|
80 }
|
|
81
|
|
82 this.loadConfig_(SLIDE_CONFIG);
|
|
83 this.addEventListeners_();
|
|
84 this.updateSlides_();
|
|
85
|
|
86 // Add slide numbers and total slide count metadata to each slide.
|
|
87 var that = this;
|
|
88 for (var i = 0, slide; slide = this.slides[i]; ++i) {
|
|
89 slide.dataset.slideNum = i + 1;
|
|
90 slide.dataset.totalSlides = this.slides.length;
|
|
91
|
|
92 slide.addEventListener('click', function(e) {
|
|
93 if (document.body.classList.contains('overview')) {
|
|
94 that.loadSlide(this.dataset.slideNum);
|
|
95 e.preventDefault();
|
|
96 window.setTimeout(function() {
|
|
97 that.toggleOverview();
|
|
98 }, 500);
|
|
99 }
|
|
100 }, false);
|
|
101 }
|
|
102
|
|
103 // Note: this needs to come after addEventListeners_(), which adds a
|
|
104 // 'keydown' listener that this controller relies on.
|
|
105 // Also, no need to set this up if we're on mobile.
|
|
106 if (!Modernizr.touch) {
|
|
107 this.controller = new SlideController(this);
|
|
108 if (this.controller.isPopup) {
|
|
109 document.body.classList.add('popup');
|
|
110 }
|
|
111 }
|
|
112 };
|
|
113
|
|
114 /**
|
|
115 * @private
|
|
116 */
|
|
117 SlideDeck.prototype.addEventListeners_ = function() {
|
|
118 document.addEventListener('keydown', this.onBodyKeyDown_.bind(this), false);
|
|
119 window.addEventListener('popstate', this.onPopState_.bind(this), false);
|
|
120
|
|
121 // var transEndEventNames = {
|
|
122 // 'WebkitTransition': 'webkitTransitionEnd',
|
|
123 // 'MozTransition': 'transitionend',
|
|
124 // 'OTransition': 'oTransitionEnd',
|
|
125 // 'msTransition': 'MSTransitionEnd',
|
|
126 // 'transition': 'transitionend'
|
|
127 // };
|
|
128 //
|
|
129 // // Find the correct transitionEnd vendor prefix.
|
|
130 // window.transEndEventName = transEndEventNames[
|
|
131 // Modernizr.prefixed('transition')];
|
|
132 //
|
|
133 // // When slides are done transitioning, kickoff loading iframes.
|
|
134 // // Note: we're only looking at a single transition (on the slide). This
|
|
135 // // doesn't include autobuilds the slides may have. Also, if the slide
|
|
136 // // transitions on multiple properties (e.g. not just 'all'), this doesn't
|
|
137 // // handle that case.
|
|
138 // this.container.addEventListener(transEndEventName, function(e) {
|
|
139 // this.enableSlideFrames_(this.curSlide_);
|
|
140 // }.bind(this), false);
|
|
141
|
|
142 // document.addEventListener('slideenter', function(e) {
|
|
143 // var slide = e.target;
|
|
144 // window.setTimeout(function() {
|
|
145 // this.enableSlideFrames_(e.slideNumber);
|
|
146 // this.enableSlideFrames_(e.slideNumber + 1);
|
|
147 // }.bind(this), 300);
|
|
148 // }.bind(this), false);
|
|
149 };
|
|
150
|
|
151 /**
|
|
152 * @private
|
|
153 * @param {Event} e The pop event.
|
|
154 */
|
|
155 SlideDeck.prototype.onPopState_ = function(e) {
|
|
156 if (e.state != null) {
|
|
157 this.curSlide_ = e.state;
|
|
158 this.updateSlides_(true);
|
|
159 }
|
|
160 };
|
|
161
|
|
162 /**
|
|
163 * @param {Event} e
|
|
164 */
|
|
165 SlideDeck.prototype.onBodyKeyDown_ = function(e) {
|
|
166 if (/^(input|textarea)$/i.test(e.target.nodeName) ||
|
|
167 e.target.isContentEditable) {
|
|
168 return;
|
|
169 }
|
|
170
|
|
171 // Forward keydowns to the main slides if we're the popup.
|
|
172 if (this.controller && this.controller.isPopup) {
|
|
173 this.controller.sendMsg({keyCode: e.keyCode});
|
|
174 }
|
|
175
|
|
176 switch (e.keyCode) {
|
|
177 case 13: // Enter
|
|
178 if (document.body.classList.contains('overview')) {
|
|
179 this.toggleOverview();
|
|
180 }
|
|
181 break;
|
|
182
|
|
183 case 39: // right arrow
|
|
184 case 32: // space
|
|
185 case 34: // PgDn
|
|
186 this.nextSlide();
|
|
187 e.preventDefault();
|
|
188 break;
|
|
189
|
|
190 case 37: // left arrow
|
|
191 case 8: // Backspace
|
|
192 case 33: // PgUp
|
|
193 this.prevSlide();
|
|
194 e.preventDefault();
|
|
195 break;
|
|
196
|
|
197 case 40: // down arrow
|
|
198 this.nextSlide();
|
|
199 e.preventDefault();
|
|
200 break;
|
|
201
|
|
202 case 38: // up arrow
|
|
203 this.prevSlide();
|
|
204 e.preventDefault();
|
|
205 break;
|
|
206
|
|
207 case 72: // H: Toggle code highlighting
|
|
208 document.body.classList.toggle('highlight-code');
|
|
209 break;
|
|
210
|
|
211 case 79: // O: Toggle overview
|
|
212 this.toggleOverview();
|
|
213 break;
|
|
214
|
|
215 case 80: // P
|
|
216 if (this.controller && this.controller.isPopup) {
|
|
217 document.body.classList.toggle('with-notes');
|
|
218 } else if (this.controller && !this.controller.popup) {
|
|
219 document.body.classList.toggle('with-notes');
|
|
220 }
|
|
221 break;
|
|
222
|
|
223 case 82: // R
|
|
224 // TODO: implement refresh on main slides when popup is refreshed.
|
|
225 break;
|
|
226
|
|
227 case 27: // ESC: Hide notes and highlighting
|
|
228 document.body.classList.remove('with-notes');
|
|
229 document.body.classList.remove('highlight-code');
|
|
230
|
|
231 if (document.body.classList.contains('overview')) {
|
|
232 this.toggleOverview();
|
|
233 }
|
|
234 break;
|
|
235
|
|
236 case 70: // F: Toggle fullscreen
|
|
237 // Only respect 'f' on body. Don't want to capture keys from an <input>.
|
|
238 // Also, ignore browser's fullscreen shortcut (cmd+shift+f) so we don't
|
|
239 // get trapped in fullscreen!
|
|
240 if (e.target == document.body && !(e.shiftKey && e.metaKey)) {
|
|
241 if (document.mozFullScreen !== undefined && !document.mozFullScreen) {
|
|
242 document.body.mozRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
|
|
243 } else if (document.webkitIsFullScreen !== undefined && !document.webkitIsFullScreen) {
|
|
244 document.body.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
|
|
245 } else {
|
|
246 document.cancelFullScreen();
|
|
247 }
|
|
248 }
|
|
249 break;
|
|
250
|
|
251 case 87: // W: Toggle widescreen
|
|
252 // Only respect 'w' on body. Don't want to capture keys from an <input>.
|
|
253 if (e.target == document.body && !(e.shiftKey && e.metaKey)) {
|
|
254 this.container.classList.toggle('layout-widescreen');
|
|
255 }
|
|
256 break;
|
|
257 }
|
|
258 };
|
|
259
|
|
260 /**
|
|
261 *
|
|
262 */
|
|
263 SlideDeck.prototype.focusOverview_ = function() {
|
|
264 var overview = document.body.classList.contains('overview');
|
|
265
|
|
266 for (var i = 0, slide; slide = this.slides[i]; i++) {
|
|
267 slide.style[Modernizr.prefixed('transform')] = overview ?
|
|
268 'translateZ(-2500px) translate(' + (( i - this.curSlide_ ) * 105) +
|
|
269 '%, 0%)' : '';
|
|
270 }
|
|
271 };
|
|
272
|
|
273 /**
|
|
274 */
|
|
275 SlideDeck.prototype.toggleOverview = function() {
|
|
276 document.body.classList.toggle('overview');
|
|
277
|
|
278 this.focusOverview_();
|
|
279 };
|
|
280
|
|
281 /**
|
|
282 * @private
|
|
283 */
|
|
284 SlideDeck.prototype.loadConfig_ = function(config) {
|
|
285 if (!config) {
|
|
286 return;
|
|
287 }
|
|
288
|
|
289 this.config_ = config;
|
|
290
|
|
291 var settings = this.config_.settings;
|
|
292
|
|
293 this.loadTheme_(settings.theme || []);
|
|
294
|
|
295 if (settings.favIcon) {
|
|
296 this.addFavIcon_(settings.favIcon);
|
|
297 }
|
|
298
|
|
299 // Prettyprint. Default to on.
|
|
300 if (!!!('usePrettify' in settings) || settings.usePrettify) {
|
|
301 prettyPrint();
|
|
302 }
|
|
303
|
|
304 if (settings.analytics) {
|
|
305 this.loadAnalytics_();
|
|
306 }
|
|
307
|
|
308 if (settings.fonts) {
|
|
309 this.addFonts_(settings.fonts);
|
|
310 }
|
|
311
|
|
312 // Builds. Default to on.
|
|
313 if (!!!('useBuilds' in settings) || settings.useBuilds) {
|
|
314 this.makeBuildLists_();
|
|
315 }
|
|
316
|
|
317 if (settings.title) {
|
|
318 document.title = settings.title.replace(/<br\/?>/, ' ');
|
|
319 if (settings.eventTitle) {
|
|
320 document.title += ' - ' + settings.eventTitle;
|
|
321 }
|
|
322 document.querySelector('[data-config-title]').innerHTML = settings.title;
|
|
323 }
|
|
324
|
|
325 if (settings.subtitle) {
|
|
326 document.querySelector('[data-config-subtitle]').innerHTML = settings.subtitle;
|
|
327 }
|
|
328
|
|
329 if (this.config_.presenters) {
|
|
330 var presenters = this.config_.presenters;
|
|
331 var dataConfigContact = document.querySelector('[data-config-contact]');
|
|
332
|
|
333 var html = [];
|
|
334 if (presenters.length == 1) {
|
|
335 var p = presenters[0];
|
|
336
|
|
337 html = [p.name, p.company].join('<br>');
|
|
338
|
|
339 var gplus = p.gplus ? '<span>g+</span><a href="' + p.gplus +
|
|
340 '">' + p.gplus.replace(/https?:\/\//, '') + '</a>' : '';
|
|
341
|
|
342 var twitter = p.twitter ? '<span>twitter</span>' +
|
|
343 '<a href="http://twitter.com/' + p.twitter + '">' +
|
|
344 p.twitter + '</a>' : '';
|
|
345
|
|
346 var www = p.www ? '<span>www</span><a href="' + p.www +
|
|
347 '">' + p.www.replace(/https?:\/\//, '') + '</a>' : '';
|
|
348
|
|
349 var github = p.github ? '<span>github</span><a href="' + p.github +
|
|
350 '">' + p.github.replace(/https?:\/\//, '') + '</a>' : '';
|
|
351
|
|
352 var html2 = [gplus, twitter, www, github].join('<br>');
|
|
353
|
|
354 if (dataConfigContact) {
|
|
355 dataConfigContact.innerHTML = html2;
|
|
356 }
|
|
357 } else {
|
|
358 for (var i = 0, p; p = presenters[i]; ++i) {
|
|
359 html.push(p.name + ' - ' + p.company);
|
|
360 }
|
|
361 html = html.join('<br>');
|
|
362 if (dataConfigContact) {
|
|
363 dataConfigContact.innerHTML = html;
|
|
364 }
|
|
365 }
|
|
366
|
|
367 var dataConfigPresenter = document.querySelector('[data-config-presenter]');
|
|
368 if (dataConfigPresenter) {
|
|
369 dataConfigPresenter.innerHTML = html;
|
|
370 if (settings.eventTitle) {
|
|
371 dataConfigPresenter.innerHTML = dataConfigPresenter.innerHTML + '<br>' +
|
|
372 settings.eventTitle;
|
|
373 }
|
|
374 }
|
|
375 }
|
|
376
|
|
377 /* Left/Right tap areas. Default to including. */
|
|
378 if (!!!('enableSlideAreas' in settings) || settings.enableSlideAreas) {
|
|
379 var el = document.createElement('div');
|
|
380 el.classList.add('slide-area');
|
|
381 el.id = 'prev-slide-area';
|
|
382 el.addEventListener('click', this.prevSlide.bind(this), false);
|
|
383 this.container.appendChild(el);
|
|
384
|
|
385 var el = document.createElement('div');
|
|
386 el.classList.add('slide-area');
|
|
387 el.id = 'next-slide-area';
|
|
388 el.addEventListener('click', this.nextSlide.bind(this), false);
|
|
389 this.container.appendChild(el);
|
|
390 }
|
|
391
|
|
392 if (Modernizr.touch && (!!!('enableTouch' in settings) ||
|
|
393 settings.enableTouch)) {
|
|
394 var self = this;
|
|
395
|
|
396 // Note: this prevents mobile zoom in/out but prevents iOS from doing
|
|
397 // it's crazy scroll over effect and disaligning the slides.
|
|
398 window.addEventListener('touchstart', function(e) {
|
|
399 e.preventDefault();
|
|
400 }, false);
|
|
401
|
|
402 var hammer = new Hammer(this.container);
|
|
403 hammer.ondragend = function(e) {
|
|
404 if (e.direction == 'right' || e.direction == 'down') {
|
|
405 self.prevSlide();
|
|
406 } else if (e.direction == 'left' || e.direction == 'up') {
|
|
407 self.nextSlide();
|
|
408 }
|
|
409 };
|
|
410 }
|
|
411 };
|
|
412
|
|
413 /**
|
|
414 * @private
|
|
415 * @param {Array.<string>} fonts
|
|
416 */
|
|
417 SlideDeck.prototype.addFonts_ = function(fonts) {
|
|
418 var el = document.createElement('link');
|
|
419 el.rel = 'stylesheet';
|
|
420 el.href = ('https:' == document.location.protocol ? 'https' : 'http') +
|
|
421 '://fonts.googleapis.com/css?family=' + fonts.join('|') + '&v2';
|
|
422 document.querySelector('head').appendChild(el);
|
|
423 };
|
|
424
|
|
425 /**
|
|
426 * @private
|
|
427 */
|
|
428 SlideDeck.prototype.buildNextItem_ = function() {
|
|
429 var slide = this.slides[this.curSlide_];
|
|
430 var toBuild = slide.querySelector('.to-build');
|
|
431 var built = slide.querySelector('.build-current');
|
|
432
|
|
433 if (built) {
|
|
434 built.classList.remove('build-current');
|
|
435 if (built.classList.contains('fade')) {
|
|
436 built.classList.add('build-fade');
|
|
437 }
|
|
438 }
|
|
439
|
|
440 if (!toBuild) {
|
|
441 var items = slide.querySelectorAll('.build-fade');
|
|
442 for (var j = 0, item; item = items[j]; j++) {
|
|
443 item.classList.remove('build-fade');
|
|
444 }
|
|
445 return false;
|
|
446 }
|
|
447
|
|
448 toBuild.classList.remove('to-build');
|
|
449 toBuild.classList.add('build-current');
|
|
450
|
|
451 return true;
|
|
452 };
|
|
453
|
|
454 /**
|
|
455 * @param {boolean=} opt_dontPush
|
|
456 */
|
|
457 SlideDeck.prototype.prevSlide = function(opt_dontPush) {
|
|
458 if (this.curSlide_ > 0) {
|
|
459 var bodyClassList = document.body.classList;
|
|
460 bodyClassList.remove('highlight-code');
|
|
461
|
|
462 // Toggle off speaker notes if they're showing when we move backwards on the
|
|
463 // main slides. If we're the speaker notes popup, leave them up.
|
|
464 if (this.controller && !this.controller.isPopup) {
|
|
465 bodyClassList.remove('with-notes');
|
|
466 } else if (!this.controller) {
|
|
467 bodyClassList.remove('with-notes');
|
|
468 }
|
|
469
|
|
470 this.prevSlide_ = this.curSlide_--;
|
|
471
|
|
472 this.updateSlides_(opt_dontPush);
|
|
473 }
|
|
474 };
|
|
475
|
|
476 /**
|
|
477 * @param {boolean=} opt_dontPush
|
|
478 */
|
|
479 SlideDeck.prototype.nextSlide = function(opt_dontPush) {
|
|
480 if (!document.body.classList.contains('overview') && this.buildNextItem_()) {
|
|
481 return;
|
|
482 }
|
|
483
|
|
484 if (this.curSlide_ < this.slides.length - 1) {
|
|
485 var bodyClassList = document.body.classList;
|
|
486 bodyClassList.remove('highlight-code');
|
|
487
|
|
488 // Toggle off speaker notes if they're showing when we advanced on the main
|
|
489 // slides. If we're the speaker notes popup, leave them up.
|
|
490 if (this.controller && !this.controller.isPopup) {
|
|
491 bodyClassList.remove('with-notes');
|
|
492 } else if (!this.controller) {
|
|
493 bodyClassList.remove('with-notes');
|
|
494 }
|
|
495
|
|
496 this.prevSlide_ = this.curSlide_++;
|
|
497
|
|
498 this.updateSlides_(opt_dontPush);
|
|
499 }
|
|
500 };
|
|
501
|
|
502 /* Slide events */
|
|
503
|
|
504 /**
|
|
505 * Triggered when a slide enter/leave event should be dispatched.
|
|
506 *
|
|
507 * @param {string} type The type of event to trigger
|
|
508 * (e.g. 'slideenter', 'slideleave').
|
|
509 * @param {number} slideNo The index of the slide that is being left.
|
|
510 */
|
|
511 SlideDeck.prototype.triggerSlideEvent = function(type, slideNo) {
|
|
512 var el = this.getSlideEl_(slideNo);
|
|
513 if (!el) {
|
|
514 return;
|
|
515 }
|
|
516
|
|
517 // Call onslideenter/onslideleave if the attribute is defined on this slide.
|
|
518 var func = el.getAttribute(type);
|
|
519 if (func) {
|
|
520 new Function(func).call(el); // TODO: Don't use new Function() :(
|
|
521 }
|
|
522
|
|
523 // Dispatch event to listeners setup using addEventListener.
|
|
524 var evt = document.createEvent('Event');
|
|
525 evt.initEvent(type, true, true);
|
|
526 evt.slideNumber = slideNo + 1; // Make it readable
|
|
527 evt.slide = el;
|
|
528
|
|
529 el.dispatchEvent(evt);
|
|
530 };
|
|
531
|
|
532 /**
|
|
533 * @private
|
|
534 */
|
|
535 SlideDeck.prototype.updateSlides_ = function(opt_dontPush) {
|
|
536 var dontPush = opt_dontPush || false;
|
|
537
|
|
538 var curSlide = this.curSlide_;
|
|
539 for (var i = 0; i < this.slides.length; ++i) {
|
|
540 switch (i) {
|
|
541 case curSlide - 2:
|
|
542 this.updateSlideClass_(i, 'far-past');
|
|
543 break;
|
|
544 case curSlide - 1:
|
|
545 this.updateSlideClass_(i, 'past');
|
|
546 break;
|
|
547 case curSlide:
|
|
548 this.updateSlideClass_(i, 'current');
|
|
549 break;
|
|
550 case curSlide + 1:
|
|
551 this.updateSlideClass_(i, 'next');
|
|
552 break;
|
|
553 case curSlide + 2:
|
|
554 this.updateSlideClass_(i, 'far-next');
|
|
555 break;
|
|
556 default:
|
|
557 this.updateSlideClass_(i);
|
|
558 break;
|
|
559 }
|
|
560 };
|
|
561
|
|
562 this.triggerSlideEvent('slideleave', this.prevSlide_);
|
|
563 this.triggerSlideEvent('slideenter', curSlide);
|
|
564
|
|
565 // window.setTimeout(this.disableSlideFrames_.bind(this, curSlide - 2), 301);
|
|
566 //
|
|
567 // this.enableSlideFrames_(curSlide - 1); // Previous slide.
|
|
568 // this.enableSlideFrames_(curSlide + 1); // Current slide.
|
|
569 // this.enableSlideFrames_(curSlide + 2); // Next slide.
|
|
570
|
|
571 // Enable current slide's iframes (needed for page loat at current slide).
|
|
572 this.enableSlideFrames_(curSlide + 1);
|
|
573
|
|
574 // No way to tell when all slide transitions + auto builds are done.
|
|
575 // Give ourselves a good buffer to preload the next slide's iframes.
|
|
576 window.setTimeout(this.enableSlideFrames_.bind(this, curSlide + 2), 1000);
|
|
577
|
|
578 this.updateHash_(dontPush);
|
|
579
|
|
580 if (document.body.classList.contains('overview')) {
|
|
581 this.focusOverview_();
|
|
582 return;
|
|
583 }
|
|
584
|
|
585 };
|
|
586
|
|
587 /**
|
|
588 * @private
|
|
589 * @param {number} slideNo
|
|
590 */
|
|
591 SlideDeck.prototype.enableSlideFrames_ = function(slideNo) {
|
|
592 var el = this.slides[slideNo - 1];
|
|
593 if (!el) {
|
|
594 return;
|
|
595 }
|
|
596
|
|
597 var frames = el.querySelectorAll('iframe');
|
|
598 for (var i = 0, frame; frame = frames[i]; i++) {
|
|
599 this.enableFrame_(frame);
|
|
600 }
|
|
601 };
|
|
602
|
|
603 /**
|
|
604 * @private
|
|
605 * @param {number} slideNo
|
|
606 */
|
|
607 SlideDeck.prototype.enableFrame_ = function(frame) {
|
|
608 var src = frame.dataset.src;
|
|
609 if (src && frame.src != src) {
|
|
610 frame.src = src;
|
|
611 }
|
|
612 };
|
|
613
|
|
614 /**
|
|
615 * @private
|
|
616 * @param {number} slideNo
|
|
617 */
|
|
618 SlideDeck.prototype.disableSlideFrames_ = function(slideNo) {
|
|
619 var el = this.slides[slideNo - 1];
|
|
620 if (!el) {
|
|
621 return;
|
|
622 }
|
|
623
|
|
624 var frames = el.querySelectorAll('iframe');
|
|
625 for (var i = 0, frame; frame = frames[i]; i++) {
|
|
626 this.disableFrame_(frame);
|
|
627 }
|
|
628 };
|
|
629
|
|
630 /**
|
|
631 * @private
|
|
632 * @param {Node} frame
|
|
633 */
|
|
634 SlideDeck.prototype.disableFrame_ = function(frame) {
|
|
635 frame.src = 'about:blank';
|
|
636 };
|
|
637
|
|
638 /**
|
|
639 * @private
|
|
640 * @param {number} slideNo
|
|
641 */
|
|
642 SlideDeck.prototype.getSlideEl_ = function(no) {
|
|
643 if ((no < 0) || (no >= this.slides.length)) {
|
|
644 return null;
|
|
645 } else {
|
|
646 return this.slides[no];
|
|
647 }
|
|
648 };
|
|
649
|
|
650 /**
|
|
651 * @private
|
|
652 * @param {number} slideNo
|
|
653 * @param {string} className
|
|
654 */
|
|
655 SlideDeck.prototype.updateSlideClass_ = function(slideNo, className) {
|
|
656 var el = this.getSlideEl_(slideNo);
|
|
657
|
|
658 if (!el) {
|
|
659 return;
|
|
660 }
|
|
661
|
|
662 if (className) {
|
|
663 el.classList.add(className);
|
|
664 }
|
|
665
|
|
666 for (var i = 0, slideClass; slideClass = this.SLIDE_CLASSES_[i]; ++i) {
|
|
667 if (className != slideClass) {
|
|
668 el.classList.remove(slideClass);
|
|
669 }
|
|
670 }
|
|
671 };
|
|
672
|
|
673 /**
|
|
674 * @private
|
|
675 */
|
|
676 SlideDeck.prototype.makeBuildLists_ = function () {
|
|
677 for (var i = this.curSlide_, slide; slide = this.slides[i]; ++i) {
|
|
678 var items = slide.querySelectorAll('.build > *');
|
|
679 for (var j = 0, item; item = items[j]; ++j) {
|
|
680 if (item.classList) {
|
|
681 item.classList.add('to-build');
|
|
682 if (item.parentNode.classList.contains('fade')) {
|
|
683 item.classList.add('fade');
|
|
684 }
|
|
685 }
|
|
686 }
|
|
687 }
|
|
688 };
|
|
689
|
|
690 /**
|
|
691 * @private
|
|
692 * @param {boolean} dontPush
|
|
693 */
|
|
694 SlideDeck.prototype.updateHash_ = function(dontPush) {
|
|
695 if (!dontPush) {
|
|
696 var slideNo = this.curSlide_ + 1;
|
|
697 var hash = '#' + slideNo;
|
|
698 if (window.history.pushState) {
|
|
699 window.history.pushState(this.curSlide_, 'Slide ' + slideNo, hash);
|
|
700 } else {
|
|
701 window.location.replace(hash);
|
|
702 }
|
|
703
|
|
704 // Record GA hit on this slide.
|
|
705 window['_gaq'] && window['_gaq'].push(['_trackPageview',
|
|
706 document.location.href]);
|
|
707 }
|
|
708 };
|
|
709
|
|
710
|
|
711 /**
|
|
712 * @private
|
|
713 * @param {string} favIcon
|
|
714 */
|
|
715 SlideDeck.prototype.addFavIcon_ = function(favIcon) {
|
|
716 var el = document.createElement('link');
|
|
717 el.rel = 'icon';
|
|
718 el.type = 'image/png';
|
|
719 el.href = favIcon;
|
|
720 document.querySelector('head').appendChild(el);
|
|
721 };
|
|
722
|
|
723 /**
|
|
724 * @private
|
|
725 * @param {string} theme
|
|
726 */
|
|
727 SlideDeck.prototype.loadTheme_ = function(theme) {
|
|
728 var styles = [];
|
|
729 if (theme.constructor.name === 'String') {
|
|
730 styles.push(theme);
|
|
731 } else {
|
|
732 styles = theme;
|
|
733 }
|
|
734
|
|
735 for (var i = 0, style; themeUrl = styles[i]; i++) {
|
|
736 var style = document.createElement('link');
|
|
737 style.rel = 'stylesheet';
|
|
738 style.type = 'text/css';
|
|
739 if (themeUrl.indexOf('http') == -1) {
|
|
740 style.href = this.CSS_DIR_ + themeUrl + '.css';
|
|
741 } else {
|
|
742 style.href = themeUrl;
|
|
743 }
|
|
744 document.querySelector('head').appendChild(style);
|
|
745 }
|
|
746 };
|
|
747
|
|
748 /**
|
|
749 * @private
|
|
750 */
|
|
751 SlideDeck.prototype.loadAnalytics_ = function() {
|
|
752 var _gaq = window['_gaq'] || [];
|
|
753 _gaq.push(['_setAccount', this.config_.settings.analytics]);
|
|
754 _gaq.push(['_trackPageview']);
|
|
755
|
|
756 (function() {
|
|
757 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
|
758 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
|
759 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
|
760 })();
|
|
761 };
|
|
762
|
|
763
|
|
764 // Polyfill missing APIs (if we need to), then create the slide deck.
|
|
765 // iOS < 5 needs classList, dataset, and window.matchMedia. Modernizr contains
|
|
766 // the last one.
|
|
767 (function() {
|
|
768 Modernizr.load({
|
|
769 test: !!document.body.classList && !!document.body.dataset,
|
|
770 nope: ['js/polyfills/classList.min.js', 'js/polyfills/dataset.min.js'],
|
|
771 complete: function() {
|
|
772 window.slidedeck = new SlideDeck();
|
|
773 }
|
|
774 });
|
|
775 })();
|