slides.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. /*
  2. Google HTML5 slides template
  3. Authors: Luke Mahé (code)
  4. Marcin Wichary (code and design)
  5. Dominic Mazzoni (browser compatibility)
  6. Charles Chen (ChromeVox support)
  7. URL: http://code.google.com/p/html5slides/
  8. Contributors:
  9. Benjamin Erb (presenter mode, notes, structuring)
  10. URL: https://github.com/berb/html5slides-uulm
  11. */
  12. var SLIDE_CLASSES = [ 'far-past', 'past', 'current', 'next', 'far-next' ];
  13. var PM_TOUCH_SENSITIVITY = 15;
  14. var curSlide;
  15. var showPresenterNotes = false;
  16. var isPresenterSlave = false;
  17. var presenterSlaveWindow = null;
  18. var doTransitions = true;
  19. /* ---------------------------------------------------------------------- */
  20. /*
  21. * classList polyfill by Eli Grey
  22. * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js)
  23. */
  24. if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
  25. (function(view) {
  26. var classListProp = "classList", protoProp = "prototype", elemCtrProto = (view.HTMLElement || view.Element)[protoProp], objCtr = Object
  27. strTrim = String[protoProp].trim || function() {
  28. return this.replace(/^\s+|\s+$/g, "");
  29. }, arrIndexOf = Array[protoProp].indexOf || function(item) {
  30. for ( var i = 0, len = this.length; i < len; i++) {
  31. if (i in this && this[i] === item) {
  32. return i;
  33. }
  34. }
  35. return -1;
  36. }
  37. // Vendors: please allow content code to instantiate DOMExceptions
  38. , DOMEx = function(type, message) {
  39. this.name = type;
  40. this.code = DOMException[type];
  41. this.message = message;
  42. }, checkTokenAndGetIndex = function(classList, token) {
  43. if (token === "") {
  44. throw new DOMEx("SYNTAX_ERR", "An invalid or illegal string was specified");
  45. }
  46. if (/\s/.test(token)) {
  47. throw new DOMEx("INVALID_CHARACTER_ERR", "String contains an invalid character");
  48. }
  49. return arrIndexOf.call(classList, token);
  50. }, ClassList = function(elem) {
  51. var trimmedClasses = strTrim.call(elem.className), classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [];
  52. for ( var i = 0, len = classes.length; i < len; i++) {
  53. this.push(classes[i]);
  54. }
  55. this._updateClassName = function() {
  56. elem.className = this.toString();
  57. };
  58. }, classListProto = ClassList[protoProp] = [], classListGetter = function() {
  59. return new ClassList(this);
  60. };
  61. // Most DOMException implementations don't allow calling DOMException's
  62. // toString()
  63. // on non-DOMExceptions. Error's toString() is sufficient here.
  64. DOMEx[protoProp] = Error[protoProp];
  65. classListProto.item = function(i) {
  66. return this[i] || null;
  67. };
  68. classListProto.contains = function(token) {
  69. token += "";
  70. return checkTokenAndGetIndex(this, token) !== -1;
  71. };
  72. classListProto.add = function(token) {
  73. token += "";
  74. if (checkTokenAndGetIndex(this, token) === -1) {
  75. this.push(token);
  76. this._updateClassName();
  77. }
  78. };
  79. classListProto.remove = function(token) {
  80. token += "";
  81. var index = checkTokenAndGetIndex(this, token);
  82. if (index !== -1) {
  83. this.splice(index, 1);
  84. this._updateClassName();
  85. }
  86. };
  87. classListProto.toggle = function(token) {
  88. token += "";
  89. if (checkTokenAndGetIndex(this, token) === -1) {
  90. this.add(token);
  91. }
  92. else {
  93. this.remove(token);
  94. }
  95. };
  96. classListProto.toString = function() {
  97. return this.join(" ");
  98. };
  99. if (objCtr.defineProperty) {
  100. var classListPropDesc = {
  101. get : classListGetter,
  102. enumerable : true,
  103. configurable : true
  104. };
  105. try {
  106. objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
  107. }
  108. catch (ex) { // IE 8 doesn't support enumerable:true
  109. if (ex.number === -0x7FF5EC54) {
  110. classListPropDesc.enumerable = false;
  111. objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
  112. }
  113. }
  114. }
  115. else if (objCtr[protoProp].__defineGetter__) {
  116. elemCtrProto.__defineGetter__(classListProp, classListGetter);
  117. }
  118. }(self));
  119. }
  120. /* ---------------------------------------------------------------------- */
  121. /* Slide movement */
  122. function getSlideEl(no) {
  123. if ((no < 0) || (no >= slideEls.length)) {
  124. return null;
  125. }
  126. else {
  127. return slideEls[no];
  128. }
  129. };
  130. function updateSlideClass(slideNo, className) {
  131. var el = getSlideEl(slideNo);
  132. if (!el) {
  133. return;
  134. }
  135. if (className) {
  136. el.classList.add(className);
  137. }
  138. for ( var i in SLIDE_CLASSES) {
  139. if (className != SLIDE_CLASSES[i]) {
  140. el.classList.remove(SLIDE_CLASSES[i]);
  141. }
  142. }
  143. };
  144. function updateSlides() {
  145. for ( var i = 0; i < slideEls.length; i++) {
  146. switch (i) {
  147. case curSlide - 2:
  148. updateSlideClass(i, 'far-past');
  149. break;
  150. case curSlide - 1:
  151. updateSlideClass(i, 'past');
  152. break;
  153. case curSlide:
  154. updateSlideClass(i, 'current');
  155. break;
  156. case curSlide + 1:
  157. updateSlideClass(i, 'next');
  158. break;
  159. case curSlide + 2:
  160. updateSlideClass(i, 'far-next');
  161. break;
  162. default:
  163. updateSlideClass(i);
  164. break;
  165. }
  166. }
  167. triggerLeaveEvent(curSlide - 1);
  168. triggerEnterEvent(curSlide);
  169. window.setTimeout(function() {
  170. // Hide after the slide
  171. disableSlideFrames(curSlide - 2);
  172. }, 301);
  173. enableSlideFrames(curSlide - 1);
  174. enableSlideFrames(curSlide + 2);
  175. if (isChromeVoxActive()) {
  176. speakAndSyncToNode(slideEls[curSlide]);
  177. }
  178. updateHash();
  179. if (presenterSlaveWindow !== null) {
  180. presenterSlaveWindow.postMessage("" + curSlide, "*");
  181. }
  182. };
  183. function buildNextItem() {
  184. var toBuild = slideEls[curSlide].querySelectorAll('.to-build');
  185. if (!toBuild.length) {
  186. return false;
  187. }
  188. toBuild[0].classList.remove('to-build', '');
  189. if (isChromeVoxActive()) {
  190. speakAndSyncToNode(toBuild[0]);
  191. }
  192. return true;
  193. };
  194. function prevSlide() {
  195. if (curSlide > 0) {
  196. curSlide--;
  197. updateSlides();
  198. }
  199. };
  200. function nextSlide() {
  201. if (buildNextItem()) {
  202. return;
  203. }
  204. if (curSlide < slideEls.length - 1) {
  205. curSlide++;
  206. updateSlides();
  207. }
  208. };
  209. function goToSlide(target) {
  210. if (target >= 0 && target <= slideEls.length) {
  211. var steps = target - (curSlide + 1);
  212. while (steps !== 0) {
  213. if (steps > 0) {
  214. curSlide++;
  215. steps--;
  216. }
  217. else if (steps < 0) {
  218. curSlide--;
  219. steps++;
  220. }
  221. }
  222. updateSlides();
  223. }
  224. };
  225. function togglePresenterNotes() {
  226. if (showPresenterNotes === false) {
  227. var section = document.querySelector("div.slides");
  228. section.classList.add('presenter');
  229. }
  230. else {
  231. var section = document.querySelector("div.slides");
  232. section.classList.remove('presenter');
  233. }
  234. showPresenterNotes = !showPresenterNotes;
  235. };
  236. function toggleTransitions() {
  237. if (doTransitions === false) {
  238. var section = document.querySelector("div.slides");
  239. section.classList.remove('no-trans');
  240. }
  241. else {
  242. var section = document.querySelector("div.slides");
  243. section.classList.add('no-trans');
  244. }
  245. doTransitions = !doTransitions;
  246. };
  247. /* Slide events */
  248. function triggerEnterEvent(no) {
  249. var el = getSlideEl(no);
  250. if (!el) {
  251. return;
  252. }
  253. var onEnter = el.getAttribute('onslideenter');
  254. if (onEnter) {
  255. new Function(onEnter).call(el);
  256. }
  257. var evt = document.createEvent('Event');
  258. evt.initEvent('slideenter', true, true);
  259. evt.slideNumber = no + 1; // Make it readable
  260. el.dispatchEvent(evt);
  261. };
  262. function triggerLeaveEvent(no) {
  263. var el = getSlideEl(no);
  264. if (!el) {
  265. return;
  266. }
  267. var onLeave = el.getAttribute('onslideleave');
  268. if (onLeave) {
  269. new Function(onLeave).call(el);
  270. }
  271. var evt = document.createEvent('Event');
  272. evt.initEvent('slideleave', true, true);
  273. evt.slideNumber = no + 1; // Make it readable
  274. el.dispatchEvent(evt);
  275. };
  276. /* Touch events */
  277. function handleTouchStart(event) {
  278. if (event.touches.length == 1) {
  279. touchDX = 0;
  280. touchDY = 0;
  281. touchStartX = event.touches[0].pageX;
  282. touchStartY = event.touches[0].pageY;
  283. document.body.addEventListener('touchmove', handleTouchMove, true);
  284. document.body.addEventListener('touchend', handleTouchEnd, true);
  285. }
  286. };
  287. function handleTouchMove(event) {
  288. if (event.touches.length > 1) {
  289. cancelTouch();
  290. }
  291. else {
  292. touchDX = event.touches[0].pageX - touchStartX;
  293. touchDY = event.touches[0].pageY - touchStartY;
  294. }
  295. };
  296. function handleTouchEnd(event) {
  297. var dx = Math.abs(touchDX);
  298. var dy = Math.abs(touchDY);
  299. if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {
  300. if (touchDX > 0) {
  301. prevSlide();
  302. }
  303. else {
  304. nextSlide();
  305. }
  306. }
  307. cancelTouch();
  308. };
  309. function cancelTouch() {
  310. document.body.removeEventListener('touchmove', handleTouchMove, true);
  311. document.body.removeEventListener('touchend', handleTouchEnd, true);
  312. };
  313. /* Fadings */
  314. function toggleFade(type){
  315. var fadeDiv = document.querySelector('#fadeDiv');
  316. if(fadeDiv === null){
  317. fadeDiv = document.createElement('div');
  318. fadeDiv.id = "fadeDiv";
  319. fadeDiv.className = type;
  320. document.body.appendChild(fadeDiv);
  321. }
  322. else if(fadeDiv.className !== type){
  323. fadeDiv.className = type;
  324. }
  325. if(fadeDiv.style.display === "block"){
  326. fadeDiv.style.display = "none";
  327. }
  328. else{
  329. fadeDiv.style.display = "block";
  330. }
  331. }
  332. function toggleBlackOut(){
  333. toggleFade("blackfade");
  334. }
  335. function toggleWhiteOut(){
  336. toggleFade("whitefade");
  337. }
  338. function processToc(){
  339. var sections = [];
  340. var els = document.querySelectorAll('.slides section');
  341. for(var i = 0;i<els.length;i++){
  342. var slideSpan = els[i].querySelector('article footer span.slide-nr');
  343. var lastSlideSpan = els[i].querySelector('article:last-child footer span.slide-nr');
  344. var header = els[i].querySelector('header');
  345. if(slideSpan && lastSlideSpan && header){
  346. var slideNr = slideSpan.innerHTML;
  347. var lastSlideNr = lastSlideSpan.innerHTML;
  348. var headerTitle = header.innerHTML;
  349. sections.push({
  350. slideNr : slideNr,
  351. lastSlideNr : lastSlideNr,
  352. title : headerTitle
  353. });
  354. }
  355. }
  356. for ( var i = 0; i < slideEls.length; i++) {
  357. var nav = slideEls[i].querySelector('nav.toc');
  358. if(nav){
  359. var ol = document.createElement('ol');
  360. for(var j = 0;j<sections.length;j++){
  361. var liEl = document.createElement('li');
  362. var nr = i+1;
  363. if(nr < sections[j].slideNr){
  364. liEl.className = 'future';
  365. }
  366. else if(nr > sections[j].lastSlideNr){
  367. liEl.className = 'done';
  368. }
  369. else{
  370. liEl.className = 'current';
  371. }
  372. var aEl = document.createElement('a');
  373. aEl.href = '#'+sections[j].slideNr;
  374. aEl.innerHTML = sections[j].title;
  375. var callback = (function(nr){
  376. aEl.addEventListener('click', function(){
  377. goToSlide(nr);
  378. });
  379. })(sections[j].slideNr);
  380. liEl.appendChild(aEl);
  381. ol.appendChild(liEl);
  382. }
  383. nav.appendChild(ol);
  384. }
  385. }
  386. }
  387. /* Preloading frames */
  388. function disableSlideFrames(no) {
  389. var el = getSlideEl(no);
  390. if (!el) {
  391. return;
  392. }
  393. var frames = el.getElementsByTagName('iframe');
  394. for ( var i = 0, frame; frame = frames[i]; i++) {
  395. disableFrame(frame);
  396. }
  397. };
  398. function enableSlideFrames(no) {
  399. var el = getSlideEl(no);
  400. if (!el) {
  401. return;
  402. }
  403. var frames = el.getElementsByTagName('iframe');
  404. for ( var i = 0, frame; frame = frames[i]; i++) {
  405. enableFrame(frame);
  406. }
  407. };
  408. function disableFrame(frame) {
  409. frame.src = 'about:blank';
  410. };
  411. function enableFrame(frame) {
  412. var src = frame._src;
  413. if (frame.src != src && src != 'about:blank') {
  414. frame.src = src;
  415. }
  416. };
  417. function setupFrames() {
  418. var frames = document.querySelectorAll('iframe');
  419. for ( var i = 0, frame; frame = frames[i]; i++) {
  420. frame._src = frame.src;
  421. disableFrame(frame);
  422. }
  423. enableSlideFrames(curSlide);
  424. enableSlideFrames(curSlide + 1);
  425. enableSlideFrames(curSlide + 2);
  426. };
  427. function setupInteraction() {
  428. /* Clicking and tapping */
  429. var el = document.createElement('div');
  430. el.className = 'slide-area';
  431. el.id = 'prev-slide-area';
  432. el.addEventListener('click', prevSlide, false);
  433. document.querySelector('div.slides').appendChild(el);
  434. var el = document.createElement('div');
  435. el.className = 'slide-area';
  436. el.id = 'next-slide-area';
  437. el.addEventListener('click', nextSlide, false);
  438. document.querySelector('div.slides').appendChild(el);
  439. /* Swiping */
  440. document.body.addEventListener('touchstart', handleTouchStart, false);
  441. }
  442. /* ChromeVox support */
  443. function isChromeVoxActive() {
  444. if (typeof (cvox) == 'undefined') {
  445. return false;
  446. }
  447. else {
  448. return true;
  449. }
  450. };
  451. function speakAndSyncToNode(node) {
  452. if (!isChromeVoxActive()) {
  453. return;
  454. }
  455. cvox.ChromeVox.navigationManager.switchToStrategy(cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
  456. cvox.ChromeVox.navigationManager.syncToNode(node);
  457. cvox.ChromeVoxUserCommands.finishNavCommand('');
  458. var target = node;
  459. while (target.firstChild) {
  460. target = target.firstChild;
  461. }
  462. cvox.ChromeVox.navigationManager.syncToNode(target);
  463. };
  464. function speakNextItem() {
  465. if (!isChromeVoxActive()) {
  466. return;
  467. }
  468. cvox.ChromeVox.navigationManager.switchToStrategy(cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
  469. cvox.ChromeVox.navigationManager.next(true);
  470. if (!cvox.DomUtil.isDescendantOfNode(cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])) {
  471. var target = slideEls[curSlide];
  472. while (target.firstChild) {
  473. target = target.firstChild;
  474. }
  475. cvox.ChromeVox.navigationManager.syncToNode(target);
  476. cvox.ChromeVox.navigationManager.next(true);
  477. }
  478. cvox.ChromeVoxUserCommands.finishNavCommand('');
  479. };
  480. function speakPrevItem() {
  481. if (!isChromeVoxActive()) {
  482. return;
  483. }
  484. cvox.ChromeVox.navigationManager.switchToStrategy(cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
  485. cvox.ChromeVox.navigationManager.previous(true);
  486. if (!cvox.DomUtil.isDescendantOfNode(cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])) {
  487. var target = slideEls[curSlide];
  488. while (target.lastChild) {
  489. target = target.lastChild;
  490. }
  491. cvox.ChromeVox.navigationManager.syncToNode(target);
  492. cvox.ChromeVox.navigationManager.previous(true);
  493. }
  494. cvox.ChromeVoxUserCommands.finishNavCommand('');
  495. };
  496. /* Hash functions */
  497. function getCurSlideFromHash() {
  498. var slideNo = parseInt(location.hash.substr(1));
  499. if (slideNo) {
  500. curSlide = slideNo - 1;
  501. }
  502. else {
  503. curSlide = 0;
  504. }
  505. };
  506. function updateHash() {
  507. location.replace('#' + (curSlide + 1));
  508. };
  509. /* Event listeners */
  510. function handleBodyKeyDown(event) {
  511. switch (event.keyCode) {
  512. case 39: // right arrow
  513. case 13: // Enter
  514. case 32: // space
  515. case 34: // PgDn
  516. nextSlide();
  517. event.preventDefault();
  518. break;
  519. case 37: // left arrow
  520. case 8: // Backspace
  521. case 33: // PgUp
  522. prevSlide();
  523. event.preventDefault();
  524. break;
  525. case 40: // down arrow
  526. if (isChromeVoxActive()) {
  527. speakNextItem();
  528. }
  529. else {
  530. nextSlide();
  531. }
  532. event.preventDefault();
  533. break;
  534. case 38: // up arrow
  535. if (isChromeVoxActive()) {
  536. speakPrevItem();
  537. }
  538. else {
  539. prevSlide();
  540. }
  541. event.preventDefault();
  542. break;
  543. case 71: // g
  544. case 103: // G
  545. var nr = prompt("Go to slide", (curSlide + 1));
  546. goToSlide(parseInt(nr));
  547. event.preventDefault();
  548. // Fix for (at least) FF: set focus on slide again to get next key
  549. // event
  550. window.focus();
  551. break;
  552. case 36: // home
  553. goToSlide(1);
  554. event.preventDefault();
  555. break;
  556. case 35: // end
  557. goToSlide(slideEls.length);
  558. event.preventDefault();
  559. break;
  560. case 190: // "."
  561. if (presenterSlaveWindow !== null) {
  562. presenterSlaveWindow.postMessage("forward", "*");
  563. }
  564. event.preventDefault();
  565. break;
  566. case 188: // ","
  567. if (presenterSlaveWindow !== null) {
  568. presenterSlaveWindow.postMessage("back", "*");
  569. }
  570. event.preventDefault();
  571. break;
  572. case 80:
  573. case 112:
  574. if (!isPresenterSlave && presenterSlaveWindow === null) {
  575. presenterSlaveWindow = window.open(window.location.href, "Presenter Notes", "dependent=yes,width=900,height=700");
  576. // Wait some time (2 sec.) hoping that the window has been opened
  577. setTimeout(function() {
  578. if (presenterSlaveWindow !== null) {
  579. // you are a slave now!
  580. presenterSlaveWindow.postMessage("slave", "*");
  581. // goto master's current slide
  582. presenterSlaveWindow.postMessage("" + curSlide, "*");
  583. }
  584. }, 2000);
  585. // if master has notes enabled, toggle now
  586. if (showPresenterNotes) {
  587. togglePresenterNotes();
  588. }
  589. }
  590. event.preventDefault();
  591. break;
  592. case 78: // n / N
  593. case 110:
  594. if (presenterSlaveWindow !== null) {
  595. presenterSlaveWindow.postMessage("togglenotes", "*");
  596. }
  597. else {
  598. togglePresenterNotes();
  599. }
  600. event.preventDefault();
  601. break;
  602. case 84: // t / T
  603. case 110:
  604. toggleTransitions();
  605. event.preventDefault();
  606. break;
  607. case 66: // b
  608. toggleBlackOut();
  609. break;
  610. case 87: // w
  611. toggleWhiteOut();
  612. break;
  613. }
  614. };
  615. var handleTheMessage = function(event) {
  616. //only using strings as older Firefox version do not support complex messaging payload
  617. if (event.data !== null) {
  618. if (event.data === 'slave') {
  619. //become a slave
  620. isPresenterSlave = true;
  621. document.title = "Presenter Notes: " + document.title;
  622. togglePresenterNotes();
  623. // disable transitions
  624. var section = document.querySelector("div.slides");
  625. section.classList.add('no-trans');
  626. }
  627. else if (event.data === 'togglenotes') {
  628. //"remote" note toggling
  629. togglePresenterNotes();
  630. }
  631. else if (event.data === 'forward') {
  632. //move forward only in presenter screen
  633. // off-by-one => +2 instead of +1
  634. var target = curSlide + 2;
  635. goToSlide(target);
  636. }
  637. else if (event.data === 'back') {
  638. //move back only in presenter screen
  639. // off-by-one, not less than 1;
  640. goToSlide(curSlide || 1);
  641. }
  642. else {
  643. // optimistic: if none of the above cases, expect a number to be send
  644. goToSlide(parseInt(event.data) + 1);
  645. }
  646. }
  647. }
  648. function addEventListeners() {
  649. document.addEventListener('keydown', handleBodyKeyDown, false);
  650. window.addEventListener("message", handleTheMessage, false);
  651. };
  652. /* Initialization */
  653. function enumrateSlides() {
  654. for ( var i = 0, slide; slide = slideEls[i]; i++) {
  655. var el = document.createElement('footer');
  656. var span = document.createElement('span');
  657. span.className = 'slide-nr';
  658. span.innerHTML = "" + (i + 1);
  659. el.appendChild(span);
  660. slide.appendChild(el);
  661. }
  662. };
  663. function addPrettify() {
  664. var els = document.querySelectorAll('pre');
  665. for ( var i = 0, el; el = els[i]; i++) {
  666. if (!el.classList.contains('noprettyprint')) {
  667. el.classList.add('prettyprint');
  668. }
  669. }
  670. var el = document.createElement('script');
  671. el.type = 'text/javascript';
  672. el.src = 'lib/prettify.js';
  673. el.onload = function() {
  674. prettyPrint();
  675. }
  676. document.body.appendChild(el);
  677. };
  678. function addFontStyle() {
  679. var el = document.createElement('link');
  680. el.rel = 'stylesheet';
  681. el.type = 'text/css';
  682. el.href = 'http://fonts.googleapis.com/css?family=' + 'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
  683. document.body.appendChild(el);
  684. };
  685. function addGeneralStyle() {
  686. var el = document.createElement('meta');
  687. el.name = 'viewport';
  688. el.content = 'width=1100,height=750';
  689. document.querySelector('head').appendChild(el);
  690. var el = document.createElement('meta');
  691. el.name = 'apple-mobile-web-app-capable';
  692. el.content = 'yes';
  693. document.querySelector('head').appendChild(el);
  694. };
  695. function makeBuildLists() {
  696. for ( var i = curSlide, slide; slide = slideEls[i]; i++) {
  697. var items = slide.querySelectorAll('.build > *');
  698. for ( var j = 0, item; item = items[j]; j++) {
  699. if (item.classList) {
  700. item.classList.add('to-build');
  701. }
  702. }
  703. }
  704. };
  705. function handleDomLoaded() {
  706. slideEls = document.querySelectorAll('div.slides section > article');
  707. setupFrames();
  708. enumrateSlides();
  709. processToc();
  710. addFontStyle();
  711. addGeneralStyle();
  712. addPrettify();
  713. addEventListeners();
  714. updateSlides();
  715. setupInteraction();
  716. makeBuildLists();
  717. document.body.classList.add('loaded');
  718. };
  719. function initialize() {
  720. getCurSlideFromHash();
  721. if (window['_DCL']) {
  722. handleDomLoaded();
  723. }
  724. else {
  725. document.addEventListener('DOMContentLoaded', handleDomLoaded, false);
  726. }
  727. }
  728. initialize();