Mercurial > hg > Papers > 2013 > nobuyasu-jssst
comparison presen/slides.js @ 48:5dadfd75cfb0
add presen
author | Nobuyasu Oshiro <dimolto@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 09 Sep 2013 09:03:33 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
47:533180cea89f | 48:5dadfd75cfb0 |
---|---|
1 /* | |
2 Google HTML5 slides template | |
3 | |
4 Authors: Luke Mahé (code) | |
5 Marcin Wichary (code and design) | |
6 | |
7 Dominic Mazzoni (browser compatibility) | |
8 Charles Chen (ChromeVox support) | |
9 | |
10 URL: http://code.google.com/p/html5slides/ | |
11 */ | |
12 | |
13 // var PERMANENT_URL_PREFIX = 'http://html5slides.googlecode.com/svn/trunk/'; | |
14 var PERMANENT_URL_PREFIX = './'; | |
15 | |
16 var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next']; | |
17 | |
18 var PM_TOUCH_SENSITIVITY = 15; | |
19 | |
20 var curSlide; | |
21 | |
22 /* ---------------------------------------------------------------------- */ | |
23 /* classList polyfill by Eli Grey | |
24 * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */ | |
25 | |
26 if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) { | |
27 | |
28 (function (view) { | |
29 | |
30 var | |
31 classListProp = "classList" | |
32 , protoProp = "prototype" | |
33 , elemCtrProto = (view.HTMLElement || view.Element)[protoProp] | |
34 , objCtr = Object | |
35 strTrim = String[protoProp].trim || function () { | |
36 return this.replace(/^\s+|\s+$/g, ""); | |
37 } | |
38 , arrIndexOf = Array[protoProp].indexOf || function (item) { | |
39 for (var i = 0, len = this.length; i < len; i++) { | |
40 if (i in this && this[i] === item) { | |
41 return i; | |
42 } | |
43 } | |
44 return -1; | |
45 } | |
46 // Vendors: please allow content code to instantiate DOMExceptions | |
47 , DOMEx = function (type, message) { | |
48 this.name = type; | |
49 this.code = DOMException[type]; | |
50 this.message = message; | |
51 } | |
52 , checkTokenAndGetIndex = function (classList, token) { | |
53 if (token === "") { | |
54 throw new DOMEx( | |
55 "SYNTAX_ERR" | |
56 , "An invalid or illegal string was specified" | |
57 ); | |
58 } | |
59 if (/\s/.test(token)) { | |
60 throw new DOMEx( | |
61 "INVALID_CHARACTER_ERR" | |
62 , "String contains an invalid character" | |
63 ); | |
64 } | |
65 return arrIndexOf.call(classList, token); | |
66 } | |
67 , ClassList = function (elem) { | |
68 var | |
69 trimmedClasses = strTrim.call(elem.className) | |
70 , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] | |
71 ; | |
72 for (var i = 0, len = classes.length; i < len; i++) { | |
73 this.push(classes[i]); | |
74 } | |
75 this._updateClassName = function () { | |
76 elem.className = this.toString(); | |
77 }; | |
78 } | |
79 , classListProto = ClassList[protoProp] = [] | |
80 , classListGetter = function () { | |
81 return new ClassList(this); | |
82 } | |
83 ; | |
84 // Most DOMException implementations don't allow calling DOMException's toString() | |
85 // on non-DOMExceptions. Error's toString() is sufficient here. | |
86 DOMEx[protoProp] = Error[protoProp]; | |
87 classListProto.item = function (i) { | |
88 return this[i] || null; | |
89 }; | |
90 classListProto.contains = function (token) { | |
91 token += ""; | |
92 return checkTokenAndGetIndex(this, token) !== -1; | |
93 }; | |
94 classListProto.add = function (token) { | |
95 token += ""; | |
96 if (checkTokenAndGetIndex(this, token) === -1) { | |
97 this.push(token); | |
98 this._updateClassName(); | |
99 } | |
100 }; | |
101 classListProto.remove = function (token) { | |
102 token += ""; | |
103 var index = checkTokenAndGetIndex(this, token); | |
104 if (index !== -1) { | |
105 this.splice(index, 1); | |
106 this._updateClassName(); | |
107 } | |
108 }; | |
109 classListProto.toggle = function (token) { | |
110 token += ""; | |
111 if (checkTokenAndGetIndex(this, token) === -1) { | |
112 this.add(token); | |
113 } else { | |
114 this.remove(token); | |
115 } | |
116 }; | |
117 classListProto.toString = function () { | |
118 return this.join(" "); | |
119 }; | |
120 | |
121 if (objCtr.defineProperty) { | |
122 var classListPropDesc = { | |
123 get: classListGetter | |
124 , enumerable: true | |
125 , configurable: true | |
126 }; | |
127 try { | |
128 objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); | |
129 } catch (ex) { // IE 8 doesn't support enumerable:true | |
130 if (ex.number === -0x7FF5EC54) { | |
131 classListPropDesc.enumerable = false; | |
132 objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); | |
133 } | |
134 } | |
135 } else if (objCtr[protoProp].__defineGetter__) { | |
136 elemCtrProto.__defineGetter__(classListProp, classListGetter); | |
137 } | |
138 | |
139 }(self)); | |
140 | |
141 } | |
142 /* ---------------------------------------------------------------------- */ | |
143 | |
144 /* Slide movement */ | |
145 | |
146 function getSlideEl(no) { | |
147 if ((no < 0) || (no >= slideEls.length)) { | |
148 return null; | |
149 } else { | |
150 return slideEls[no]; | |
151 } | |
152 }; | |
153 | |
154 function updateSlideClass(slideNo, className) { | |
155 var el = getSlideEl(slideNo); | |
156 | |
157 if (!el) { | |
158 return; | |
159 } | |
160 | |
161 if (className) { | |
162 el.classList.add(className); | |
163 } | |
164 | |
165 for (var i in SLIDE_CLASSES) { | |
166 if (className != SLIDE_CLASSES[i]) { | |
167 el.classList.remove(SLIDE_CLASSES[i]); | |
168 } | |
169 } | |
170 }; | |
171 | |
172 function updateSlides() { | |
173 for (var i = 0; i < slideEls.length; i++) { | |
174 switch (i) { | |
175 case curSlide - 2: | |
176 updateSlideClass(i, 'far-past'); | |
177 break; | |
178 case curSlide - 1: | |
179 updateSlideClass(i, 'past'); | |
180 break; | |
181 case curSlide: | |
182 updateSlideClass(i, 'current'); | |
183 break; | |
184 case curSlide + 1: | |
185 updateSlideClass(i, 'next'); | |
186 break; | |
187 case curSlide + 2: | |
188 updateSlideClass(i, 'far-next'); | |
189 break; | |
190 default: | |
191 updateSlideClass(i); | |
192 break; | |
193 } | |
194 } | |
195 | |
196 triggerLeaveEvent(curSlide - 1); | |
197 triggerEnterEvent(curSlide); | |
198 | |
199 window.setTimeout(function() { | |
200 // Hide after the slide | |
201 disableSlideFrames(curSlide - 2); | |
202 }, 301); | |
203 | |
204 enableSlideFrames(curSlide - 1); | |
205 enableSlideFrames(curSlide + 2); | |
206 | |
207 if (isChromeVoxActive()) { | |
208 speakAndSyncToNode(slideEls[curSlide]); | |
209 } | |
210 | |
211 updateHash(); | |
212 }; | |
213 | |
214 function buildNextItem() { | |
215 var toBuild = slideEls[curSlide].querySelectorAll('.to-build'); | |
216 | |
217 if (!toBuild.length) { | |
218 return false; | |
219 } | |
220 | |
221 toBuild[0].classList.remove('to-build', ''); | |
222 | |
223 if (isChromeVoxActive()) { | |
224 speakAndSyncToNode(toBuild[0]); | |
225 } | |
226 | |
227 return true; | |
228 }; | |
229 | |
230 function prevSlide() { | |
231 if (curSlide > 0) { | |
232 curSlide--; | |
233 | |
234 updateSlides(); | |
235 } | |
236 }; | |
237 | |
238 function nextSlide() { | |
239 if (buildNextItem()) { | |
240 return; | |
241 } | |
242 | |
243 if (curSlide < slideEls.length - 1) { | |
244 curSlide++; | |
245 | |
246 updateSlides(); | |
247 } | |
248 }; | |
249 | |
250 /* Slide events */ | |
251 | |
252 function triggerEnterEvent(no) { | |
253 var el = getSlideEl(no); | |
254 if (!el) { | |
255 return; | |
256 } | |
257 | |
258 var onEnter = el.getAttribute('onslideenter'); | |
259 if (onEnter) { | |
260 new Function(onEnter).call(el); | |
261 } | |
262 | |
263 var evt = document.createEvent('Event'); | |
264 evt.initEvent('slideenter', true, true); | |
265 evt.slideNumber = no + 1; // Make it readable | |
266 | |
267 el.dispatchEvent(evt); | |
268 }; | |
269 | |
270 function triggerLeaveEvent(no) { | |
271 var el = getSlideEl(no); | |
272 if (!el) { | |
273 return; | |
274 } | |
275 | |
276 var onLeave = el.getAttribute('onslideleave'); | |
277 if (onLeave) { | |
278 new Function(onLeave).call(el); | |
279 } | |
280 | |
281 var evt = document.createEvent('Event'); | |
282 evt.initEvent('slideleave', true, true); | |
283 evt.slideNumber = no + 1; // Make it readable | |
284 | |
285 el.dispatchEvent(evt); | |
286 }; | |
287 | |
288 /* Touch events */ | |
289 | |
290 function handleTouchStart(event) { | |
291 if (event.touches.length == 1) { | |
292 touchDX = 0; | |
293 touchDY = 0; | |
294 | |
295 touchStartX = event.touches[0].pageX; | |
296 touchStartY = event.touches[0].pageY; | |
297 | |
298 document.body.addEventListener('touchmove', handleTouchMove, true); | |
299 document.body.addEventListener('touchend', handleTouchEnd, true); | |
300 } | |
301 }; | |
302 | |
303 function handleTouchMove(event) { | |
304 if (event.touches.length > 1) { | |
305 cancelTouch(); | |
306 } else { | |
307 touchDX = event.touches[0].pageX - touchStartX; | |
308 touchDY = event.touches[0].pageY - touchStartY; | |
309 } | |
310 }; | |
311 | |
312 function handleTouchEnd(event) { | |
313 var dx = Math.abs(touchDX); | |
314 var dy = Math.abs(touchDY); | |
315 | |
316 if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) { | |
317 if (touchDX > 0) { | |
318 prevSlide(); | |
319 } else { | |
320 nextSlide(); | |
321 } | |
322 } | |
323 | |
324 cancelTouch(); | |
325 }; | |
326 | |
327 function cancelTouch() { | |
328 document.body.removeEventListener('touchmove', handleTouchMove, true); | |
329 document.body.removeEventListener('touchend', handleTouchEnd, true); | |
330 }; | |
331 | |
332 /* Preloading frames */ | |
333 | |
334 function disableSlideFrames(no) { | |
335 var el = getSlideEl(no); | |
336 if (!el) { | |
337 return; | |
338 } | |
339 | |
340 var frames = el.getElementsByTagName('iframe'); | |
341 for (var i = 0, frame; frame = frames[i]; i++) { | |
342 disableFrame(frame); | |
343 } | |
344 }; | |
345 | |
346 function enableSlideFrames(no) { | |
347 var el = getSlideEl(no); | |
348 if (!el) { | |
349 return; | |
350 } | |
351 | |
352 var frames = el.getElementsByTagName('iframe'); | |
353 for (var i = 0, frame; frame = frames[i]; i++) { | |
354 enableFrame(frame); | |
355 } | |
356 }; | |
357 | |
358 function disableFrame(frame) { | |
359 frame.src = 'about:blank'; | |
360 }; | |
361 | |
362 function enableFrame(frame) { | |
363 var src = frame._src; | |
364 | |
365 if (frame.src != src && src != 'about:blank') { | |
366 frame.src = src; | |
367 } | |
368 }; | |
369 | |
370 function setupFrames() { | |
371 var frames = document.querySelectorAll('iframe'); | |
372 for (var i = 0, frame; frame = frames[i]; i++) { | |
373 frame._src = frame.src; | |
374 disableFrame(frame); | |
375 } | |
376 | |
377 enableSlideFrames(curSlide); | |
378 enableSlideFrames(curSlide + 1); | |
379 enableSlideFrames(curSlide + 2); | |
380 }; | |
381 | |
382 function setupInteraction() { | |
383 /* Clicking and tapping */ | |
384 | |
385 var el = document.createElement('div'); | |
386 el.className = 'slide-area'; | |
387 el.id = 'prev-slide-area'; | |
388 el.addEventListener('click', prevSlide, false); | |
389 document.querySelector('section.slides').appendChild(el); | |
390 | |
391 var el = document.createElement('div'); | |
392 el.className = 'slide-area'; | |
393 el.id = 'next-slide-area'; | |
394 el.addEventListener('click', nextSlide, false); | |
395 document.querySelector('section.slides').appendChild(el); | |
396 | |
397 /* Swiping */ | |
398 | |
399 document.body.addEventListener('touchstart', handleTouchStart, false); | |
400 } | |
401 | |
402 /* ChromeVox support */ | |
403 | |
404 function isChromeVoxActive() { | |
405 if (typeof(cvox) == 'undefined') { | |
406 return false; | |
407 } else { | |
408 return true; | |
409 } | |
410 }; | |
411 | |
412 function speakAndSyncToNode(node) { | |
413 if (!isChromeVoxActive()) { | |
414 return; | |
415 } | |
416 | |
417 cvox.ChromeVox.navigationManager.switchToStrategy( | |
418 cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true); | |
419 cvox.ChromeVox.navigationManager.syncToNode(node); | |
420 cvox.ChromeVoxUserCommands.finishNavCommand(''); | |
421 var target = node; | |
422 while (target.firstChild) { | |
423 target = target.firstChild; | |
424 } | |
425 cvox.ChromeVox.navigationManager.syncToNode(target); | |
426 }; | |
427 | |
428 function speakNextItem() { | |
429 if (!isChromeVoxActive()) { | |
430 return; | |
431 } | |
432 | |
433 cvox.ChromeVox.navigationManager.switchToStrategy( | |
434 cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true); | |
435 cvox.ChromeVox.navigationManager.next(true); | |
436 if (!cvox.DomUtil.isDescendantOfNode( | |
437 cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])){ | |
438 var target = slideEls[curSlide]; | |
439 while (target.firstChild) { | |
440 target = target.firstChild; | |
441 } | |
442 cvox.ChromeVox.navigationManager.syncToNode(target); | |
443 cvox.ChromeVox.navigationManager.next(true); | |
444 } | |
445 cvox.ChromeVoxUserCommands.finishNavCommand(''); | |
446 }; | |
447 | |
448 function speakPrevItem() { | |
449 if (!isChromeVoxActive()) { | |
450 return; | |
451 } | |
452 | |
453 cvox.ChromeVox.navigationManager.switchToStrategy( | |
454 cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true); | |
455 cvox.ChromeVox.navigationManager.previous(true); | |
456 if (!cvox.DomUtil.isDescendantOfNode( | |
457 cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])){ | |
458 var target = slideEls[curSlide]; | |
459 while (target.lastChild){ | |
460 target = target.lastChild; | |
461 } | |
462 cvox.ChromeVox.navigationManager.syncToNode(target); | |
463 cvox.ChromeVox.navigationManager.previous(true); | |
464 } | |
465 cvox.ChromeVoxUserCommands.finishNavCommand(''); | |
466 }; | |
467 | |
468 /* Hash functions */ | |
469 | |
470 function getCurSlideFromHash() { | |
471 var slideNo = parseInt(location.hash.substr(1)); | |
472 | |
473 if (slideNo) { | |
474 curSlide = slideNo - 1; | |
475 } else { | |
476 curSlide = 0; | |
477 } | |
478 }; | |
479 | |
480 function updateHash() { | |
481 location.replace('#' + (curSlide + 1)); | |
482 }; | |
483 | |
484 /* Event listeners */ | |
485 | |
486 function handleBodyKeyDown(event) { | |
487 switch (event.keyCode) { | |
488 case 39: // right arrow | |
489 case 13: // Enter | |
490 case 32: // space | |
491 case 34: // PgDn | |
492 nextSlide(); | |
493 event.preventDefault(); | |
494 break; | |
495 | |
496 case 37: // left arrow | |
497 case 8: // Backspace | |
498 case 33: // PgUp | |
499 prevSlide(); | |
500 event.preventDefault(); | |
501 break; | |
502 | |
503 case 40: // down arrow | |
504 if (isChromeVoxActive()) { | |
505 speakNextItem(); | |
506 } else { | |
507 nextSlide(); | |
508 } | |
509 event.preventDefault(); | |
510 break; | |
511 | |
512 case 38: // up arrow | |
513 if (isChromeVoxActive()) { | |
514 speakPrevItem(); | |
515 } else { | |
516 prevSlide(); | |
517 } | |
518 event.preventDefault(); | |
519 break; | |
520 } | |
521 }; | |
522 | |
523 function addEventListeners() { | |
524 document.addEventListener('keydown', handleBodyKeyDown, false); | |
525 }; | |
526 | |
527 /* Initialization */ | |
528 | |
529 function addPrettify() { | |
530 var els = document.querySelectorAll('pre'); | |
531 for (var i = 0, el; el = els[i]; i++) { | |
532 if (!el.classList.contains('noprettyprint')) { | |
533 el.classList.add('prettyprint'); | |
534 } | |
535 } | |
536 | |
537 var el = document.createElement('script'); | |
538 el.type = 'text/javascript'; | |
539 el.src = PERMANENT_URL_PREFIX + 'prettify.js'; | |
540 el.onload = function() { | |
541 prettyPrint(); | |
542 } | |
543 document.body.appendChild(el); | |
544 }; | |
545 | |
546 function addFontStyle() { | |
547 var el = document.createElement('link'); | |
548 el.rel = 'stylesheet'; | |
549 el.type = 'text/css'; | |
550 el.href = 'http://fonts.googleapis.com/css?family=' + | |
551 'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono'; | |
552 | |
553 document.body.appendChild(el); | |
554 }; | |
555 | |
556 function addGeneralStyle() { | |
557 var el = document.createElement('link'); | |
558 el.rel = 'stylesheet'; | |
559 el.type = 'text/css'; | |
560 el.href = PERMANENT_URL_PREFIX + 'styles.css'; | |
561 document.body.appendChild(el); | |
562 | |
563 var el = document.createElement('meta'); | |
564 el.name = 'viewport'; | |
565 el.content = 'width=1100,height=750'; | |
566 document.querySelector('head').appendChild(el); | |
567 | |
568 var el = document.createElement('meta'); | |
569 el.name = 'apple-mobile-web-app-capable'; | |
570 el.content = 'yes'; | |
571 document.querySelector('head').appendChild(el); | |
572 }; | |
573 | |
574 function makeBuildLists() { | |
575 for (var i = curSlide, slide; slide = slideEls[i]; i++) { | |
576 var items = slide.querySelectorAll('.build > *'); | |
577 for (var j = 0, item; item = items[j]; j++) { | |
578 if (item.classList) { | |
579 item.classList.add('to-build'); | |
580 } | |
581 } | |
582 } | |
583 }; | |
584 | |
585 function handleDomLoaded() { | |
586 slideEls = document.querySelectorAll('section.slides > article'); | |
587 | |
588 setupFrames(); | |
589 | |
590 addFontStyle(); | |
591 addGeneralStyle(); | |
592 addPrettify(); | |
593 addEventListeners(); | |
594 | |
595 updateSlides(); | |
596 | |
597 setupInteraction(); | |
598 makeBuildLists(); | |
599 | |
600 document.body.classList.add('loaded'); | |
601 }; | |
602 | |
603 function initialize() { | |
604 getCurSlideFromHash(); | |
605 | |
606 if (window['_DEBUG']) { | |
607 PERMANENT_URL_PREFIX = '../'; | |
608 } | |
609 | |
610 if (window['_DCL']) { | |
611 handleDomLoaded(); | |
612 } else { | |
613 document.addEventListener('DOMContentLoaded', handleDomLoaded, false); | |
614 } | |
615 } | |
616 | |
617 // If ?debug exists then load the script relative instead of absolute | |
618 if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) { | |
619 document.addEventListener('DOMContentLoaded', function() { | |
620 // Avoid missing the DomContentLoaded event | |
621 window['_DCL'] = true | |
622 }, false); | |
623 | |
624 window['_DEBUG'] = true; | |
625 var script = document.createElement('script'); | |
626 script.type = 'text/javascript'; | |
627 script.src = '../slides.js'; | |
628 var s = document.getElementsByTagName('script')[0]; | |
629 s.parentNode.insertBefore(script, s); | |
630 | |
631 // Remove this script | |
632 s.parentNode.removeChild(s); | |
633 } else { | |
634 initialize(); | |
635 } |