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