aboutsummaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/slide-deck.js701
1 files changed, 701 insertions, 0 deletions
diff --git a/js/slide-deck.js b/js/slide-deck.js
new file mode 100644
index 0000000..9ad0290
--- /dev/null
+++ b/js/slide-deck.js
@@ -0,0 +1,701 @@
1document.cancelFullScreen = document.webkitCancelFullScreen ||
2 document.mozCancelFullScreen;
3
4/**
5 * @constructor
6 */
7function SlideDeck(el) {
8 this.curSlide_ = 0;
9 this.prevSlide_ = 0;
10 this.config_ = null;
11 this.container = el || document.querySelector('slides');
12 this.slides = [];
13 this.controller = null;
14
15 this.getCurrentSlideFromHash_();
16
17 // Call this explicitly. Modernizr.load won't be done until after DOM load.
18 this.onDomLoaded_.bind(this)();
19}
20
21/**
22 * @const
23 * @private
24 */
25SlideDeck.prototype.SLIDE_CLASSES_ = [
26 'far-past', 'past', 'current', 'next', 'far-next'];
27
28/**
29 * @const
30 * @private
31 */
32SlideDeck.prototype.CSS_DIR_ = 'theme/css/';
33
34/**
35 * @private
36 */
37SlideDeck.prototype.getCurrentSlideFromHash_ = function() {
38 var slideNo = parseInt(document.location.hash.substr(1));
39
40 if (slideNo) {
41 this.curSlide_ = slideNo - 1;
42 } else {
43 this.curSlide_ = 0;
44 }
45};
46
47/**
48 * @private
49 */
50SlideDeck.prototype.onDomLoaded_ = function(e) {
51 document.body.classList.add('loaded'); // Fade in deck.
52
53 this.slides = this.container.querySelectorAll('slide:not([hidden]):not(.backdrop)');
54
55 // If we're on a smartphone, apply special sauce.
56 if (Modernizr.mq('only screen and (max-device-width: 480px)')) {
57 // var style = document.createElement('link');
58 // style.rel = 'stylesheet';
59 // style.type = 'text/css';
60 // style.href = this.CSS_DIR_ + 'phone.css';
61 // document.querySelector('head').appendChild(style);
62
63 // No need for widescreen layout on a phone.
64 this.container.classList.remove('layout-widescreen');
65 }
66
67 this.loadConfig_(SLIDE_CONFIG);
68 this.addEventListeners_();
69 this.updateSlides_();
70
71 // Add slide numbers and total slide count metadata to each slide.
72 for (var i = 0, slide; slide = this.slides[i]; ++i) {
73 slide.dataset.slideNum = i + 1;
74 slide.dataset.totalSlides = this.slides.length;
75 }
76
77 // Note: this needs to come after addEventListeners_(), which adds a
78 // 'keydown' listener that this controller relies on.
79 // Also, no need to set this up if we're on mobile.
80 if (!Modernizr.touch) {
81 this.controller = new SlideController(this);
82 if (this.controller.isPopup) {
83 document.body.classList.add('popup');
84 }
85 }
86};
87
88/**
89 * @private
90 */
91SlideDeck.prototype.addEventListeners_ = function() {
92 document.addEventListener('keydown', this.onBodyKeyDown_.bind(this), false);
93 window.addEventListener('popstate', this.onPopState_.bind(this), false);
94
95 // var transEndEventNames = {
96 // 'WebkitTransition': 'webkitTransitionEnd',
97 // 'MozTransition': 'transitionend',
98 // 'OTransition': 'oTransitionEnd',
99 // 'msTransition': 'MSTransitionEnd',
100 // 'transition': 'transitionend'
101 // };
102 //
103 // // Find the correct transitionEnd vendor prefix.
104 // window.transEndEventName = transEndEventNames[
105 // Modernizr.prefixed('transition')];
106 //
107 // // When slides are done transitioning, kickoff loading iframes.
108 // // Note: we're only looking at a single transition (on the slide). This
109 // // doesn't include autobuilds the slides may have. Also, if the slide
110 // // transitions on multiple properties (e.g. not just 'all'), this doesn't
111 // // handle that case.
112 // this.container.addEventListener(transEndEventName, function(e) {
113 // this.enableSlideFrames_(this.curSlide_);
114 // }.bind(this), false);
115
116 // document.addEventListener('slideenter', function(e) {
117 // var slide = e.target;
118 // window.setTimeout(function() {
119 // this.enableSlideFrames_(e.slideNumber);
120 // this.enableSlideFrames_(e.slideNumber + 1);
121 // }.bind(this), 300);
122 // }.bind(this), false);
123};
124
125/**
126 * @private
127 * @param {Event} e The pop event.
128 */
129SlideDeck.prototype.onPopState_ = function(e) {
130 if (e.state != null) {
131 this.curSlide_ = e.state;
132 this.updateSlides_(true);
133 }
134};
135
136/**
137 * @param {Event} e
138 */
139SlideDeck.prototype.onBodyKeyDown_ = function(e) {
140 if (/^(input|textarea)$/i.test(e.target.nodeName) ||
141 e.target.isContentEditable) {
142 return;
143 }
144
145 // Forward keydowns to the main slides if we're the popup.
146 if (this.controller && this.controller.isPopup) {
147 this.controller.sendMsg({keyCode: e.keyCode});
148 }
149
150 switch (e.keyCode) {
151 case 39: // right arrow
152 case 32: // space
153 case 34: // PgDn
154 this.nextSlide();
155 e.preventDefault();
156 break;
157
158 case 37: // left arrow
159 case 8: // Backspace
160 case 33: // PgUp
161 this.prevSlide();
162 e.preventDefault();
163 break;
164
165 case 40: // down arrow
166 //if (this.isChromeVoxActive()) {
167 // speakNextItem();
168 //} else {
169 this.nextSlide();
170 //}
171 e.preventDefault();
172 break;
173
174 case 38: // up arrow
175 //if (this.isChromeVoxActive()) {
176 // speakPrevItem();
177 //} else {
178 this.prevSlide();
179 //}
180 e.preventDefault();
181 break;
182
183 case 72: // H
184 document.body.classList.toggle('highlight-code');
185 break;
186
187 case 80: // P
188 if (this.controller && this.controller.isPopup) {
189 document.body.classList.toggle('with-notes');
190 } else if (this.controller && !this.controller.popup) {
191 document.body.classList.toggle('with-notes');
192 }
193 break;
194
195 case 82: // R
196 // TODO: implement refresh on main slides when popup is refreshed.
197 break;
198
199 case 27: // ESC
200 document.body.classList.remove('with-notes');
201 document.body.classList.remove('highlight-code');
202 break;
203
204 case 70: // F
205 // Only respect 'f' on body. Don't want to capture keys from an <input>.
206 // Also, ignore browser's fullscreen shortcut (cmd+shift+f) so we don't
207 // get trapped in fullscreen!
208 if (e.target == document.body && !(e.shiftKey && e.metaKey)) {
209 if (document.mozFullScreen !== undefined && !document.mozFullScreen) {
210 document.body.mozRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
211 } else if (document.webkitIsFullScreen !== undefined && !document.webkitIsFullScreen) {
212 document.body.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
213 } else {
214 document.cancelFullScreen();
215 }
216 }
217 break;
218 }
219};
220
221/**
222 * @private
223 */
224SlideDeck.prototype.loadConfig_ = function(config) {
225 if (!config) {
226 return;
227 }
228
229 this.config_ = config;
230
231 var settings = this.config_.settings;
232
233 this.loadTheme_(settings.theme || []);
234
235 if (settings.favIcon) {
236 this.addFavIcon_(settings.favIcon);
237 }
238
239 // Prettyprint. Default to on.
240 if (!!!('usePrettify' in settings) || settings.usePrettify) {
241 prettyPrint();
242 }
243
244 if (settings.analytics) {
245 this.loadAnalytics_();
246 }
247
248 if (settings.fonts) {
249 this.addFonts_(settings.fonts);
250 }
251
252 // Builds. Default to on.
253 if (!!!('useBuilds' in settings) || settings.useBuilds) {