hyla 1.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (404) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile.lock +35 -7
  3. data/README.adoc +10 -2
  4. data/bin/hyla +16 -13
  5. data/hyla.gemspec +6 -3
  6. data/lib/hyla.rb +5 -1
  7. data/lib/hyla/command.rb +12 -10
  8. data/lib/hyla/commands/create.rb +21 -12
  9. data/lib/hyla/commands/generate.rb +168 -59
  10. data/lib/hyla/commands/new.rb +36 -15
  11. data/lib/hyla/commands/watch.rb +6 -2
  12. data/lib/hyla/configuration.rb +169 -27
  13. data/lib/hyla/core_ext.rb +24 -0
  14. data/lib/hyla/project.rb +1 -1
  15. data/lib/resources/backends/haml/deckjs/block_admonition.html.haml +14 -0
  16. data/lib/resources/backends/haml/deckjs/block_audio.html.haml +7 -0
  17. data/lib/resources/backends/haml/deckjs/block_colist.html.haml +21 -0
  18. data/lib/resources/backends/haml/deckjs/block_dlist.html.haml +54 -0
  19. data/lib/resources/backends/haml/deckjs/block_example.haml.haml +5 -0
  20. data/lib/resources/backends/haml/deckjs/block_floating_title.html.haml +1 -0
  21. data/lib/resources/backends/haml/deckjs/block_image.html.haml +11 -0
  22. data/lib/resources/backends/haml/deckjs/block_listing.html.haml +32 -0
  23. data/lib/resources/backends/haml/deckjs/block_literal.html.haml +5 -0
  24. data/lib/resources/backends/haml/deckjs/block_olist.html.haml +11 -0
  25. data/lib/resources/backends/haml/deckjs/block_open.html.haml +5 -0
  26. data/lib/resources/backends/haml/deckjs/block_page_break.html.haml +1 -0
  27. data/lib/resources/backends/haml/deckjs/block_paragraph.html.haml +4 -0
  28. data/lib/resources/backends/haml/deckjs/block_pass.html.haml +1 -0
  29. data/lib/resources/backends/haml/deckjs/block_preamble.html.haml +3 -0
  30. data/lib/resources/backends/haml/deckjs/block_quote.html.haml +12 -0
  31. data/lib/resources/backends/haml/deckjs/block_ruler.html.haml +1 -0
  32. data/lib/resources/backends/haml/deckjs/block_sidebar.html.haml +5 -0
  33. data/lib/resources/backends/haml/deckjs/block_table.html.haml +48 -0
  34. data/lib/resources/backends/haml/deckjs/block_ulist.html.haml +25 -0
  35. data/lib/resources/backends/haml/deckjs/block_verse.html.haml +13 -0
  36. data/lib/resources/backends/haml/deckjs/block_video.html.haml +24 -0
  37. data/lib/resources/backends/haml/deckjs/document.html.haml +182 -0
  38. data/lib/resources/backends/haml/deckjs/embedded.html.haml +10 -0
  39. data/lib/resources/backends/haml/deckjs/inline_anchor.html.haml +12 -0
  40. data/lib/resources/backends/haml/deckjs/inline_break.html.haml +2 -0
  41. data/lib/resources/backends/haml/deckjs/inline_callout.html.haml +7 -0
  42. data/lib/resources/backends/haml/deckjs/inline_footnote.html.haml +8 -0
  43. data/lib/resources/backends/haml/deckjs/inline_image.html.haml +24 -0
  44. data/lib/resources/backends/haml/deckjs/inline_indexterm.html.haml +2 -0
  45. data/lib/resources/backends/haml/deckjs/inline_quoted.html.haml +19 -0
  46. data/lib/resources/backends/haml/deckjs/section.html.haml +7 -0
  47. data/lib/resources/backends/haml/docbook45/block_admonition.xml.haml +7 -0
  48. data/lib/resources/backends/haml/docbook45/block_colist.xml.haml +8 -0
  49. data/lib/resources/backends/haml/docbook45/block_dlist.xml.haml +61 -0
  50. data/lib/resources/backends/haml/docbook45/block_example.xml.haml +14 -0
  51. data/lib/resources/backends/haml/docbook45/block_floating_title.xml.haml +1 -0
  52. data/lib/resources/backends/haml/docbook45/block_image.xml.haml +8 -0
  53. data/lib/resources/backends/haml/docbook45/block_listing.xml.haml +14 -0
  54. data/lib/resources/backends/haml/docbook45/block_literal.xml.haml +7 -0
  55. data/lib/resources/backends/haml/docbook45/block_olist.xml.haml +8 -0
  56. data/lib/resources/backends/haml/docbook45/block_open.xml.haml +25 -0
  57. data/lib/resources/backends/haml/docbook45/block_page_break.xml.haml +1 -0
  58. data/lib/resources/backends/haml/docbook45/block_paragraph.xml.haml +6 -0
  59. data/lib/resources/backends/haml/docbook45/block_pass.xml.haml +1 -0
  60. data/lib/resources/backends/haml/docbook45/block_preamble.xml.haml +7 -0
  61. data/lib/resources/backends/haml/docbook45/block_quote.xml.haml +13 -0
  62. data/lib/resources/backends/haml/docbook45/block_ruler.xml.haml +2 -0
  63. data/lib/resources/backends/haml/docbook45/block_sidebar.xml.haml +7 -0
  64. data/lib/resources/backends/haml/docbook45/block_table.xml.haml +37 -0
  65. data/lib/resources/backends/haml/docbook45/block_ulist.xml.haml +18 -0
  66. data/lib/resources/backends/haml/docbook45/block_verse.xml.haml +10 -0
  67. data/lib/resources/backends/haml/docbook45/document.xml.haml +63 -0
  68. data/lib/resources/backends/haml/docbook45/embedded.xml.haml +1 -0
  69. data/lib/resources/backends/haml/docbook45/helpers.rb +7 -0
  70. data/lib/resources/backends/haml/docbook45/inline_anchor.xml.haml +13 -0
  71. data/lib/resources/backends/haml/docbook45/inline_break.xml.haml +2 -0
  72. data/lib/resources/backends/haml/docbook45/inline_callout.xml.haml +1 -0
  73. data/lib/resources/backends/haml/docbook45/inline_footnote.xml.haml +5 -0
  74. data/lib/resources/backends/haml/docbook45/inline_image.xml.haml +5 -0
  75. data/lib/resources/backends/haml/docbook45/inline_indexterm.xml.haml +18 -0
  76. data/lib/resources/backends/haml/docbook45/inline_quoted.xml.haml +21 -0
  77. data/lib/resources/backends/haml/docbook45/section.xml.haml +5 -0
  78. data/lib/resources/backends/haml/html5/block_admonition.html.haml +14 -0
  79. data/lib/resources/backends/haml/html5/block_audio.html.haml +7 -0
  80. data/lib/resources/backends/haml/html5/block_colist.html.haml +21 -0
  81. data/lib/resources/backends/haml/html5/block_dlist.html.haml +54 -0
  82. data/lib/resources/backends/haml/html5/block_example.haml.haml +5 -0
  83. data/lib/resources/backends/haml/html5/block_floating_title.html.haml +1 -0
  84. data/lib/resources/backends/haml/html5/block_image.html.haml +11 -0
  85. data/lib/resources/backends/haml/html5/block_listing.html.haml +31 -0
  86. data/lib/resources/backends/haml/html5/block_literal.html.haml +5 -0
  87. data/lib/resources/backends/haml/html5/block_math.html.haml +10 -0
  88. data/lib/resources/backends/haml/html5/block_olist.html.haml +9 -0
  89. data/lib/resources/backends/haml/html5/block_open.html.haml +15 -0
  90. data/lib/resources/backends/haml/html5/block_page_break.html.haml +1 -0
  91. data/lib/resources/backends/haml/html5/block_paragraph.html.haml +4 -0
  92. data/lib/resources/backends/haml/html5/block_pass.html.haml +1 -0
  93. data/lib/resources/backends/haml/html5/block_preamble.html.haml +6 -0
  94. data/lib/resources/backends/haml/html5/block_quote.html.haml +12 -0
  95. data/lib/resources/backends/haml/html5/block_ruler.html.haml +1 -0
  96. data/lib/resources/backends/haml/html5/block_sidebar.html.haml +5 -0
  97. data/lib/resources/backends/haml/html5/block_table.html.haml +47 -0
  98. data/lib/resources/backends/haml/html5/block_toc.html.haml +11 -0
  99. data/lib/resources/backends/haml/html5/block_ulist.html.haml +25 -0
  100. data/lib/resources/backends/haml/html5/block_verse.html.haml +13 -0
  101. data/lib/resources/backends/haml/html5/block_video.html.haml +27 -0
  102. data/lib/resources/backends/haml/html5/document.html.haml +114 -0
  103. data/lib/resources/backends/haml/html5/embedded.html.haml +10 -0
  104. data/lib/resources/backends/haml/html5/helpers.rb +7 -0
  105. data/lib/resources/backends/haml/html5/inline_anchor.html.haml +11 -0
  106. data/lib/resources/backends/haml/html5/inline_break.html.haml +2 -0
  107. data/lib/resources/backends/haml/html5/inline_button.html.haml +1 -0
  108. data/lib/resources/backends/haml/html5/inline_callout.html.haml +7 -0
  109. data/lib/resources/backends/haml/html5/inline_footnote.html.haml +8 -0
  110. data/lib/resources/backends/haml/html5/inline_image.html.haml +24 -0
  111. data/lib/resources/backends/haml/html5/inline_indexterm.html.haml +2 -0
  112. data/lib/resources/backends/haml/html5/inline_kbd.html.haml +8 -0
  113. data/lib/resources/backends/haml/html5/inline_menu.html.haml +15 -0
  114. data/lib/resources/backends/haml/html5/inline_quoted.html.haml +22 -0
  115. data/lib/resources/backends/haml/html5/section.html.haml +21 -0
  116. data/lib/resources/backends/haml/revealjs/block_image.html.haml +11 -0
  117. data/lib/resources/backends/haml/revealjs/block_olist.html.haml +8 -0
  118. data/lib/resources/backends/haml/revealjs/block_ulist.html.haml +8 -0
  119. data/lib/resources/backends/haml/revealjs/document.html.haml +61 -0
  120. data/lib/resources/backends/haml/revealjs/section.html.haml +10 -0
  121. data/lib/resources/backends/slim/docbook45/block_admonition.xml.slim +7 -0
  122. data/lib/resources/backends/slim/docbook45/block_colist.xml.slim +8 -0
  123. data/lib/resources/backends/slim/docbook45/block_dlist.xml.slim +61 -0
  124. data/lib/resources/backends/slim/docbook45/block_example.xml.slim +13 -0
  125. data/lib/resources/backends/slim/docbook45/block_floating_title.xml.slim +1 -0
  126. data/lib/resources/backends/slim/docbook45/block_image.xml.slim +7 -0
  127. data/lib/resources/backends/slim/docbook45/block_listing.xml.slim +14 -0
  128. data/lib/resources/backends/slim/docbook45/block_literal.xml.slim +7 -0
  129. data/lib/resources/backends/slim/docbook45/block_olist.xml.slim +8 -0
  130. data/lib/resources/backends/slim/docbook45/block_open.xml.slim +25 -0
  131. data/lib/resources/backends/slim/docbook45/block_page_break.xml.slim +1 -0
  132. data/lib/resources/backends/slim/docbook45/block_paragraph.xml.slim +6 -0
  133. data/lib/resources/backends/slim/docbook45/block_pass.xml.slim +1 -0
  134. data/lib/resources/backends/slim/docbook45/block_preamble.xml.slim +7 -0
  135. data/lib/resources/backends/slim/docbook45/block_quote.xml.slim +13 -0
  136. data/lib/resources/backends/slim/docbook45/block_ruler.xml.slim +2 -0
  137. data/lib/resources/backends/slim/docbook45/block_sidebar.xml.slim +7 -0
  138. data/lib/resources/backends/slim/docbook45/block_table.xml.slim +36 -0
  139. data/lib/resources/backends/slim/docbook45/block_ulist.xml.slim +18 -0
  140. data/lib/resources/backends/slim/docbook45/block_verse.xml.slim +10 -0
  141. data/lib/resources/backends/slim/docbook45/document.xml.slim +63 -0
  142. data/lib/resources/backends/slim/docbook45/embedded.xml.slim +1 -0
  143. data/lib/resources/backends/slim/docbook45/helpers.rb +9 -0
  144. data/lib/resources/backends/slim/docbook45/inline_anchor.xml.slim +13 -0
  145. data/lib/resources/backends/slim/docbook45/inline_break.xml.slim +2 -0
  146. data/lib/resources/backends/slim/docbook45/inline_callout.xml.slim +1 -0
  147. data/lib/resources/backends/slim/docbook45/inline_footnote.xml.slim +4 -0
  148. data/lib/resources/backends/slim/docbook45/inline_image.xml.slim +4 -0
  149. data/lib/resources/backends/slim/docbook45/inline_indexterm.xml.slim +16 -0
  150. data/lib/resources/backends/slim/docbook45/inline_quoted.xml.slim +21 -0
  151. data/lib/resources/backends/slim/docbook45/section.xml.slim +5 -0
  152. data/lib/resources/backends/slim/document.html.bk.slim +335 -0
  153. data/lib/resources/backends/slim/dzslides/block_admonition.html.slim +19 -0
  154. data/lib/resources/backends/slim/dzslides/block_colist.html.slim +20 -0
  155. data/lib/resources/backends/slim/dzslides/block_dlist.html.slim +10 -0
  156. data/lib/resources/backends/slim/dzslides/block_example.html.slim +4 -0
  157. data/lib/resources/backends/slim/dzslides/block_floating_title.html.slim +1 -0
  158. data/lib/resources/backends/slim/dzslides/block_image.html.slim +14 -0
  159. data/lib/resources/backends/slim/dzslides/block_listing.html.slim +8 -0
  160. data/lib/resources/backends/slim/dzslides/block_literal.html.slim +11 -0
  161. data/lib/resources/backends/slim/dzslides/block_olist.html.slim +6 -0
  162. data/lib/resources/backends/slim/dzslides/block_open.html.slim +4 -0
  163. data/lib/resources/backends/slim/dzslides/block_paragraph.html.slim +1 -0
  164. data/lib/resources/backends/slim/dzslides/block_pass.html.slim +1 -0
  165. data/lib/resources/backends/slim/dzslides/block_quote.html.slim +11 -0
  166. data/lib/resources/backends/slim/dzslides/block_ruler.html.slim +1 -0
  167. data/lib/resources/backends/slim/dzslides/block_table.html.slim +45 -0
  168. data/lib/resources/backends/slim/dzslides/block_ulist.html.slim +6 -0
  169. data/lib/resources/backends/slim/dzslides/block_verse.html.slim +12 -0
  170. data/lib/resources/backends/slim/dzslides/block_video.html.slim +6 -0
  171. data/lib/resources/backends/slim/dzslides/document.html.slim +67 -0
  172. data/lib/resources/backends/slim/dzslides/embedded.html.slim +1 -0
  173. data/lib/resources/backends/slim/dzslides/helpers.rb +9 -0
  174. data/lib/resources/backends/slim/dzslides/inline_anchor.html.slim +11 -0
  175. data/lib/resources/backends/slim/dzslides/inline_break.html.slim +2 -0
  176. data/lib/resources/backends/slim/dzslides/inline_callout.html.slim +7 -0
  177. data/lib/resources/backends/slim/dzslides/inline_footnote.html.slim +6 -0
  178. data/lib/resources/backends/slim/dzslides/inline_image.html.slim +24 -0
  179. data/lib/resources/backends/slim/dzslides/inline_indexterm.html.slim +2 -0
  180. data/lib/resources/backends/slim/dzslides/inline_kbd.html.slim +8 -0
  181. data/lib/resources/backends/slim/dzslides/inline_quoted.html.slim +23 -0
  182. data/lib/resources/backends/slim/dzslides/section.html.slim +4 -0
  183. data/lib/resources/backends/slim/html5/block_admonition.html.slim +13 -0
  184. data/lib/resources/backends/slim/html5/block_audio.html.slim +6 -0
  185. data/lib/resources/backends/slim/html5/block_colist.html.slim +20 -0
  186. data/lib/resources/backends/slim/html5/block_dlist.html.slim +53 -0
  187. data/lib/resources/backends/slim/html5/block_example.html.slim +4 -0
  188. data/lib/resources/backends/slim/html5/block_floating_title.html.slim +1 -0
  189. data/lib/resources/backends/slim/html5/block_image.html.slim +10 -0
  190. data/lib/resources/backends/slim/html5/block_listing.html.slim +31 -0
  191. data/lib/resources/backends/slim/html5/block_literal.html.slim +4 -0
  192. data/lib/resources/backends/slim/html5/block_math.html.slim +10 -0
  193. data/lib/resources/backends/slim/html5/block_olist.html.slim +9 -0
  194. data/lib/resources/backends/slim/html5/block_open.html.slim +15 -0
  195. data/lib/resources/backends/slim/html5/block_page_break.html.slim +1 -0
  196. data/lib/resources/backends/slim/html5/block_paragraph.html.slim +4 -0
  197. data/lib/resources/backends/slim/html5/block_pass.html.slim +1 -0
  198. data/lib/resources/backends/slim/html5/block_preamble.html.slim +6 -0
  199. data/lib/resources/backends/slim/html5/block_quote.html.slim +14 -0
  200. data/lib/resources/backends/slim/html5/block_ruler.html.slim +1 -0
  201. data/lib/resources/backends/slim/html5/block_sidebar.html.slim +5 -0
  202. data/lib/resources/backends/slim/html5/block_table.html.slim +45 -0
  203. data/lib/resources/backends/slim/html5/block_toc.html.slim +12 -0
  204. data/lib/resources/backends/slim/html5/block_ulist.html.slim +25 -0
  205. data/lib/resources/backends/slim/html5/block_verse.html.slim +14 -0
  206. data/lib/resources/backends/slim/html5/block_video.html.slim +27 -0
  207. data/lib/resources/backends/slim/html5/document.html.slim +112 -0
  208. data/lib/resources/backends/slim/html5/embedded.html.slim +9 -0
  209. data/lib/resources/backends/slim/html5/helpers.rb +8 -0
  210. data/lib/resources/backends/slim/html5/inline_anchor.html.slim +11 -0
  211. data/lib/resources/backends/slim/html5/inline_break.html.slim +2 -0
  212. data/lib/resources/backends/slim/html5/inline_button.html.slim +1 -0
  213. data/lib/resources/backends/slim/html5/inline_callout.html.slim +7 -0
  214. data/lib/resources/backends/slim/html5/inline_footnote.html.slim +6 -0
  215. data/lib/resources/backends/slim/html5/inline_image.html.slim +24 -0
  216. data/lib/resources/backends/slim/html5/inline_indexterm.html.slim +2 -0
  217. data/lib/resources/backends/slim/html5/inline_kbd.html.slim +8 -0
  218. data/lib/resources/backends/slim/html5/inline_menu.html.slim +15 -0
  219. data/lib/resources/backends/slim/html5/inline_quoted.html.slim +22 -0
  220. data/lib/resources/backends/slim/html5/section.html.slim +20 -0
  221. data/lib/resources/backends/slim/revealjs/block_admonition.html.slim +13 -0
  222. data/lib/resources/backends/slim/revealjs/block_audio.html.slim +6 -0
  223. data/lib/resources/backends/slim/revealjs/block_colist.html.slim +20 -0
  224. data/lib/resources/backends/slim/revealjs/block_dlist.html.slim +53 -0
  225. data/lib/resources/backends/slim/revealjs/block_example.html.slim +4 -0
  226. data/lib/resources/backends/slim/revealjs/block_floating_title.html.slim +1 -0
  227. data/lib/resources/backends/slim/revealjs/block_image.html.slim +10 -0
  228. data/lib/resources/backends/slim/revealjs/block_listing.html.slim +31 -0
  229. data/lib/resources/backends/slim/revealjs/block_literal.html.slim +4 -0
  230. data/lib/resources/backends/slim/revealjs/block_math.html.slim +10 -0
  231. data/lib/resources/backends/slim/revealjs/block_olist.html.slim +9 -0
  232. data/lib/resources/backends/slim/revealjs/block_open.html.slim +15 -0
  233. data/lib/resources/backends/slim/revealjs/block_page_break.html.slim +1 -0
  234. data/lib/resources/backends/slim/revealjs/block_paragraph.html.slim +4 -0
  235. data/lib/resources/backends/slim/revealjs/block_pass.html.slim +1 -0
  236. data/lib/resources/backends/slim/revealjs/block_preamble.html.slim +6 -0
  237. data/lib/resources/backends/slim/revealjs/block_quote.html.slim +14 -0
  238. data/lib/resources/backends/slim/revealjs/block_ruler.html.slim +1 -0
  239. data/lib/resources/backends/slim/revealjs/block_sidebar.html.slim +5 -0
  240. data/lib/resources/backends/slim/revealjs/block_table.html.slim +45 -0
  241. data/lib/resources/backends/slim/revealjs/block_toc.html.slim +12 -0
  242. data/lib/resources/backends/slim/revealjs/block_ulist.html.slim +25 -0
  243. data/lib/resources/backends/slim/revealjs/block_verse.html.slim +14 -0
  244. data/lib/resources/backends/slim/revealjs/block_video.html.slim +27 -0
  245. data/lib/resources/backends/slim/revealjs/document.html.slim +87 -0
  246. data/lib/resources/backends/slim/revealjs/embedded.html.slim +9 -0
  247. data/lib/resources/backends/slim/revealjs/helpers.rb +8 -0
  248. data/lib/resources/backends/slim/revealjs/inline_anchor.html.slim +11 -0
  249. data/lib/resources/backends/slim/revealjs/inline_break.html.slim +2 -0
  250. data/lib/resources/backends/slim/revealjs/inline_button.html.slim +1 -0
  251. data/lib/resources/backends/slim/revealjs/inline_callout.html.slim +7 -0
  252. data/lib/resources/backends/slim/revealjs/inline_footnote.html.slim +6 -0
  253. data/lib/resources/backends/slim/revealjs/inline_image.html.slim +24 -0
  254. data/lib/resources/backends/slim/revealjs/inline_indexterm.html.slim +2 -0
  255. data/lib/resources/backends/slim/revealjs/inline_kbd.html.slim +8 -0
  256. data/lib/resources/backends/slim/revealjs/inline_menu.html.slim +15 -0
  257. data/lib/resources/backends/slim/revealjs/inline_quoted.html.slim +22 -0
  258. data/lib/resources/backends/slim/revealjs/section.html.slim +3 -0
  259. data/lib/resources/deck.js/core/deck.core.css +407 -0
  260. data/lib/resources/deck.js/core/deck.core.js +498 -0
  261. data/lib/resources/deck.js/extensions/deck.js-blank/README.md +10 -0
  262. data/lib/resources/deck.js/extensions/deck.js-blank/deck.blank.js +33 -0
  263. data/lib/resources/deck.js/extensions/goto/deck.goto.css +41 -0
  264. data/lib/resources/deck.js/extensions/goto/deck.goto.html +7 -0
  265. data/lib/resources/deck.js/extensions/goto/deck.goto.js +170 -0
  266. data/lib/resources/deck.js/extensions/hash/deck.hash.css +13 -0
  267. data/lib/resources/deck.js/extensions/hash/deck.hash.html +2 -0
  268. data/lib/resources/deck.js/extensions/hash/deck.hash.js +142 -0
  269. data/lib/resources/deck.js/extensions/menu/deck.menu.css +47 -0
  270. data/lib/resources/deck.js/extensions/menu/deck.menu.js +187 -0
  271. data/lib/resources/deck.js/extensions/navigation/deck.navigation.css +43 -0
  272. data/lib/resources/deck.js/extensions/navigation/deck.navigation.html +3 -0
  273. data/lib/resources/deck.js/extensions/navigation/deck.navigation.js +92 -0
  274. data/lib/resources/deck.js/extensions/scale/deck.scale.css +28 -0
  275. data/lib/resources/deck.js/extensions/scale/deck.scale.js +170 -0
  276. data/lib/resources/deck.js/extensions/split/README.md +30 -0
  277. data/lib/resources/deck.js/extensions/split/deck.split.js +59 -0
  278. data/lib/resources/deck.js/extensions/status/deck.status.css +18 -0
  279. data/lib/resources/deck.js/extensions/status/deck.status.html +6 -0
  280. data/lib/resources/deck.js/extensions/status/deck.status.js +95 -0
  281. data/lib/resources/deck.js/extensions/toc/deck.toc.css +40 -0
  282. data/lib/resources/deck.js/extensions/toc/deck.toc.html +12 -0
  283. data/lib/resources/deck.js/extensions/toc/deck.toc.js +271 -0
  284. data/lib/resources/deck.js/jquery-1.7.2.min.js +4 -0
  285. data/lib/resources/deck.js/jquery.min.js +4 -0
  286. data/lib/resources/deck.js/modernizr.custom.js +4 -0
  287. data/lib/resources/deck.js/themes/style/beamer.css +286 -0
  288. data/lib/resources/deck.js/themes/style/neon.css +123 -0
  289. data/lib/resources/deck.js/themes/style/swiss.css +84 -0
  290. data/lib/resources/deck.js/themes/style/web-2.0.css +214 -0
  291. data/lib/resources/deck.js/themes/transition/beamer.css +66 -0
  292. data/lib/resources/deck.js/themes/transition/fade.css +43 -0
  293. data/lib/resources/deck.js/themes/transition/horizontal-slide.css +76 -0
  294. data/lib/resources/deck.js/themes/transition/vertical-slide.css +94 -0
  295. data/lib/resources/revealjs/LICENSE +19 -0
  296. data/lib/resources/revealjs/css/print/paper.css +176 -0
  297. data/lib/resources/revealjs/css/print/pdf.css +190 -0
  298. data/lib/resources/revealjs/css/reveal.css +1880 -0
  299. data/lib/resources/revealjs/css/reveal.min.css +7 -0
  300. data/lib/resources/revealjs/css/theme/README.md +25 -0
  301. data/lib/resources/revealjs/css/theme/beige.css +148 -0
  302. data/lib/resources/revealjs/css/theme/blood.css +175 -0
  303. data/lib/resources/revealjs/css/theme/default.css +148 -0
  304. data/lib/resources/revealjs/css/theme/moon.css +148 -0
  305. data/lib/resources/revealjs/css/theme/night.css +136 -0
  306. data/lib/resources/revealjs/css/theme/serif.css +138 -0
  307. data/lib/resources/revealjs/css/theme/simple.css +138 -0
  308. data/lib/resources/revealjs/css/theme/sky.css +145 -0
  309. data/lib/resources/revealjs/css/theme/solarized.css +148 -0
  310. data/lib/resources/revealjs/css/theme/source/beige.scss +50 -0
  311. data/lib/resources/revealjs/css/theme/source/blood.scss +91 -0
  312. data/lib/resources/revealjs/css/theme/source/default.scss +42 -0
  313. data/lib/resources/revealjs/css/theme/source/moon.scss +68 -0
  314. data/lib/resources/revealjs/css/theme/source/night.scss +35 -0
  315. data/lib/resources/revealjs/css/theme/source/serif.scss +35 -0
  316. data/lib/resources/revealjs/css/theme/source/simple.scss +38 -0
  317. data/lib/resources/revealjs/css/theme/source/sky.scss +46 -0
  318. data/lib/resources/revealjs/css/theme/source/solarized.scss +74 -0
  319. data/lib/resources/revealjs/css/theme/template/mixins.scss +29 -0
  320. data/lib/resources/revealjs/css/theme/template/settings.scss +34 -0
  321. data/lib/resources/revealjs/css/theme/template/theme.scss +170 -0
  322. data/lib/resources/revealjs/js/reveal.js +3382 -0
  323. data/lib/resources/revealjs/js/reveal.min.js +9 -0
  324. data/lib/resources/revealjs/lib/css/zenburn.css +114 -0
  325. data/lib/resources/revealjs/lib/font/league_gothic-webfont.eot +0 -0
  326. data/lib/resources/revealjs/lib/font/league_gothic-webfont.svg +230 -0
  327. data/lib/resources/revealjs/lib/font/league_gothic-webfont.ttf +0 -0
  328. data/lib/resources/revealjs/lib/font/league_gothic-webfont.woff +0 -0
  329. data/lib/resources/revealjs/lib/font/league_gothic_license +2 -0
  330. data/lib/resources/revealjs/lib/js/classList.js +2 -0
  331. data/lib/resources/revealjs/lib/js/head.min.js +8 -0
  332. data/lib/resources/revealjs/lib/js/html5shiv.js +7 -0
  333. data/lib/resources/revealjs/plugin/highlight/highlight.js +32 -0
  334. data/lib/resources/revealjs/plugin/leap/leap.js +157 -0
  335. data/lib/resources/revealjs/plugin/markdown/example.html +129 -0
  336. data/lib/resources/revealjs/plugin/markdown/example.md +31 -0
  337. data/lib/resources/revealjs/plugin/markdown/markdown.js +392 -0
  338. data/lib/resources/revealjs/plugin/markdown/marked.js +37 -0
  339. data/lib/resources/revealjs/plugin/math/math.js +64 -0
  340. data/lib/resources/revealjs/plugin/multiplex/client.js +13 -0
  341. data/lib/resources/revealjs/plugin/multiplex/index.js +56 -0
  342. data/lib/resources/revealjs/plugin/multiplex/master.js +51 -0
  343. data/lib/resources/revealjs/plugin/notes-server/client.js +57 -0
  344. data/lib/resources/revealjs/plugin/notes-server/index.js +59 -0
  345. data/lib/resources/revealjs/plugin/notes-server/notes.html +142 -0
  346. data/lib/resources/revealjs/plugin/notes/notes.html +267 -0
  347. data/lib/resources/revealjs/plugin/notes/notes.js +78 -0
  348. data/lib/resources/revealjs/plugin/postmessage/example.html +39 -0
  349. data/lib/resources/revealjs/plugin/postmessage/postmessage.js +42 -0
  350. data/lib/resources/revealjs/plugin/print-pdf/print-pdf.js +44 -0
  351. data/lib/resources/revealjs/plugin/remotes/remotes.js +39 -0
  352. data/lib/resources/revealjs/plugin/search/search.js +196 -0
  353. data/lib/resources/revealjs/plugin/zoom-js/zoom.js +258 -0
  354. data/lib/resources/styles/asciidoctor.css +652 -0
  355. data/lib/resources/styles/colony.css +660 -0
  356. data/lib/resources/styles/foundation-lime.css +657 -0
  357. data/lib/resources/styles/foundation-potion.css +657 -0
  358. data/lib/resources/styles/foundation.css +648 -0
  359. data/lib/resources/styles/github.css +669 -0
  360. data/lib/resources/styles/golo.css +669 -0
  361. data/lib/resources/styles/iconic.css +692 -0
  362. data/lib/resources/styles/maker.css +668 -0
  363. data/lib/resources/styles/readthedocs.css +669 -0
  364. data/lib/resources/styles/redhat.css +654 -0
  365. data/lib/resources/styles/riak.css +689 -0
  366. data/lib/resources/styles/rocket-panda.css +662 -0
  367. data/lib/resources/styles/rubygems.css +650 -0
  368. data/lib/templates/_config.yml +27 -0
  369. data/lib/templates/{training → book}/GemFile +2 -1
  370. data/lib/templates/{training → book}/development/audio/ocean_waves.mp3 +0 -0
  371. data/lib/templates/book/development/book.adoc +121 -0
  372. data/lib/templates/{training → book}/development/image/hyla_frog.jpg +0 -0
  373. data/lib/templates/{training → book}/development/video/small.ogv +0 -0
  374. data/lib/templates/{training → book}/introduction/audio/ocean_waves.mp3 +0 -0
  375. data/lib/templates/book/introduction/book.adoc +121 -0
  376. data/lib/templates/{training → book}/introduction/image/hyla_frog.jpg +0 -0
  377. data/lib/templates/{training → book}/introduction/video/small.ogv +0 -0
  378. data/lib/templates/book/readme.adoc +10 -0
  379. data/lib/templates/sample/asciidoc_article.adoc +101 -10
  380. data/lib/templates/sample/asciidoc_audio.adoc +1 -1
  381. data/lib/templates/sample/asciidoc_book.adoc +125 -0
  382. data/lib/templates/sample/asciidoc_image.adoc +26 -0
  383. data/lib/templates/sample/asciidoc_report.adoc +66 -0
  384. data/lib/templates/sample/asciidoc_source.adoc +53 -0
  385. data/lib/templates/sample/asciidoc_table.adoc +36 -0
  386. data/lib/templates/sample/asciidoc_video.adoc +9 -1
  387. data/lib/templates/sample/slideshow_revealjs.adoc +26 -0
  388. data/lib/templates/training-exercises/_config.yml +3 -0
  389. data/lib/templates/training/{introduction → module-1}/article.adoc +7 -2
  390. data/lib/templates/training/module-1/audio/ocean_waves.mp3 +0 -0
  391. data/lib/templates/training/module-1/image/hyla_arborea.jpg +0 -0
  392. data/lib/templates/training/module-1/video/small.ogv +0 -0
  393. data/lib/templates/training/{development → module-2}/article.adoc +7 -2
  394. data/lib/templates/training/module-2/audio/ocean_waves.mp3 +0 -0
  395. data/lib/templates/training/module-2/image/hyla_arborea.jpg +0 -0
  396. data/lib/templates/training/module-2/video/small.ogv +0 -0
  397. data/lib/templates/training/readme.adoc +10 -1
  398. data/scenario.adoc +113 -20
  399. data/test/reports/test-1.0.x-11-12-2013.txt +123 -0
  400. data/todo.adoc +39 -0
  401. metadata +473 -51
  402. data/lib/hyla/training.rb +0 -25
  403. data/lib/templates/sample/asciidoc_source_highlight.adoc +0 -37
  404. data/lib/templates/training-exercises/Gemfile +0 -4
@@ -0,0 +1,3382 @@
1
+ /*!
2
+ * reveal.js
3
+ * http://lab.hakim.se/reveal-js
4
+ * MIT licensed
5
+ *
6
+ * Copyright (C) 2013 Hakim El Hattab, http://hakim.se
7
+ */
8
+ var Reveal = (function(){
9
+
10
+ 'use strict';
11
+
12
+ var SLIDES_SELECTOR = '.reveal .slides section',
13
+ HORIZONTAL_SLIDES_SELECTOR = '.reveal .slides>section',
14
+ VERTICAL_SLIDES_SELECTOR = '.reveal .slides>section.present>section',
15
+ HOME_SLIDE_SELECTOR = '.reveal .slides>section:first-of-type',
16
+
17
+ // Configurations defaults, can be overridden at initialization time
18
+ config = {
19
+
20
+ // The "normal" size of the presentation, aspect ratio will be preserved
21
+ // when the presentation is scaled to fit different resolutions
22
+ width: 960,
23
+ height: 700,
24
+
25
+ // Factor of the display size that should remain empty around the content
26
+ margin: 0.1,
27
+
28
+ // Bounds for smallest/largest possible scale to apply to content
29
+ minScale: 0.2,
30
+ maxScale: 1.0,
31
+
32
+ // Display controls in the bottom right corner
33
+ controls: true,
34
+
35
+ // Display a presentation progress bar
36
+ progress: true,
37
+
38
+ // Display the page number of the current slide
39
+ slideNumber: false,
40
+
41
+ // Push each slide change to the browser history
42
+ history: false,
43
+
44
+ // Enable keyboard shortcuts for navigation
45
+ keyboard: true,
46
+
47
+ // Enable the slide overview mode
48
+ overview: true,
49
+
50
+ // Vertical centering of slides
51
+ center: true,
52
+
53
+ // Enables touch navigation on devices with touch input
54
+ touch: true,
55
+
56
+ // Loop the presentation
57
+ loop: false,
58
+
59
+ // Change the presentation direction to be RTL
60
+ rtl: false,
61
+
62
+ // Turns fragments on and off globally
63
+ fragments: true,
64
+
65
+ // Flags if the presentation is running in an embedded mode,
66
+ // i.e. contained within a limited portion of the screen
67
+ embedded: false,
68
+
69
+ // Number of milliseconds between automatically proceeding to the
70
+ // next slide, disabled when set to 0, this value can be overwritten
71
+ // by using a data-autoslide attribute on your slides
72
+ autoSlide: 0,
73
+
74
+ // Stop auto-sliding after user input
75
+ autoSlideStoppable: true,
76
+
77
+ // Enable slide navigation via mouse wheel
78
+ mouseWheel: false,
79
+
80
+ // Apply a 3D roll to links on hover
81
+ rollingLinks: false,
82
+
83
+ // Hides the address bar on mobile devices
84
+ hideAddressBar: true,
85
+
86
+ // Opens links in an iframe preview overlay
87
+ previewLinks: false,
88
+
89
+ // Focuses body when page changes visiblity to ensure keyboard shortcuts work
90
+ focusBodyOnPageVisiblityChange: true,
91
+
92
+ // Theme (see /css/theme)
93
+ theme: null,
94
+
95
+ // Transition style
96
+ transition: 'default', // default/cube/page/concave/zoom/linear/fade/none
97
+
98
+ // Transition speed
99
+ transitionSpeed: 'default', // default/fast/slow
100
+
101
+ // Transition style for full page slide backgrounds
102
+ backgroundTransition: 'default', // default/linear/none
103
+
104
+ // Parallax background image
105
+ parallaxBackgroundImage: '', // CSS syntax, e.g. "a.jpg"
106
+
107
+ // Parallax background size
108
+ parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px"
109
+
110
+ // Number of slides away from the current that are visible
111
+ viewDistance: 3,
112
+
113
+ // Script dependencies to load
114
+ dependencies: []
115
+
116
+ },
117
+
118
+ // Flags if reveal.js is loaded (has dispatched the 'ready' event)
119
+ loaded = false,
120
+
121
+ // The horizontal and vertical index of the currently active slide
122
+ indexh,
123
+ indexv,
124
+
125
+ // The previous and current slide HTML elements
126
+ previousSlide,
127
+ currentSlide,
128
+
129
+ previousBackground,
130
+
131
+ // Slides may hold a data-state attribute which we pick up and apply
132
+ // as a class to the body. This list contains the combined state of
133
+ // all current slides.
134
+ state = [],
135
+
136
+ // The current scale of the presentation (see width/height config)
137
+ scale = 1,
138
+
139
+ // Cached references to DOM elements
140
+ dom = {},
141
+
142
+ // Features supported by the browser, see #checkCapabilities()
143
+ features = {},
144
+
145
+ // Client is a mobile device, see #checkCapabilities()
146
+ isMobileDevice,
147
+
148
+ // Throttles mouse wheel navigation
149
+ lastMouseWheelStep = 0,
150
+
151
+ // Delays updates to the URL due to a Chrome thumbnailer bug
152
+ writeURLTimeout = 0,
153
+
154
+ // A delay used to activate the overview mode
155
+ activateOverviewTimeout = 0,
156
+
157
+ // A delay used to deactivate the overview mode
158
+ deactivateOverviewTimeout = 0,
159
+
160
+ // Flags if the interaction event listeners are bound
161
+ eventsAreBound = false,
162
+
163
+ // The current auto-slide duration
164
+ autoSlide = 0,
165
+
166
+ // Auto slide properties
167
+ autoSlidePlayer,
168
+ autoSlideTimeout = 0,
169
+ autoSlideStartTime = -1,
170
+ autoSlidePaused = false,
171
+
172
+ // Holds information about the currently ongoing touch input
173
+ touch = {
174
+ startX: 0,
175
+ startY: 0,
176
+ startSpan: 0,
177
+ startCount: 0,
178
+ captured: false,
179
+ threshold: 40
180
+ };
181
+
182
+ /**
183
+ * Starts up the presentation if the client is capable.
184
+ */
185
+ function initialize( options ) {
186
+
187
+ checkCapabilities();
188
+
189
+ if( !features.transforms2d && !features.transforms3d ) {
190
+ document.body.setAttribute( 'class', 'no-transforms' );
191
+
192
+ // If the browser doesn't support core features we won't be
193
+ // using JavaScript to control the presentation
194
+ return;
195
+ }
196
+
197
+ // Force a layout when the whole page, incl fonts, has loaded
198
+ window.addEventListener( 'load', layout, false );
199
+
200
+ var query = Reveal.getQueryHash();
201
+
202
+ // Do not accept new dependencies via query config to avoid
203
+ // the potential of malicious script injection
204
+ if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];
205
+
206
+ // Copy options over to our config object
207
+ extend( config, options );
208
+ extend( config, query );
209
+
210
+ // Hide the address bar in mobile browsers
211
+ hideAddressBar();
212
+
213
+ // Loads the dependencies and continues to #start() once done
214
+ load();
215
+
216
+ }
217
+
218
+ /**
219
+ * Inspect the client to see what it's capable of, this
220
+ * should only happens once per runtime.
221
+ */
222
+ function checkCapabilities() {
223
+
224
+ features.transforms3d = 'WebkitPerspective' in document.body.style ||
225
+ 'MozPerspective' in document.body.style ||
226
+ 'msPerspective' in document.body.style ||
227
+ 'OPerspective' in document.body.style ||
228
+ 'perspective' in document.body.style;
229
+
230
+ features.transforms2d = 'WebkitTransform' in document.body.style ||
231
+ 'MozTransform' in document.body.style ||
232
+ 'msTransform' in document.body.style ||
233
+ 'OTransform' in document.body.style ||
234
+ 'transform' in document.body.style;
235
+
236
+ features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
237
+ features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function';
238
+
239
+ features.canvas = !!document.createElement( 'canvas' ).getContext;
240
+
241
+ isMobileDevice = navigator.userAgent.match( /(iphone|ipod|android)/gi );
242
+
243
+ }
244
+
245
+
246
+ /**
247
+ * Loads the dependencies of reveal.js. Dependencies are
248
+ * defined via the configuration option 'dependencies'
249
+ * and will be loaded prior to starting/binding reveal.js.
250
+ * Some dependencies may have an 'async' flag, if so they
251
+ * will load after reveal.js has been started up.
252
+ */
253
+ function load() {
254
+
255
+ var scripts = [],
256
+ scriptsAsync = [],
257
+ scriptsToPreload = 0;
258
+
259
+ // Called once synchronous scripts finish loading
260
+ function proceed() {
261
+ if( scriptsAsync.length ) {
262
+ // Load asynchronous scripts
263
+ head.js.apply( null, scriptsAsync );
264
+ }
265
+
266
+ start();
267
+ }
268
+
269
+ function loadScript( s ) {
270
+ head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], function() {
271
+ // Extension may contain callback functions
272
+ if( typeof s.callback === 'function' ) {
273
+ s.callback.apply( this );
274
+ }
275
+
276
+ if( --scriptsToPreload === 0 ) {
277
+ proceed();
278
+ }
279
+ });
280
+ }
281
+
282
+ for( var i = 0, len = config.dependencies.length; i < len; i++ ) {
283
+ var s = config.dependencies[i];
284
+
285
+ // Load if there's no condition or the condition is truthy
286
+ if( !s.condition || s.condition() ) {
287
+ if( s.async ) {
288
+ scriptsAsync.push( s.src );
289
+ }
290
+ else {
291
+ scripts.push( s.src );
292
+ }
293
+
294
+ loadScript( s );
295
+ }
296
+ }
297
+
298
+ if( scripts.length ) {
299
+ scriptsToPreload = scripts.length;
300
+
301
+ // Load synchronous scripts
302
+ head.js.apply( null, scripts );
303
+ }
304
+ else {
305
+ proceed();
306
+ }
307
+
308
+ }
309
+
310
+ /**
311
+ * Starts up reveal.js by binding input events and navigating
312
+ * to the current URL deeplink if there is one.
313
+ */
314
+ function start() {
315
+
316
+ // Make sure we've got all the DOM elements we need
317
+ setupDOM();
318
+
319
+ // Resets all vertical slides so that only the first is visible
320
+ resetVerticalSlides();
321
+
322
+ // Updates the presentation to match the current configuration values
323
+ configure();
324
+
325
+ // Read the initial hash
326
+ readURL();
327
+
328
+ // Update all backgrounds
329
+ updateBackground( true );
330
+
331
+ // Notify listeners that the presentation is ready but use a 1ms
332
+ // timeout to ensure it's not fired synchronously after #initialize()
333
+ setTimeout( function() {
334
+ // Enable transitions now that we're loaded
335
+ dom.slides.classList.remove( 'no-transition' );
336
+
337
+ loaded = true;
338
+
339
+ dispatchEvent( 'ready', {
340
+ 'indexh': indexh,
341
+ 'indexv': indexv,
342
+ 'currentSlide': currentSlide
343
+ } );
344
+ }, 1 );
345
+
346
+ }
347
+
348
+ /**
349
+ * Finds and stores references to DOM elements which are
350
+ * required by the presentation. If a required element is
351
+ * not found, it is created.
352
+ */
353
+ function setupDOM() {
354
+
355
+ // Cache references to key DOM elements
356
+ dom.theme = document.querySelector( '#theme' );
357
+ dom.wrapper = document.querySelector( '.reveal' );
358
+ dom.slides = document.querySelector( '.reveal .slides' );
359
+
360
+ // Prevent transitions while we're loading
361
+ dom.slides.classList.add( 'no-transition' );
362
+
363
+ // Background element
364
+ dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null );
365
+
366
+ // Progress bar
367
+ dom.progress = createSingletonNode( dom.wrapper, 'div', 'progress', '<span></span>' );
368
+ dom.progressbar = dom.progress.querySelector( 'span' );
369
+
370
+ // Arrow controls
371
+ createSingletonNode( dom.wrapper, 'aside', 'controls',
372
+ '<div class="navigate-left"></div>' +
373
+ '<div class="navigate-right"></div>' +
374
+ '<div class="navigate-up"></div>' +
375
+ '<div class="navigate-down"></div>' );
376
+
377
+ // Slide number
378
+ dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' );
379
+
380
+ // State background element [DEPRECATED]
381
+ createSingletonNode( dom.wrapper, 'div', 'state-background', null );
382
+
383
+ // Overlay graphic which is displayed during the paused mode
384
+ createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null );
385
+
386
+ // Cache references to elements
387
+ dom.controls = document.querySelector( '.reveal .controls' );
388
+
389
+ // There can be multiple instances of controls throughout the page
390
+ dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) );
391
+ dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) );
392
+ dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) );
393
+ dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) );
394
+ dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) );
395
+ dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) );
396
+
397
+ }
398
+
399
+ /**
400
+ * Creates an HTML element and returns a reference to it.
401
+ * If the element already exists the existing instance will
402
+ * be returned.
403
+ */
404
+ function createSingletonNode( container, tagname, classname, innerHTML ) {
405
+
406
+ var node = container.querySelector( '.' + classname );
407
+ if( !node ) {
408
+ node = document.createElement( tagname );
409
+ node.classList.add( classname );
410
+ if( innerHTML !== null ) {
411
+ node.innerHTML = innerHTML;
412
+ }
413
+ container.appendChild( node );
414
+ }
415
+ return node;
416
+
417
+ }
418
+
419
+ /**
420
+ * Creates the slide background elements and appends them
421
+ * to the background container. One element is created per
422
+ * slide no matter if the given slide has visible background.
423
+ */
424
+ function createBackgrounds() {
425
+
426
+ if( isPrintingPDF() ) {
427
+ document.body.classList.add( 'print-pdf' );
428
+ }
429
+
430
+ // Clear prior backgrounds
431
+ dom.background.innerHTML = '';
432
+ dom.background.classList.add( 'no-transition' );
433
+
434
+ // Helper method for creating a background element for the
435
+ // given slide
436
+ function _createBackground( slide, container ) {
437
+
438
+ var data = {
439
+ background: slide.getAttribute( 'data-background' ),
440
+ backgroundSize: slide.getAttribute( 'data-background-size' ),
441
+ backgroundImage: slide.getAttribute( 'data-background-image' ),
442
+ backgroundColor: slide.getAttribute( 'data-background-color' ),
443
+ backgroundRepeat: slide.getAttribute( 'data-background-repeat' ),
444
+ backgroundPosition: slide.getAttribute( 'data-background-position' ),
445
+ backgroundTransition: slide.getAttribute( 'data-background-transition' )
446
+ };
447
+
448
+ var element = document.createElement( 'div' );
449
+ element.className = 'slide-background';
450
+
451
+ if( data.background ) {
452
+ // Auto-wrap image urls in url(...)
453
+ if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)$/gi.test( data.background ) ) {
454
+ element.style.backgroundImage = 'url('+ data.background +')';
455
+ }
456
+ else {
457
+ element.style.background = data.background;
458
+ }
459
+ }
460
+
461
+ if( data.background || data.backgroundColor || data.backgroundImage ) {
462
+ element.setAttribute( 'data-background-hash', data.background + data.backgroundSize + data.backgroundImage + data.backgroundColor + data.backgroundRepeat + data.backgroundPosition + data.backgroundTransition );
463
+ }
464
+
465
+ // Additional and optional background properties
466
+ if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize;
467
+ if( data.backgroundImage ) element.style.backgroundImage = 'url("' + data.backgroundImage + '")';
468
+ if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
469
+ if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat;
470
+ if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition;
471
+ if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );
472
+
473
+ container.appendChild( element );
474
+
475
+ return element;
476
+
477
+ }
478
+
479
+ // Iterate over all horizontal slides
480
+ toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) {
481
+
482
+ var backgroundStack;
483
+
484
+ if( isPrintingPDF() ) {
485
+ backgroundStack = _createBackground( slideh, slideh );
486
+ }
487
+ else {
488
+ backgroundStack = _createBackground( slideh, dom.background );
489
+ }
490
+
491
+ // Iterate over all vertical slides
492
+ toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) {
493
+
494
+ if( isPrintingPDF() ) {
495
+ _createBackground( slidev, slidev );
496
+ }
497
+ else {
498
+ _createBackground( slidev, backgroundStack );
499
+ }
500
+
501
+ } );
502
+
503
+ } );
504
+
505
+ // Add parallax background if specified
506
+ if( config.parallaxBackgroundImage ) {
507
+
508
+ dom.background.style.backgroundImage = 'url("' + config.parallaxBackgroundImage + '")';
509
+ dom.background.style.backgroundSize = config.parallaxBackgroundSize;
510
+
511
+ // Make sure the below properties are set on the element - these properties are
512
+ // needed for proper transitions to be set on the element via CSS. To remove
513
+ // annoying background slide-in effect when the presentation starts, apply
514
+ // these properties after short time delay
515
+ setTimeout( function() {
516
+ dom.wrapper.classList.add( 'has-parallax-background' );
517
+ }, 1 );
518
+
519
+ }
520
+ else {
521
+
522
+ dom.background.style.backgroundImage = '';
523
+ dom.wrapper.classList.remove( 'has-parallax-background' );
524
+
525
+ }
526
+
527
+ }
528
+
529
+ /**
530
+ * Applies the configuration settings from the config
531
+ * object. May be called multiple times.
532
+ */
533
+ function configure( options ) {
534
+
535
+ var numberOfSlides = document.querySelectorAll( SLIDES_SELECTOR ).length;
536
+
537
+ dom.wrapper.classList.remove( config.transition );
538
+
539
+ // New config options may be passed when this method
540
+ // is invoked through the API after initialization
541
+ if( typeof options === 'object' ) extend( config, options );
542
+
543
+ // Force linear transition based on browser capabilities
544
+ if( features.transforms3d === false ) config.transition = 'linear';
545
+
546
+ dom.wrapper.classList.add( config.transition );
547
+
548
+ dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
549
+ dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition );
550
+
551
+ dom.controls.style.display = config.controls ? 'block' : 'none';
552
+ dom.progress.style.display = config.progress ? 'block' : 'none';
553
+
554
+ if( config.rtl ) {
555
+ dom.wrapper.classList.add( 'rtl' );
556
+ }
557
+ else {
558
+ dom.wrapper.classList.remove( 'rtl' );
559
+ }
560
+
561
+ if( config.center ) {
562
+ dom.wrapper.classList.add( 'center' );
563
+ }
564
+ else {
565
+ dom.wrapper.classList.remove( 'center' );
566
+ }
567
+
568
+ if( config.mouseWheel ) {
569
+ document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
570
+ document.addEventListener( 'mousewheel', onDocumentMouseScroll, false );
571
+ }
572
+ else {
573
+ document.removeEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
574
+ document.removeEventListener( 'mousewheel', onDocumentMouseScroll, false );
575
+ }
576
+
577
+ // Rolling 3D links
578
+ if( config.rollingLinks ) {
579
+ enableRollingLinks();
580
+ }
581
+ else {
582
+ disableRollingLinks();
583
+ }
584
+
585
+ // Iframe link previews
586
+ if( config.previewLinks ) {
587
+ enablePreviewLinks();
588
+ }
589
+ else {
590
+ disablePreviewLinks();
591
+ enablePreviewLinks( '[data-preview-link]' );
592
+ }
593
+
594
+ // Auto-slide playback controls
595
+ if( numberOfSlides > 1 && config.autoSlide && config.autoSlideStoppable && features.canvas && features.requestAnimationFrame ) {
596
+ autoSlidePlayer = new Playback( dom.wrapper, function() {
597
+ return Math.min( Math.max( ( Date.now() - autoSlideStartTime ) / autoSlide, 0 ), 1 );
598
+ } );
599
+
600
+ autoSlidePlayer.on( 'click', onAutoSlidePlayerClick );
601
+ autoSlidePaused = false;
602
+ }
603
+ else if( autoSlidePlayer ) {
604
+ autoSlidePlayer.destroy();
605
+ autoSlidePlayer = null;
606
+ }
607
+
608
+ // Load the theme in the config, if it's not already loaded
609
+ if( config.theme && dom.theme ) {
610
+ var themeURL = dom.theme.getAttribute( 'href' );
611
+ var themeFinder = /[^\/]*?(?=\.css)/;
612
+ var themeName = themeURL.match(themeFinder)[0];
613
+
614
+ if( config.theme !== themeName ) {
615
+ themeURL = themeURL.replace(themeFinder, config.theme);
616
+ dom.theme.setAttribute( 'href', themeURL );
617
+ }
618
+ }
619
+
620
+ sync();
621
+
622
+ }
623
+
624
+ /**
625
+ * Binds all event listeners.
626
+ */
627
+ function addEventListeners() {
628
+
629
+ eventsAreBound = true;
630
+
631
+ window.addEventListener( 'hashchange', onWindowHashChange, false );
632
+ window.addEventListener( 'resize', onWindowResize, false );
633
+
634
+ if( config.touch ) {
635
+ dom.wrapper.addEventListener( 'touchstart', onTouchStart, false );
636
+ dom.wrapper.addEventListener( 'touchmove', onTouchMove, false );
637
+ dom.wrapper.addEventListener( 'touchend', onTouchEnd, false );
638
+
639
+ // Support pointer-style touch interaction as well
640
+ if( window.navigator.msPointerEnabled ) {
641
+ dom.wrapper.addEventListener( 'MSPointerDown', onPointerDown, false );
642
+ dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false );
643
+ dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false );
644
+ }
645
+ }
646
+
647
+ if( config.keyboard ) {
648
+ document.addEventListener( 'keydown', onDocumentKeyDown, false );
649
+ }
650
+
651
+ if( config.progress && dom.progress ) {
652
+ dom.progress.addEventListener( 'click', onProgressClicked, false );
653
+ }
654
+
655
+ if( config.focusBodyOnPageVisiblityChange ) {
656
+ var visibilityChange;
657
+
658
+ if( 'hidden' in document ) {
659
+ visibilityChange = 'visibilitychange';
660
+ }
661
+ else if( 'msHidden' in document ) {
662
+ visibilityChange = 'msvisibilitychange';
663
+ }
664
+ else if( 'webkitHidden' in document ) {
665
+ visibilityChange = 'webkitvisibilitychange';
666
+ }
667
+
668
+ if( visibilityChange ) {
669
+ document.addEventListener( visibilityChange, onPageVisibilityChange, false );
670
+ }
671
+ }
672
+
673
+ [ 'touchstart', 'click' ].forEach( function( eventName ) {
674
+ dom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } );
675
+ dom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } );
676
+ dom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } );
677
+ dom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } );
678
+ dom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } );
679
+ dom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } );
680
+ } );
681
+
682
+ }
683
+
684
+ /**
685
+ * Unbinds all event listeners.
686
+ */
687
+ function removeEventListeners() {
688
+
689
+ eventsAreBound = false;
690
+
691
+ document.removeEventListener( 'keydown', onDocumentKeyDown, false );
692
+ window.removeEventListener( 'hashchange', onWindowHashChange, false );
693
+ window.removeEventListener( 'resize', onWindowResize, false );
694
+
695
+ dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false );
696
+ dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false );
697
+ dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false );
698
+
699
+ if( window.navigator.msPointerEnabled ) {
700
+ dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false );
701
+ dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false );
702
+ dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false );
703
+ }
704
+
705
+ if ( config.progress && dom.progress ) {
706
+ dom.progress.removeEventListener( 'click', onProgressClicked, false );
707
+ }
708
+
709
+ [ 'touchstart', 'click' ].forEach( function( eventName ) {
710
+ dom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } );
711
+ dom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } );
712
+ dom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } );
713
+ dom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } );
714
+ dom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } );
715
+ dom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } );
716
+ } );
717
+
718
+ }
719
+
720
+ /**
721
+ * Extend object a with the properties of object b.
722
+ * If there's a conflict, object b takes precedence.
723
+ */
724
+ function extend( a, b ) {
725
+
726
+ for( var i in b ) {
727
+ a[ i ] = b[ i ];
728
+ }
729
+
730
+ }
731
+
732
+ /**
733
+ * Converts the target object to an array.
734
+ */
735
+ function toArray( o ) {
736
+
737
+ return Array.prototype.slice.call( o );
738
+
739
+ }
740
+
741
+ /**
742
+ * Measures the distance in pixels between point a
743
+ * and point b.
744
+ *
745
+ * @param {Object} a point with x/y properties
746
+ * @param {Object} b point with x/y properties
747
+ */
748
+ function distanceBetween( a, b ) {
749
+
750
+ var dx = a.x - b.x,
751
+ dy = a.y - b.y;
752
+
753
+ return Math.sqrt( dx*dx + dy*dy );
754
+
755
+ }
756
+
757
+ /**
758
+ * Applies a CSS transform to the target element.
759
+ */
760
+ function transformElement( element, transform ) {
761
+
762
+ element.style.WebkitTransform = transform;
763
+ element.style.MozTransform = transform;
764
+ element.style.msTransform = transform;
765
+ element.style.OTransform = transform;
766
+ element.style.transform = transform;
767
+
768
+ }
769
+
770
+ /**
771
+ * Retrieves the height of the given element by looking
772
+ * at the position and height of its immediate children.
773
+ */
774
+ function getAbsoluteHeight( element ) {
775
+
776
+ var height = 0;
777
+
778
+ if( element ) {
779
+ var absoluteChildren = 0;
780
+
781
+ toArray( element.childNodes ).forEach( function( child ) {
782
+
783
+ if( typeof child.offsetTop === 'number' && child.style ) {
784
+ // Count # of abs children
785
+ if( child.style.position === 'absolute' ) {
786
+ absoluteChildren += 1;
787
+ }
788
+
789
+ height = Math.max( height, child.offsetTop + child.offsetHeight );
790
+ }
791
+
792
+ } );
793
+
794
+ // If there are no absolute children, use offsetHeight
795
+ if( absoluteChildren === 0 ) {
796
+ height = element.offsetHeight;
797
+ }
798
+
799
+ }
800
+
801
+ return height;
802
+
803
+ }
804
+
805
+ /**
806
+ * Returns the remaining height within the parent of the
807
+ * target element after subtracting the height of all
808
+ * siblings.
809
+ *
810
+ * remaining height = [parent height] - [ siblings height]
811
+ */
812
+ function getRemainingHeight( element, height ) {
813
+
814
+ height = height || 0;
815
+
816
+ if( element ) {
817
+ var parent = element.parentNode;
818
+ var siblings = parent.childNodes;
819
+
820
+ // Subtract the height of each sibling
821
+ toArray( siblings ).forEach( function( sibling ) {
822
+
823
+ if( typeof sibling.offsetHeight === 'number' && sibling !== element ) {
824
+
825
+ var styles = window.getComputedStyle( sibling ),
826
+ marginTop = parseInt( styles.marginTop, 10 ),
827
+ marginBottom = parseInt( styles.marginBottom, 10 );
828
+
829
+ height -= sibling.offsetHeight + marginTop + marginBottom;
830
+
831
+ }
832
+
833
+ } );
834
+
835
+ var elementStyles = window.getComputedStyle( element );
836
+
837
+ // Subtract the margins of the target element
838
+ height -= parseInt( elementStyles.marginTop, 10 ) +
839
+ parseInt( elementStyles.marginBottom, 10 );
840
+
841
+ }
842
+
843
+ return height;
844
+
845
+ }
846
+
847
+ /**
848
+ * Checks if this instance is being used to print a PDF.
849
+ */
850
+ function isPrintingPDF() {
851
+
852
+ return ( /print-pdf/gi ).test( window.location.search );
853
+
854
+ }
855
+
856
+ /**
857
+ * Hides the address bar if we're on a mobile device.
858
+ */
859
+ function hideAddressBar() {
860
+
861
+ if( config.hideAddressBar && isMobileDevice ) {
862
+ // Events that should trigger the address bar to hide
863
+ window.addEventListener( 'load', removeAddressBar, false );
864
+ window.addEventListener( 'orientationchange', removeAddressBar, false );
865
+ }
866
+
867
+ }
868
+
869
+ /**
870
+ * Causes the address bar to hide on mobile devices,
871
+ * more vertical space ftw.
872
+ */
873
+ function removeAddressBar() {
874
+
875
+ setTimeout( function() {
876
+ window.scrollTo( 0, 1 );
877
+ }, 10 );
878
+
879
+ }
880
+
881
+ /**
882
+ * Dispatches an event of the specified type from the
883
+ * reveal DOM element.
884
+ */
885
+ function dispatchEvent( type, properties ) {
886
+
887
+ var event = document.createEvent( "HTMLEvents", 1, 2 );
888
+ event.initEvent( type, true, true );
889
+ extend( event, properties );
890
+ dom.wrapper.dispatchEvent( event );
891
+
892
+ }
893
+
894
+ /**
895
+ * Wrap all links in 3D goodness.
896
+ */
897
+ function enableRollingLinks() {
898
+
899
+ if( features.transforms3d && !( 'msPerspective' in document.body.style ) ) {
900
+ var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a:not(.image)' );
901
+
902
+ for( var i = 0, len = anchors.length; i < len; i++ ) {
903
+ var anchor = anchors[i];
904
+
905
+ if( anchor.textContent && !anchor.querySelector( '*' ) && ( !anchor.className || !anchor.classList.contains( anchor, 'roll' ) ) ) {
906
+ var span = document.createElement('span');
907
+ span.setAttribute('data-title', anchor.text);
908
+ span.innerHTML = anchor.innerHTML;
909
+
910
+ anchor.classList.add( 'roll' );
911
+ anchor.innerHTML = '';
912
+ anchor.appendChild(span);
913
+ }
914
+ }
915
+ }
916
+
917
+ }
918
+
919
+ /**
920
+ * Unwrap all 3D links.
921
+ */
922
+ function disableRollingLinks() {
923
+
924
+ var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a.roll' );
925
+
926
+ for( var i = 0, len = anchors.length; i < len; i++ ) {
927
+ var anchor = anchors[i];
928
+ var span = anchor.querySelector( 'span' );
929
+
930
+ if( span ) {
931
+ anchor.classList.remove( 'roll' );
932
+ anchor.innerHTML = span.innerHTML;
933
+ }
934
+ }
935
+
936
+ }
937
+
938
+ /**
939
+ * Bind preview frame links.
940
+ */
941
+ function enablePreviewLinks( selector ) {
942
+
943
+ var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) );
944
+
945
+ anchors.forEach( function( element ) {
946
+ if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
947
+ element.addEventListener( 'click', onPreviewLinkClicked, false );
948
+ }
949
+ } );
950
+
951
+ }
952
+
953
+ /**
954
+ * Unbind preview frame links.
955
+ */
956
+ function disablePreviewLinks() {
957
+
958
+ var anchors = toArray( document.querySelectorAll( 'a' ) );
959
+
960
+ anchors.forEach( function( element ) {
961
+ if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
962
+ element.removeEventListener( 'click', onPreviewLinkClicked, false );
963
+ }
964
+ } );
965
+
966
+ }
967
+
968
+ /**
969
+ * Opens a preview window for the target URL.
970
+ */
971
+ function openPreview( url ) {
972
+
973
+ closePreview();
974
+
975
+ dom.preview = document.createElement( 'div' );
976
+ dom.preview.classList.add( 'preview-link-overlay' );
977
+ dom.wrapper.appendChild( dom.preview );
978
+
979
+ dom.preview.innerHTML = [
980
+ '<header>',
981
+ '<a class="close" href="#"><span class="icon"></span></a>',
982
+ '<a class="external" href="'+ url +'" target="_blank"><span class="icon"></span></a>',
983
+ '</header>',
984
+ '<div class="spinner"></div>',
985
+ '<div class="viewport">',
986
+ '<iframe src="'+ url +'"></iframe>',
987
+ '</div>'
988
+ ].join('');
989
+
990
+ dom.preview.querySelector( 'iframe' ).addEventListener( 'load', function( event ) {
991
+ dom.preview.classList.add( 'loaded' );
992
+ }, false );
993
+
994
+ dom.preview.querySelector( '.close' ).addEventListener( 'click', function( event ) {
995
+ closePreview();
996
+ event.preventDefault();
997
+ }, false );
998
+
999
+ dom.preview.querySelector( '.external' ).addEventListener( 'click', function( event ) {
1000
+ closePreview();
1001
+ }, false );
1002
+
1003
+ setTimeout( function() {
1004
+ dom.preview.classList.add( 'visible' );
1005
+ }, 1 );
1006
+
1007
+ }
1008
+
1009
+ /**
1010
+ * Closes the iframe preview window.
1011
+ */
1012
+ function closePreview() {
1013
+
1014
+ if( dom.preview ) {
1015
+ dom.preview.setAttribute( 'src', '' );
1016
+ dom.preview.parentNode.removeChild( dom.preview );
1017
+ dom.preview = null;
1018
+ }
1019
+
1020
+ }
1021
+
1022
+ /**
1023
+ * Applies JavaScript-controlled layout rules to the
1024
+ * presentation.
1025
+ */
1026
+ function layout() {
1027
+
1028
+ if( dom.wrapper && !isPrintingPDF() ) {
1029
+
1030
+ // Available space to scale within
1031
+ var availableWidth = dom.wrapper.offsetWidth,
1032
+ availableHeight = dom.wrapper.offsetHeight;
1033
+
1034
+ // Reduce available space by margin
1035
+ availableWidth -= ( availableHeight * config.margin );
1036
+ availableHeight -= ( availableHeight * config.margin );
1037
+
1038
+ // Dimensions of the content
1039
+ var slideWidth = config.width,
1040
+ slideHeight = config.height,
1041
+ slidePadding = 20; // TODO Dig this out of DOM
1042
+
1043
+ // Layout the contents of the slides
1044
+ layoutSlideContents( config.width, config.height, slidePadding );
1045
+
1046
+ // Slide width may be a percentage of available width
1047
+ if( typeof slideWidth === 'string' && /%$/.test( slideWidth ) ) {
1048
+ slideWidth = parseInt( slideWidth, 10 ) / 100 * availableWidth;
1049
+ }
1050
+
1051
+ // Slide height may be a percentage of available height
1052
+ if( typeof slideHeight === 'string' && /%$/.test( slideHeight ) ) {
1053
+ slideHeight = parseInt( slideHeight, 10 ) / 100 * availableHeight;
1054
+ }
1055
+
1056
+ dom.slides.style.width = slideWidth + 'px';
1057
+ dom.slides.style.height = slideHeight + 'px';
1058
+
1059
+ // Determine scale of content to fit within available space
1060
+ scale = Math.min( availableWidth / slideWidth, availableHeight / slideHeight );
1061
+
1062
+ // Respect max/min scale settings
1063
+ scale = Math.max( scale, config.minScale );
1064
+ scale = Math.min( scale, config.maxScale );
1065
+
1066
+ // Prefer applying scale via zoom since Chrome blurs scaled content
1067
+ // with nested transforms
1068
+ if( typeof dom.slides.style.zoom !== 'undefined' && !navigator.userAgent.match( /(iphone|ipod|ipad|android)/gi ) ) {
1069
+ dom.slides.style.zoom = scale;
1070
+ }
1071
+ // Apply scale transform as a fallback
1072
+ else {
1073
+ transformElement( dom.slides, 'translate(-50%, -50%) scale('+ scale +') translate(50%, 50%)' );
1074
+ }
1075
+
1076
+ // Select all slides, vertical and horizontal
1077
+ var slides = toArray( document.querySelectorAll( SLIDES_SELECTOR ) );
1078
+
1079
+ for( var i = 0, len = slides.length; i < len; i++ ) {
1080
+ var slide = slides[ i ];
1081
+
1082
+ // Don't bother updating invisible slides
1083
+ if( slide.style.display === 'none' ) {
1084
+ continue;
1085
+ }
1086
+
1087
+ if( config.center || slide.classList.contains( 'center' ) ) {
1088
+ // Vertical stacks are not centred since their section
1089
+ // children will be
1090
+ if( slide.classList.contains( 'stack' ) ) {
1091
+ slide.style.top = 0;
1092
+ }
1093
+ else {
1094
+ slide.style.top = Math.max( - ( getAbsoluteHeight( slide ) / 2 ) - slidePadding, -slideHeight / 2 ) + 'px';
1095
+ }
1096
+ }
1097
+ else {
1098
+ slide.style.top = '';
1099
+ }
1100
+
1101
+ }
1102
+
1103
+ updateProgress();
1104
+ updateParallax();
1105
+
1106
+ }
1107
+
1108
+ }
1109
+
1110
+ /**
1111
+ * Applies layout logic to the contents of all slides in
1112
+ * the presentation.
1113
+ */
1114
+ function layoutSlideContents( width, height, padding ) {
1115
+
1116
+ // Handle sizing of elements with the 'stretch' class
1117
+ toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) {
1118
+
1119
+ // Determine how much vertical space we can use
1120
+ var remainingHeight = getRemainingHeight( element, ( height - ( padding * 2 ) ) );
1121
+
1122
+ // Consider the aspect ratio of media elements
1123
+ if( /(img|video)/gi.test( element.nodeName ) ) {
1124
+ var nw = element.naturalWidth || element.videoWidth,
1125
+ nh = element.naturalHeight || element.videoHeight;
1126
+
1127
+ var es = Math.min( width / nw, remainingHeight / nh );
1128
+
1129
+ element.style.width = ( nw * es ) + 'px';
1130
+ element.style.height = ( nh * es ) + 'px';
1131
+
1132
+ }
1133
+ else {
1134
+ element.style.width = width + 'px';
1135
+ element.style.height = remainingHeight + 'px';
1136
+ }
1137
+
1138
+ } );
1139
+
1140
+ }
1141
+
1142
+ /**
1143
+ * Stores the vertical index of a stack so that the same
1144
+ * vertical slide can be selected when navigating to and
1145
+ * from the stack.
1146
+ *
1147
+ * @param {HTMLElement} stack The vertical stack element
1148
+ * @param {int} v Index to memorize
1149
+ */
1150
+ function setPreviousVerticalIndex( stack, v ) {
1151
+
1152
+ if( typeof stack === 'object' && typeof stack.setAttribute === 'function' ) {
1153
+ stack.setAttribute( 'data-previous-indexv', v || 0 );
1154
+ }
1155
+
1156
+ }
1157
+
1158
+ /**
1159
+ * Retrieves the vertical index which was stored using
1160
+ * #setPreviousVerticalIndex() or 0 if no previous index
1161
+ * exists.
1162
+ *
1163
+ * @param {HTMLElement} stack The vertical stack element
1164
+ */
1165
+ function getPreviousVerticalIndex( stack ) {
1166
+
1167
+ if( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) {
1168
+ // Prefer manually defined start-indexv
1169
+ var attributeName = stack.hasAttribute( 'data-start-indexv' ) ? 'data-start-indexv' : 'data-previous-indexv';
1170
+
1171
+ return parseInt( stack.getAttribute( attributeName ) || 0, 10 );
1172
+ }
1173
+
1174
+ return 0;
1175
+
1176
+ }
1177
+
1178
+ /**
1179
+ * Displays the overview of slides (quick nav) by
1180
+ * scaling down and arranging all slide elements.
1181
+ *
1182
+ * Experimental feature, might be dropped if perf
1183
+ * can't be improved.
1184
+ */
1185
+ function activateOverview() {
1186
+
1187
+ // Only proceed if enabled in config
1188
+ if( config.overview ) {
1189
+
1190
+ // Don't auto-slide while in overview mode
1191
+ cancelAutoSlide();
1192
+
1193
+ var wasActive = dom.wrapper.classList.contains( 'overview' );
1194
+
1195
+ // Vary the depth of the overview based on screen size
1196
+ var depth = window.innerWidth < 400 ? 1000 : 2500;
1197
+
1198
+ dom.wrapper.classList.add( 'overview' );
1199
+ dom.wrapper.classList.remove( 'overview-deactivating' );
1200
+
1201
+ clearTimeout( activateOverviewTimeout );
1202
+ clearTimeout( deactivateOverviewTimeout );
1203
+
1204
+ // Not the pretties solution, but need to let the overview
1205
+ // class apply first so that slides are measured accurately
1206
+ // before we can position them
1207
+ activateOverviewTimeout = setTimeout( function() {
1208
+
1209
+ var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
1210
+
1211
+ for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) {
1212
+ var hslide = horizontalSlides[i],
1213
+ hoffset = config.rtl ? -105 : 105;
1214
+
1215
+ hslide.setAttribute( 'data-index-h', i );
1216
+
1217
+ // Apply CSS transform
1218
+ transformElement( hslide, 'translateZ(-'+ depth +'px) translate(' + ( ( i - indexh ) * hoffset ) + '%, 0%)' );
1219
+
1220
+ if( hslide.classList.contains( 'stack' ) ) {
1221
+
1222
+ var verticalSlides = hslide.querySelectorAll( 'section' );
1223
+
1224
+ for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) {
1225
+ var verticalIndex = i === indexh ? indexv : getPreviousVerticalIndex( hslide );
1226
+
1227
+ var vslide = verticalSlides[j];
1228
+
1229
+ vslide.setAttribute( 'data-index-h', i );
1230
+ vslide.setAttribute( 'data-index-v', j );
1231
+
1232
+ // Apply CSS transform
1233
+ transformElement( vslide, 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)' );
1234
+
1235
+ // Navigate to this slide on click
1236
+ vslide.addEventListener( 'click', onOverviewSlideClicked, true );
1237
+ }
1238
+
1239
+ }
1240
+ else {
1241
+
1242
+ // Navigate to this slide on click
1243
+ hslide.addEventListener( 'click', onOverviewSlideClicked, true );
1244
+
1245
+ }
1246
+ }
1247
+
1248
+ updateSlidesVisibility();
1249
+
1250
+ layout();
1251
+
1252
+ if( !wasActive ) {
1253
+ // Notify observers of the overview showing
1254
+ dispatchEvent( 'overviewshown', {
1255
+ 'indexh': indexh,
1256
+ 'indexv': indexv,
1257
+ 'currentSlide': currentSlide
1258
+ } );
1259
+ }
1260
+
1261
+ }, 10 );
1262
+
1263
+ }
1264
+
1265
+ }
1266
+
1267
+ /**
1268
+ * Exits the slide overview and enters the currently
1269
+ * active slide.
1270
+ */
1271
+ function deactivateOverview() {
1272
+
1273
+ // Only proceed if enabled in config
1274
+ if( config.overview ) {
1275
+
1276
+ clearTimeout( activateOverviewTimeout );
1277
+ clearTimeout( deactivateOverviewTimeout );
1278
+
1279
+ dom.wrapper.classList.remove( 'overview' );
1280
+
1281
+ // Temporarily add a class so that transitions can do different things
1282
+ // depending on whether they are exiting/entering overview, or just
1283
+ // moving from slide to slide
1284
+ dom.wrapper.classList.add( 'overview-deactivating' );
1285
+
1286
+ deactivateOverviewTimeout = setTimeout( function () {
1287
+ dom.wrapper.classList.remove( 'overview-deactivating' );
1288
+ }, 1 );
1289
+
1290
+ // Select all slides
1291
+ toArray( document.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
1292
+ // Resets all transforms to use the external styles
1293
+ transformElement( slide, '' );
1294
+
1295
+ slide.removeEventListener( 'click', onOverviewSlideClicked, true );
1296
+ } );
1297
+
1298
+ slide( indexh, indexv );
1299
+
1300
+ cueAutoSlide();
1301
+
1302
+ // Notify observers of the overview hiding
1303
+ dispatchEvent( 'overviewhidden', {
1304
+ 'indexh': indexh,
1305
+ 'indexv': indexv,
1306
+ 'currentSlide': currentSlide
1307
+ } );
1308
+
1309
+ }
1310
+ }
1311
+
1312
+ /**
1313
+ * Toggles the slide overview mode on and off.
1314
+ *
1315
+ * @param {Boolean} override Optional flag which overrides the
1316
+ * toggle logic and forcibly sets the desired state. True means
1317
+ * overview is open, false means it's closed.
1318
+ */
1319
+ function toggleOverview( override ) {
1320
+
1321
+ if( typeof override === 'boolean' ) {
1322
+ override ? activateOverview() : deactivateOverview();
1323
+ }
1324
+ else {
1325
+ isOverview() ? deactivateOverview() : activateOverview();
1326
+ }
1327
+
1328
+ }
1329
+
1330
+ /**
1331
+ * Checks if the overview is currently active.
1332
+ *
1333
+ * @return {Boolean} true if the overview is active,
1334
+ * false otherwise
1335
+ */
1336
+ function isOverview() {
1337
+
1338
+ return dom.wrapper.classList.contains( 'overview' );
1339
+
1340
+ }
1341
+
1342
+ /**
1343
+ * Checks if the current or specified slide is vertical
1344
+ * (nested within another slide).
1345
+ *
1346
+ * @param {HTMLElement} slide [optional] The slide to check
1347
+ * orientation of
1348
+ */
1349
+ function isVerticalSlide( slide ) {
1350
+
1351
+ // Prefer slide argument, otherwise use current slide
1352
+ slide = slide ? slide : currentSlide;
1353
+
1354
+ return slide && slide.parentNode && !!slide.parentNode.nodeName.match( /section/i );
1355
+
1356
+ }
1357
+
1358
+ /**
1359
+ * Handling the fullscreen functionality via the fullscreen API
1360
+ *
1361
+ * @see http://fullscreen.spec.whatwg.org/
1362
+ * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
1363
+ */
1364
+ function enterFullscreen() {
1365
+
1366
+ var element = document.body;
1367
+
1368
+ // Check which implementation is available
1369
+ var requestMethod = element.requestFullScreen ||
1370
+ element.webkitRequestFullscreen ||
1371
+ element.webkitRequestFullScreen ||
1372
+ element.mozRequestFullScreen ||
1373
+ element.msRequestFullScreen;
1374
+
1375
+ if( requestMethod ) {
1376
+ requestMethod.apply( element );
1377
+ }
1378
+
1379
+ }
1380
+
1381
+ /**
1382
+ * Enters the paused mode which fades everything on screen to
1383
+ * black.
1384
+ */
1385
+ function pause() {
1386
+
1387
+ var wasPaused = dom.wrapper.classList.contains( 'paused' );
1388
+
1389
+ cancelAutoSlide();
1390
+ dom.wrapper.classList.add( 'paused' );
1391
+
1392
+ if( wasPaused === false ) {
1393
+ dispatchEvent( 'paused' );
1394
+ }
1395
+
1396
+ }
1397
+
1398
+ /**
1399
+ * Exits from the paused mode.
1400
+ */
1401
+ function resume() {
1402
+
1403
+ var wasPaused = dom.wrapper.classList.contains( 'paused' );
1404
+ dom.wrapper.classList.remove( 'paused' );
1405
+
1406
+ cueAutoSlide();
1407
+
1408
+ if( wasPaused ) {
1409
+ dispatchEvent( 'resumed' );
1410
+ }
1411
+
1412
+ }
1413
+
1414
+ /**
1415
+ * Toggles the paused mode on and off.
1416
+ */
1417
+ function togglePause() {
1418
+
1419
+ if( isPaused() ) {
1420
+ resume();
1421
+ }
1422
+ else {
1423
+ pause();
1424
+ }
1425
+
1426
+ }
1427
+
1428
+ /**
1429
+ * Checks if we are currently in the paused mode.
1430
+ */
1431
+ function isPaused() {
1432
+
1433
+ return dom.wrapper.classList.contains( 'paused' );
1434
+
1435
+ }
1436
+
1437
+ /**
1438
+ * Steps from the current point in the presentation to the
1439
+ * slide which matches the specified horizontal and vertical
1440
+ * indices.
1441
+ *
1442
+ * @param {int} h Horizontal index of the target slide
1443
+ * @param {int} v Vertical index of the target slide
1444
+ * @param {int} f Optional index of a fragment within the
1445
+ * target slide to activate
1446
+ * @param {int} o Optional origin for use in multimaster environments
1447
+ */
1448
+ function slide( h, v, f, o ) {
1449
+
1450
+ // Remember where we were at before
1451
+ previousSlide = currentSlide;
1452
+
1453
+ // Query all horizontal slides in the deck
1454
+ var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
1455
+
1456
+ // If no vertical index is specified and the upcoming slide is a
1457
+ // stack, resume at its previous vertical index
1458
+ if( v === undefined ) {
1459
+ v = getPreviousVerticalIndex( horizontalSlides[ h ] );
1460
+ }
1461
+
1462
+ // If we were on a vertical stack, remember what vertical index
1463
+ // it was on so we can resume at the same position when returning
1464
+ if( previousSlide && previousSlide.parentNode && previousSlide.parentNode.classList.contains( 'stack' ) ) {
1465
+ setPreviousVerticalIndex( previousSlide.parentNode, indexv );
1466
+ }
1467
+
1468
+ // Remember the state before this slide
1469
+ var stateBefore = state.concat();
1470
+
1471
+ // Reset the state array
1472
+ state.length = 0;
1473
+
1474
+ var indexhBefore = indexh || 0,
1475
+ indexvBefore = indexv || 0;
1476
+
1477
+ // Activate and transition to the new slide
1478
+ indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h );
1479
+ indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v );
1480
+
1481
+ // Update the visibility of slides now that the indices have changed
1482
+ updateSlidesVisibility();
1483
+
1484
+ layout();
1485
+
1486
+ // Apply the new state
1487
+ stateLoop: for( var i = 0, len = state.length; i < len; i++ ) {
1488
+ // Check if this state existed on the previous slide. If it
1489
+ // did, we will avoid adding it repeatedly
1490
+ for( var j = 0; j < stateBefore.length; j++ ) {
1491
+ if( stateBefore[j] === state[i] ) {
1492
+ stateBefore.splice( j, 1 );
1493
+ continue stateLoop;
1494
+ }
1495
+ }
1496
+
1497
+ document.documentElement.classList.add( state[i] );
1498
+
1499
+ // Dispatch custom event matching the state's name
1500
+ dispatchEvent( state[i] );
1501
+ }
1502
+
1503
+ // Clean up the remains of the previous state
1504
+ while( stateBefore.length ) {
1505
+ document.documentElement.classList.remove( stateBefore.pop() );
1506
+ }
1507
+
1508
+ // If the overview is active, re-activate it to update positions
1509
+ if( isOverview() ) {
1510
+ activateOverview();
1511
+ }
1512
+
1513
+ // Find the current horizontal slide and any possible vertical slides
1514
+ // within it
1515
+ var currentHorizontalSlide = horizontalSlides[ indexh ],
1516
+ currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' );
1517
+
1518
+ // Store references to the previous and current slides
1519
+ currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide;
1520
+
1521
+ // Show fragment, if specified
1522
+ if( typeof f !== 'undefined' ) {
1523
+ navigateFragment( f );
1524
+ }
1525
+
1526
+ // Dispatch an event if the slide changed
1527
+ var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore );
1528
+ if( slideChanged ) {
1529
+ dispatchEvent( 'slidechanged', {
1530
+ 'indexh': indexh,
1531
+ 'indexv': indexv,
1532
+ 'previousSlide': previousSlide,
1533
+ 'currentSlide': currentSlide,
1534
+ 'origin': o
1535
+ } );
1536
+ }
1537
+ else {
1538
+ // Ensure that the previous slide is never the same as the current
1539
+ previousSlide = null;
1540
+ }
1541
+
1542
+ // Solves an edge case where the previous slide maintains the
1543
+ // 'present' class when navigating between adjacent vertical
1544
+ // stacks
1545
+ if( previousSlide ) {
1546
+ previousSlide.classList.remove( 'present' );
1547
+
1548
+ // Reset all slides upon navigate to home
1549
+ // Issue: #285
1550
+ if ( document.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) {
1551
+ // Launch async task
1552
+ setTimeout( function () {
1553
+ var slides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i;
1554
+ for( i in slides ) {
1555
+ if( slides[i] ) {
1556
+ // Reset stack
1557
+ setPreviousVerticalIndex( slides[i], 0 );
1558
+ }
1559
+ }
1560
+ }, 0 );
1561
+ }
1562
+ }
1563
+
1564
+ // Handle embedded content
1565
+ if( slideChanged ) {
1566
+ stopEmbeddedContent( previousSlide );
1567
+ startEmbeddedContent( currentSlide );
1568
+ }
1569
+
1570
+ updateControls();
1571
+ updateProgress();
1572
+ updateBackground();
1573
+ updateParallax();
1574
+ updateSlideNumber();
1575
+
1576
+ // Update the URL hash
1577
+ writeURL();
1578
+
1579
+ cueAutoSlide();
1580
+
1581
+ }
1582
+
1583
+ /**
1584
+ * Syncs the presentation with the current DOM. Useful
1585
+ * when new slides or control elements are added or when
1586
+ * the configuration has changed.
1587
+ */
1588
+ function sync() {
1589
+
1590
+ // Subscribe to input
1591
+ removeEventListeners();
1592
+ addEventListeners();
1593
+
1594
+ // Force a layout to make sure the current config is accounted for
1595
+ layout();
1596
+
1597
+ // Reflect the current autoSlide value
1598
+ autoSlide = config.autoSlide;
1599
+
1600
+ // Start auto-sliding if it's enabled
1601
+ cueAutoSlide();
1602
+
1603
+ // Re-create the slide backgrounds
1604
+ createBackgrounds();
1605
+
1606
+ sortAllFragments();
1607
+
1608
+ updateControls();
1609
+ updateProgress();
1610
+ updateBackground( true );
1611
+ updateSlideNumber();
1612
+
1613
+ }
1614
+
1615
+ /**
1616
+ * Resets all vertical slides so that only the first
1617
+ * is visible.
1618
+ */
1619
+ function resetVerticalSlides() {
1620
+
1621
+ var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
1622
+ horizontalSlides.forEach( function( horizontalSlide ) {
1623
+
1624
+ var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );
1625
+ verticalSlides.forEach( function( verticalSlide, y ) {
1626
+
1627
+ if( y > 0 ) {
1628
+ verticalSlide.classList.remove( 'present' );
1629
+ verticalSlide.classList.remove( 'past' );
1630
+ verticalSlide.classList.add( 'future' );
1631
+ }
1632
+
1633
+ } );
1634
+
1635
+ } );
1636
+
1637
+ }
1638
+
1639
+ /**
1640
+ * Sorts and formats all of fragments in the
1641
+ * presentation.
1642
+ */
1643
+ function sortAllFragments() {
1644
+
1645
+ var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
1646
+ horizontalSlides.forEach( function( horizontalSlide ) {
1647
+
1648
+ var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );
1649
+ verticalSlides.forEach( function( verticalSlide, y ) {
1650
+
1651
+ sortFragments( verticalSlide.querySelectorAll( '.fragment' ) );
1652
+
1653
+ } );
1654
+
1655
+ if( verticalSlides.length === 0 ) sortFragments( horizontalSlide.querySelectorAll( '.fragment' ) );
1656
+
1657
+ } );
1658
+
1659
+ }
1660
+
1661
+ /**
1662
+ * Updates one dimension of slides by showing the slide
1663
+ * with the specified index.
1664
+ *
1665
+ * @param {String} selector A CSS selector that will fetch
1666
+ * the group of slides we are working with
1667
+ * @param {Number} index The index of the slide that should be
1668
+ * shown
1669
+ *
1670
+ * @return {Number} The index of the slide that is now shown,
1671
+ * might differ from the passed in index if it was out of
1672
+ * bounds.
1673
+ */
1674
+ function updateSlides( selector, index ) {
1675
+
1676
+ // Select all slides and convert the NodeList result to
1677
+ // an array
1678
+ var slides = toArray( document.querySelectorAll( selector ) ),
1679
+ slidesLength = slides.length;
1680
+
1681
+ if( slidesLength ) {
1682
+
1683
+ // Should the index loop?
1684
+ if( config.loop ) {
1685
+ index %= slidesLength;
1686
+
1687
+ if( index < 0 ) {
1688
+ index = slidesLength + index;
1689
+ }
1690
+ }
1691
+
1692
+ // Enforce max and minimum index bounds
1693
+ index = Math.max( Math.min( index, slidesLength - 1 ), 0 );
1694
+
1695
+ for( var i = 0; i < slidesLength; i++ ) {
1696
+ var element = slides[i];
1697
+
1698
+ var reverse = config.rtl && !isVerticalSlide( element );
1699
+
1700
+ element.classList.remove( 'past' );
1701
+ element.classList.remove( 'present' );
1702
+ element.classList.remove( 'future' );
1703
+
1704
+ // http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute
1705
+ element.setAttribute( 'hidden', '' );
1706
+
1707
+ if( i < index ) {
1708
+ // Any element previous to index is given the 'past' class
1709
+ element.classList.add( reverse ? 'future' : 'past' );
1710
+
1711
+ var pastFragments = toArray( element.querySelectorAll( '.fragment' ) );
1712
+
1713
+ // Show all fragments on prior slides
1714
+ while( pastFragments.length ) {
1715
+ var pastFragment = pastFragments.pop();
1716
+ pastFragment.classList.add( 'visible' );
1717
+ pastFragment.classList.remove( 'current-fragment' );
1718
+ }
1719
+ }
1720
+ else if( i > index ) {
1721
+ // Any element subsequent to index is given the 'future' class
1722
+ element.classList.add( reverse ? 'past' : 'future' );
1723
+
1724
+ var futureFragments = toArray( element.querySelectorAll( '.fragment.visible' ) );
1725
+
1726
+ // No fragments in future slides should be visible ahead of time
1727
+ while( futureFragments.length ) {
1728
+ var futureFragment = futureFragments.pop();
1729
+ futureFragment.classList.remove( 'visible' );
1730
+ futureFragment.classList.remove( 'current-fragment' );
1731
+ }
1732
+ }
1733
+
1734
+ // If this element contains vertical slides
1735
+ if( element.querySelector( 'section' ) ) {
1736
+ element.classList.add( 'stack' );
1737
+ }
1738
+ }
1739
+
1740
+ // Mark the current slide as present
1741
+ slides[index].classList.add( 'present' );
1742
+ slides[index].removeAttribute( 'hidden' );
1743
+
1744
+ // If this slide has a state associated with it, add it
1745
+ // onto the current state of the deck
1746
+ var slideState = slides[index].getAttribute( 'data-state' );
1747
+ if( slideState ) {
1748
+ state = state.concat( slideState.split( ' ' ) );
1749
+ }
1750
+
1751
+ }
1752
+ else {
1753
+ // Since there are no slides we can't be anywhere beyond the
1754
+ // zeroth index
1755
+ index = 0;
1756
+ }
1757
+
1758
+ return index;
1759
+
1760
+ }
1761
+
1762
+ /**
1763
+ * Optimization method; hide all slides that are far away
1764
+ * from the present slide.
1765
+ */
1766
+ function updateSlidesVisibility() {
1767
+
1768
+ // Select all slides and convert the NodeList result to
1769
+ // an array
1770
+ var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ),
1771
+ horizontalSlidesLength = horizontalSlides.length,
1772
+ distanceX,
1773
+ distanceY;
1774
+
1775
+ if( horizontalSlidesLength ) {
1776
+
1777
+ // The number of steps away from the present slide that will
1778
+ // be visible
1779
+ var viewDistance = isOverview() ? 10 : config.viewDistance;
1780
+
1781
+ // Limit view distance on weaker devices
1782
+ if( isMobileDevice ) {
1783
+ viewDistance = isOverview() ? 6 : 1;
1784
+ }
1785
+
1786
+ for( var x = 0; x < horizontalSlidesLength; x++ ) {
1787
+ var horizontalSlide = horizontalSlides[x];
1788
+
1789
+ var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ),
1790
+ verticalSlidesLength = verticalSlides.length;
1791
+
1792
+ // Loops so that it measures 1 between the first and last slides
1793
+ distanceX = Math.abs( ( indexh - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0;
1794
+
1795
+ // Show the horizontal slide if it's within the view distance
1796
+ horizontalSlide.style.display = distanceX > viewDistance ? 'none' : 'block';
1797
+
1798
+ if( verticalSlidesLength ) {
1799
+
1800
+ var oy = getPreviousVerticalIndex( horizontalSlide );
1801
+
1802
+ for( var y = 0; y < verticalSlidesLength; y++ ) {
1803
+ var verticalSlide = verticalSlides[y];
1804
+
1805
+ distanceY = x === indexh ? Math.abs( indexv - y ) : Math.abs( y - oy );
1806
+
1807
+ verticalSlide.style.display = ( distanceX + distanceY ) > viewDistance ? 'none' : 'block';
1808
+ }
1809
+
1810
+ }
1811
+ }
1812
+
1813
+ }
1814
+
1815
+ }
1816
+
1817
+ /**
1818
+ * Updates the progress bar to reflect the current slide.
1819
+ */
1820
+ function updateProgress() {
1821
+
1822
+ // Update progress if enabled
1823
+ if( config.progress && dom.progress ) {
1824
+
1825
+ var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
1826
+
1827
+ // The number of past and total slides
1828
+ var totalCount = document.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length;
1829
+ var pastCount = 0;
1830
+
1831
+ // Step through all slides and count the past ones
1832
+ mainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) {
1833
+
1834
+ var horizontalSlide = horizontalSlides[i];
1835
+ var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );
1836
+
1837
+ for( var j = 0; j < verticalSlides.length; j++ ) {
1838
+
1839
+ // Stop as soon as we arrive at the present
1840
+ if( verticalSlides[j].classList.contains( 'present' ) ) {
1841
+ break mainLoop;
1842
+ }
1843
+
1844
+ pastCount++;
1845
+
1846
+ }
1847
+
1848
+ // Stop as soon as we arrive at the present
1849
+ if( horizontalSlide.classList.contains( 'present' ) ) {
1850
+ break;
1851
+ }
1852
+
1853
+ // Don't count the wrapping section for vertical slides
1854
+ if( horizontalSlide.classList.contains( 'stack' ) === false ) {
1855
+ pastCount++;
1856
+ }
1857
+
1858
+ }
1859
+
1860
+ dom.progressbar.style.width = ( pastCount / ( totalCount - 1 ) ) * window.innerWidth + 'px';
1861
+
1862
+ }
1863
+
1864
+ }
1865
+
1866
+ /**
1867
+ * Updates the slide number div to reflect the current slide.
1868
+ */
1869
+ function updateSlideNumber() {
1870
+
1871
+ // Update slide number if enabled
1872
+ if( config.slideNumber && dom.slideNumber) {
1873
+
1874
+ // Display the number of the page using 'indexh - indexv' format
1875
+ var indexString = indexh;
1876
+ if( indexv > 0 ) {
1877
+ indexString += ' - ' + indexv;
1878
+ }
1879
+
1880
+ dom.slideNumber.innerHTML = indexString;
1881
+ }
1882
+
1883
+ }
1884
+
1885
+ /**
1886
+ * Updates the state of all control/navigation arrows.
1887
+ */
1888
+ function updateControls() {
1889
+
1890
+ var routes = availableRoutes();
1891
+ var fragments = availableFragments();
1892
+
1893
+ // Remove the 'enabled' class from all directions
1894
+ dom.controlsLeft.concat( dom.controlsRight )
1895
+ .concat( dom.controlsUp )
1896
+ .concat( dom.controlsDown )
1897
+ .concat( dom.controlsPrev )
1898
+ .concat( dom.controlsNext ).forEach( function( node ) {
1899
+ node.classList.remove( 'enabled' );
1900
+ node.classList.remove( 'fragmented' );
1901
+ } );
1902
+
1903
+ // Add the 'enabled' class to the available routes
1904
+ if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); } );
1905
+ if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); } );
1906
+ if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); } );
1907
+ if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); } );
1908
+
1909
+ // Prev/next buttons
1910
+ if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); } );
1911
+ if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); } );
1912
+
1913
+ // Highlight fragment directions
1914
+ if( currentSlide ) {
1915
+
1916
+ // Always apply fragment decorator to prev/next buttons
1917
+ if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
1918
+ if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
1919
+
1920
+ // Apply fragment decorators to directional buttons based on
1921
+ // what slide axis they are in
1922
+ if( isVerticalSlide( currentSlide ) ) {
1923
+ if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
1924
+ if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
1925
+ }
1926
+ else {
1927
+ if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
1928
+ if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
1929
+ }
1930
+
1931
+ }
1932
+
1933
+ }
1934
+
1935
+ /**
1936
+ * Updates the background elements to reflect the current
1937
+ * slide.
1938
+ *
1939
+ * @param {Boolean} includeAll If true, the backgrounds of
1940
+ * all vertical slides (not just the present) will be updated.
1941
+ */
1942
+ function updateBackground( includeAll ) {
1943
+
1944
+ var currentBackground = null;
1945
+
1946
+ // Reverse past/future classes when in RTL mode
1947
+ var horizontalPast = config.rtl ? 'future' : 'past',
1948
+ horizontalFuture = config.rtl ? 'past' : 'future';
1949
+
1950
+ // Update the classes of all backgrounds to match the
1951
+ // states of their slides (past/present/future)
1952
+ toArray( dom.background.childNodes ).forEach( function( backgroundh, h ) {
1953
+
1954
+ if( h < indexh ) {
1955
+ backgroundh.className = 'slide-background ' + horizontalPast;
1956
+ }
1957
+ else if ( h > indexh ) {
1958
+ backgroundh.className = 'slide-background ' + horizontalFuture;
1959
+ }
1960
+ else {
1961
+ backgroundh.className = 'slide-background present';
1962
+
1963
+ // Store a reference to the current background element
1964
+ currentBackground = backgroundh;
1965
+ }
1966
+
1967
+ if( includeAll || h === indexh ) {
1968
+ toArray( backgroundh.childNodes ).forEach( function( backgroundv, v ) {
1969
+
1970
+ if( v < indexv ) {
1971
+ backgroundv.className = 'slide-background past';
1972
+ }
1973
+ else if ( v > indexv ) {
1974
+ backgroundv.className = 'slide-background future';
1975
+ }
1976
+ else {
1977
+ backgroundv.className = 'slide-background present';
1978
+
1979
+ // Only if this is the present horizontal and vertical slide
1980
+ if( h === indexh ) currentBackground = backgroundv;
1981
+ }
1982
+
1983
+ } );
1984
+ }
1985
+
1986
+ } );
1987
+
1988
+ // Don't transition between identical backgrounds. This
1989
+ // prevents unwanted flicker.
1990
+ if( currentBackground ) {
1991
+ var previousBackgroundHash = previousBackground ? previousBackground.getAttribute( 'data-background-hash' ) : null;
1992
+ var currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' );
1993
+ if( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== previousBackground ) {
1994
+ dom.background.classList.add( 'no-transition' );
1995
+ }
1996
+
1997
+ previousBackground = currentBackground;
1998
+ }
1999
+
2000
+ // Allow the first background to apply without transition
2001
+ setTimeout( function() {
2002
+ dom.background.classList.remove( 'no-transition' );
2003
+ }, 1 );
2004
+
2005
+ }
2006
+
2007
+ /**
2008
+ * Updates the position of the parallax background based
2009
+ * on the current slide index.
2010
+ */
2011
+ function updateParallax() {
2012
+
2013
+ if( config.parallaxBackgroundImage ) {
2014
+
2015
+ var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ),
2016
+ verticalSlides = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR );
2017
+
2018
+ var backgroundSize = dom.background.style.backgroundSize.split( ' ' ),
2019
+ backgroundWidth, backgroundHeight;
2020
+
2021
+ if( backgroundSize.length === 1 ) {
2022
+ backgroundWidth = backgroundHeight = parseInt( backgroundSize[0], 10 );
2023
+ }
2024
+ else {
2025
+ backgroundWidth = parseInt( backgroundSize[0], 10 );
2026
+ backgroundHeight = parseInt( backgroundSize[1], 10 );
2027
+ }
2028
+
2029
+ var slideWidth = dom.background.offsetWidth;
2030
+ var horizontalSlideCount = horizontalSlides.length;
2031
+ var horizontalOffset = -( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) * indexh;
2032
+
2033
+ var slideHeight = dom.background.offsetHeight;
2034
+ var verticalSlideCount = verticalSlides.length;
2035
+ var verticalOffset = verticalSlideCount > 0 ? -( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ) * indexv : 0;
2036
+
2037
+ dom.background.style.backgroundPosition = horizontalOffset + 'px ' + verticalOffset + 'px';
2038
+
2039
+ }
2040
+
2041
+ }
2042
+
2043
+ /**
2044
+ * Determine what available routes there are for navigation.
2045
+ *
2046
+ * @return {Object} containing four booleans: left/right/up/down
2047
+ */
2048
+ function availableRoutes() {
2049
+
2050
+ var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ),
2051
+ verticalSlides = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR );
2052
+
2053
+ var routes = {
2054
+ left: indexh > 0 || config.loop,
2055
+ right: indexh < horizontalSlides.length - 1 || config.loop,
2056
+ up: indexv > 0,
2057
+ down: indexv < verticalSlides.length - 1
2058
+ };
2059
+
2060
+ // reverse horizontal controls for rtl
2061
+ if( config.rtl ) {
2062
+ var left = routes.left;
2063
+ routes.left = routes.right;
2064
+ routes.right = left;
2065
+ }
2066
+
2067
+ return routes;
2068
+
2069
+ }
2070
+
2071
+ /**
2072
+ * Returns an object describing the available fragment
2073
+ * directions.
2074
+ *
2075
+ * @return {Object} two boolean properties: prev/next
2076
+ */
2077
+ function availableFragments() {
2078
+
2079
+ if( currentSlide && config.fragments ) {
2080
+ var fragments = currentSlide.querySelectorAll( '.fragment' );
2081
+ var hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' );
2082
+
2083
+ return {
2084
+ prev: fragments.length - hiddenFragments.length > 0,
2085
+ next: !!hiddenFragments.length
2086
+ };
2087
+ }
2088
+ else {
2089
+ return { prev: false, next: false };
2090
+ }
2091
+
2092
+ }
2093
+
2094
+ /**
2095
+ * Start playback of any embedded content inside of
2096
+ * the targeted slide.
2097
+ */
2098
+ function startEmbeddedContent( slide ) {
2099
+
2100
+ if( slide && !isSpeakerNotes() ) {
2101
+ // HTML5 media elements
2102
+ toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
2103
+ if( el.hasAttribute( 'data-autoplay' ) ) {
2104
+ el.play();
2105
+ }
2106
+ } );
2107
+
2108
+ // iframe embeds
2109
+ toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
2110
+ el.contentWindow.postMessage( 'slide:start', '*' );
2111
+ });
2112
+
2113
+ // YouTube embeds
2114
+ toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
2115
+ if( el.hasAttribute( 'data-autoplay' ) ) {
2116
+ el.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' );
2117
+ }
2118
+ });
2119
+ }
2120
+
2121
+ }
2122
+
2123
+ /**
2124
+ * Stop playback of any embedded content inside of
2125
+ * the targeted slide.
2126
+ */
2127
+ function stopEmbeddedContent( slide ) {
2128
+
2129
+ if( slide ) {
2130
+ // HTML5 media elements
2131
+ toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
2132
+ if( !el.hasAttribute( 'data-ignore' ) ) {
2133
+ el.pause();
2134
+ }
2135
+ } );
2136
+
2137
+ // iframe embeds
2138
+ toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
2139
+ el.contentWindow.postMessage( 'slide:stop', '*' );
2140
+ });
2141
+
2142
+ // YouTube embeds
2143
+ toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
2144
+ if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
2145
+ el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' );
2146
+ }
2147
+ });
2148
+ }
2149
+
2150
+ }
2151
+
2152
+ /**
2153
+ * Checks if this presentation is running inside of the
2154
+ * speaker notes window.
2155
+ */
2156
+ function isSpeakerNotes() {
2157
+
2158
+ return !!window.location.search.match( /receiver/gi );
2159
+
2160
+ }
2161
+
2162
+ /**
2163
+ * Reads the current URL (hash) and navigates accordingly.
2164
+ */
2165
+ function readURL() {
2166
+
2167
+ var hash = window.location.hash;
2168
+
2169
+ // Attempt to parse the hash as either an index or name
2170
+ var bits = hash.slice( 2 ).split( '/' ),
2171
+ name = hash.replace( /#|\//gi, '' );
2172
+
2173
+ // If the first bit is invalid and there is a name we can
2174
+ // assume that this is a named link
2175
+ if( isNaN( parseInt( bits[0], 10 ) ) && name.length ) {
2176
+ // Find the slide with the specified name
2177
+ var element = document.querySelector( '#' + name );
2178
+
2179
+ if( element ) {
2180
+ // Find the position of the named slide and navigate to it
2181
+ var indices = Reveal.getIndices( element );
2182
+ slide( indices.h, indices.v );
2183
+ }
2184
+ // If the slide doesn't exist, navigate to the current slide
2185
+ else {
2186
+ slide( indexh || 0, indexv || 0 );
2187
+ }
2188
+ }
2189
+ else {
2190
+ // Read the index components of the hash
2191
+ var h = parseInt( bits[0], 10 ) || 0,
2192
+ v = parseInt( bits[1], 10 ) || 0;
2193
+
2194
+ if( h !== indexh || v !== indexv ) {
2195
+ slide( h, v );
2196
+ }
2197
+ }
2198
+
2199
+ }
2200
+
2201
+ /**
2202
+ * Updates the page URL (hash) to reflect the current
2203
+ * state.
2204
+ *
2205
+ * @param {Number} delay The time in ms to wait before
2206
+ * writing the hash
2207
+ */
2208
+ function writeURL( delay ) {
2209
+
2210
+ if( config.history ) {
2211
+
2212
+ // Make sure there's never more than one timeout running
2213
+ clearTimeout( writeURLTimeout );
2214
+
2215
+ // If a delay is specified, timeout this call
2216
+ if( typeof delay === 'number' ) {
2217
+ writeURLTimeout = setTimeout( writeURL, delay );
2218
+ }
2219
+ else {
2220
+ var url = '/';
2221
+
2222
+ // If the current slide has an ID, use that as a named link
2223
+ if( currentSlide && typeof currentSlide.getAttribute( 'id' ) === 'string' ) {
2224
+ url = '/' + currentSlide.getAttribute( 'id' );
2225
+ }
2226
+ // Otherwise use the /h/v index
2227
+ else {
2228
+ if( indexh > 0 || indexv > 0 ) url += indexh;
2229
+ if( indexv > 0 ) url += '/' + indexv;
2230
+ }
2231
+
2232
+ window.location.hash = url;
2233
+ }
2234
+ }
2235
+
2236
+ }
2237
+
2238
+ /**
2239
+ * Retrieves the h/v location of the current, or specified,
2240
+ * slide.
2241
+ *
2242
+ * @param {HTMLElement} slide If specified, the returned
2243
+ * index will be for this slide rather than the currently
2244
+ * active one
2245
+ *
2246
+ * @return {Object} { h: <int>, v: <int>, f: <int> }
2247
+ */
2248
+ function getIndices( slide ) {
2249
+
2250
+ // By default, return the current indices
2251
+ var h = indexh,
2252
+ v = indexv,
2253
+ f;
2254
+
2255
+ // If a slide is specified, return the indices of that slide
2256
+ if( slide ) {
2257
+ var isVertical = isVerticalSlide( slide );
2258
+ var slideh = isVertical ? slide.parentNode : slide;
2259
+
2260
+ // Select all horizontal slides
2261
+ var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
2262
+
2263
+ // Now that we know which the horizontal slide is, get its index
2264
+ h = Math.max( horizontalSlides.indexOf( slideh ), 0 );
2265
+
2266
+ // If this is a vertical slide, grab the vertical index
2267
+ if( isVertical ) {
2268
+ v = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 );
2269
+ }
2270
+ }
2271
+
2272
+ if( !slide && currentSlide ) {
2273
+ var hasFragments = currentSlide.querySelectorAll( '.fragment' ).length > 0;
2274
+ if( hasFragments ) {
2275
+ var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' );
2276
+ f = visibleFragments.length - 1;
2277
+ }
2278
+ }
2279
+
2280
+ return { h: h, v: v, f: f };
2281
+
2282
+ }
2283
+
2284
+ /**
2285
+ * Return a sorted fragments list, ordered by an increasing
2286
+ * "data-fragment-index" attribute.
2287
+ *
2288
+ * Fragments will be revealed in the order that they are returned by
2289
+ * this function, so you can use the index attributes to control the
2290
+ * order of fragment appearance.
2291
+ *
2292
+ * To maintain a sensible default fragment order, fragments are presumed
2293
+ * to be passed in document order. This function adds a "fragment-index"
2294
+ * attribute to each node if such an attribute is not already present,
2295
+ * and sets that attribute to an integer value which is the position of
2296
+ * the fragment within the fragments list.
2297
+ */
2298
+ function sortFragments( fragments ) {
2299
+
2300
+ fragments = toArray( fragments );
2301
+
2302
+ var ordered = [],
2303
+ unordered = [],
2304
+ sorted = [];
2305
+
2306
+ // Group ordered and unordered elements
2307
+ fragments.forEach( function( fragment, i ) {
2308
+ if( fragment.hasAttribute( 'data-fragment-index' ) ) {
2309
+ var index = parseInt( fragment.getAttribute( 'data-fragment-index' ), 10 );
2310
+
2311
+ if( !ordered[index] ) {
2312
+ ordered[index] = [];
2313
+ }
2314
+
2315
+ ordered[index].push( fragment );
2316
+ }
2317
+ else {
2318
+ unordered.push( [ fragment ] );
2319
+ }
2320
+ } );
2321
+
2322
+ // Append fragments without explicit indices in their
2323
+ // DOM order
2324
+ ordered = ordered.concat( unordered );
2325
+
2326
+ // Manually count the index up per group to ensure there
2327
+ // are no gaps
2328
+ var index = 0;
2329
+
2330
+ // Push all fragments in their sorted order to an array,
2331
+ // this flattens the groups
2332
+ ordered.forEach( function( group ) {
2333
+ group.forEach( function( fragment ) {
2334
+ sorted.push( fragment );
2335
+ fragment.setAttribute( 'data-fragment-index', index );
2336
+ } );
2337
+
2338
+ index ++;
2339
+ } );
2340
+
2341
+ return sorted;
2342
+
2343
+ }
2344
+
2345
+ /**
2346
+ * Navigate to the specified slide fragment.
2347
+ *
2348
+ * @param {Number} index The index of the fragment that
2349
+ * should be shown, -1 means all are invisible
2350
+ * @param {Number} offset Integer offset to apply to the
2351
+ * fragment index
2352
+ *
2353
+ * @return {Boolean} true if a change was made in any
2354
+ * fragments visibility as part of this call
2355
+ */
2356
+ function navigateFragment( index, offset ) {
2357
+
2358
+ if( currentSlide && config.fragments ) {
2359
+
2360
+ var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment' ) );
2361
+ if( fragments.length ) {
2362
+
2363
+ // If no index is specified, find the current
2364
+ if( typeof index !== 'number' ) {
2365
+ var lastVisibleFragment = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop();
2366
+
2367
+ if( lastVisibleFragment ) {
2368
+ index = parseInt( lastVisibleFragment.getAttribute( 'data-fragment-index' ) || 0, 10 );
2369
+ }
2370
+ else {
2371
+ index = -1;
2372
+ }
2373
+ }
2374
+
2375
+ // If an offset is specified, apply it to the index
2376
+ if( typeof offset === 'number' ) {
2377
+ index += offset;
2378
+ }
2379
+
2380
+ var fragmentsShown = [],
2381
+ fragmentsHidden = [];
2382
+
2383
+ toArray( fragments ).forEach( function( element, i ) {
2384
+
2385
+ if( element.hasAttribute( 'data-fragment-index' ) ) {
2386
+ i = parseInt( element.getAttribute( 'data-fragment-index' ), 10 );
2387
+ }
2388
+
2389
+ // Visible fragments
2390
+ if( i <= index ) {
2391
+ if( !element.classList.contains( 'visible' ) ) fragmentsShown.push( element );
2392
+ element.classList.add( 'visible' );
2393
+ element.classList.remove( 'current-fragment' );
2394
+
2395
+ if( i === index ) {
2396
+ element.classList.add( 'current-fragment' );
2397
+ }
2398
+ }
2399
+ // Hidden fragments
2400
+ else {
2401
+ if( element.classList.contains( 'visible' ) ) fragmentsHidden.push( element );
2402
+ element.classList.remove( 'visible' );
2403
+ element.classList.remove( 'current-fragment' );
2404
+ }
2405
+
2406
+
2407
+ } );
2408
+
2409
+ if( fragmentsHidden.length ) {
2410
+ dispatchEvent( 'fragmenthidden', { fragment: fragmentsHidden[0], fragments: fragmentsHidden } );
2411
+ }
2412
+
2413
+ if( fragmentsShown.length ) {
2414
+ dispatchEvent( 'fragmentshown', { fragment: fragmentsShown[0], fragments: fragmentsShown } );
2415
+ }
2416
+
2417
+ updateControls();
2418
+
2419
+ return !!( fragmentsShown.length || fragmentsHidden.length );
2420
+
2421
+ }
2422
+
2423
+ }
2424
+
2425
+ return false;
2426
+
2427
+ }
2428
+
2429
+ /**
2430
+ * Navigate to the next slide fragment.
2431
+ *
2432
+ * @return {Boolean} true if there was a next fragment,
2433
+ * false otherwise
2434
+ */
2435
+ function nextFragment() {
2436
+
2437
+ return navigateFragment( null, 1 );
2438
+
2439
+ }
2440
+
2441
+ /**
2442
+ * Navigate to the previous slide fragment.
2443
+ *
2444
+ * @return {Boolean} true if there was a previous fragment,
2445
+ * false otherwise
2446
+ */
2447
+ function previousFragment() {
2448
+
2449
+ return navigateFragment( null, -1 );
2450
+
2451
+ }
2452
+
2453
+ /**
2454
+ * Cues a new automated slide if enabled in the config.
2455
+ */
2456
+ function cueAutoSlide() {
2457
+
2458
+ cancelAutoSlide();
2459
+
2460
+ if( currentSlide ) {
2461
+
2462
+ var parentAutoSlide = currentSlide.parentNode ? currentSlide.parentNode.getAttribute( 'data-autoslide' ) : null;
2463
+ var slideAutoSlide = currentSlide.getAttribute( 'data-autoslide' );
2464
+
2465
+ // Pick value in the following priority order:
2466
+ // 1. Current slide's data-autoslide
2467
+ // 2. Parent slide's data-autoslide
2468
+ // 3. Global autoSlide setting
2469
+ if( slideAutoSlide ) {
2470
+ autoSlide = parseInt( slideAutoSlide, 10 );
2471
+ }
2472
+ else if( parentAutoSlide ) {
2473
+ autoSlide = parseInt( parentAutoSlide, 10 );
2474
+ }
2475
+ else {
2476
+ autoSlide = config.autoSlide;
2477
+ }
2478
+
2479
+ // If there are media elements with data-autoplay,
2480
+ // automatically set the autoSlide duration to the
2481
+ // length of that media
2482
+ toArray( currentSlide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
2483
+ if( el.hasAttribute( 'data-autoplay' ) ) {
2484
+ if( autoSlide && el.duration * 1000 > autoSlide ) {
2485
+ autoSlide = ( el.duration * 1000 ) + 1000;
2486
+ }
2487
+ }
2488
+ } );
2489
+
2490
+ // Cue the next auto-slide if:
2491
+ // - There is an autoSlide value
2492
+ // - Auto-sliding isn't paused by the user
2493
+ // - The presentation isn't paused
2494
+ // - The overview isn't active
2495
+ // - The presentation isn't over
2496
+ if( autoSlide && !autoSlidePaused && !isPaused() && !isOverview() && ( !Reveal.isLastSlide() || config.loop === true ) ) {
2497
+ autoSlideTimeout = setTimeout( navigateNext, autoSlide );
2498
+ autoSlideStartTime = Date.now();
2499
+ }
2500
+
2501
+ if( autoSlidePlayer ) {
2502
+ autoSlidePlayer.setPlaying( autoSlideTimeout !== -1 );
2503
+ }
2504
+
2505
+ }
2506
+
2507
+ }
2508
+
2509
+ /**
2510
+ * Cancels any ongoing request to auto-slide.
2511
+ */
2512
+ function cancelAutoSlide() {
2513
+
2514
+ clearTimeout( autoSlideTimeout );
2515
+ autoSlideTimeout = -1;
2516
+
2517
+ }
2518
+
2519
+ function pauseAutoSlide() {
2520
+
2521
+ autoSlidePaused = true;
2522
+ clearTimeout( autoSlideTimeout );
2523
+
2524
+ if( autoSlidePlayer ) {
2525
+ autoSlidePlayer.setPlaying( false );
2526
+ }
2527
+
2528
+ }
2529
+
2530
+ function resumeAutoSlide() {
2531
+
2532
+ autoSlidePaused = false;
2533
+ cueAutoSlide();
2534
+
2535
+ }
2536
+
2537
+ function navigateLeft() {
2538
+
2539
+ // Reverse for RTL
2540
+ if( config.rtl ) {
2541
+ if( ( isOverview() || nextFragment() === false ) && availableRoutes().left ) {
2542
+ slide( indexh + 1 );
2543
+ }
2544
+ }
2545
+ // Normal navigation
2546
+ else if( ( isOverview() || previousFragment() === false ) && availableRoutes().left ) {
2547
+ slide( indexh - 1 );
2548
+ }
2549
+
2550
+ }
2551
+
2552
+ function navigateRight() {
2553
+
2554
+ // Reverse for RTL
2555
+ if( config.rtl ) {
2556
+ if( ( isOverview() || previousFragment() === false ) && availableRoutes().right ) {
2557
+ slide( indexh - 1 );
2558
+ }
2559
+ }
2560
+ // Normal navigation
2561
+ else if( ( isOverview() || nextFragment() === false ) && availableRoutes().right ) {
2562
+ slide( indexh + 1 );
2563
+ }
2564
+
2565
+ }
2566
+
2567
+ function navigateUp() {
2568
+
2569
+ // Prioritize hiding fragments
2570
+ if( ( isOverview() || previousFragment() === false ) && availableRoutes().up ) {
2571
+ slide( indexh, indexv - 1 );
2572
+ }
2573
+
2574
+ }
2575
+
2576
+ function navigateDown() {
2577
+
2578
+ // Prioritize revealing fragments
2579
+ if( ( isOverview() || nextFragment() === false ) && availableRoutes().down ) {
2580
+ slide( indexh, indexv + 1 );
2581
+ }
2582
+
2583
+ }
2584
+
2585
+ /**
2586
+ * Navigates backwards, prioritized in the following order:
2587
+ * 1) Previous fragment
2588
+ * 2) Previous vertical slide
2589
+ * 3) Previous horizontal slide
2590
+ */
2591
+ function navigatePrev() {
2592
+
2593
+ // Prioritize revealing fragments
2594
+ if( previousFragment() === false ) {
2595
+ if( availableRoutes().up ) {
2596
+ navigateUp();
2597
+ }
2598
+ else {
2599
+ // Fetch the previous horizontal slide, if there is one
2600
+ var previousSlide = document.querySelector( HORIZONTAL_SLIDES_SELECTOR + '.past:nth-child(' + indexh + ')' );
2601
+
2602
+ if( previousSlide ) {
2603
+ var v = ( previousSlide.querySelectorAll( 'section' ).length - 1 ) || undefined;
2604
+ var h = indexh - 1;
2605
+ slide( h, v );
2606
+ }
2607
+ }
2608
+ }
2609
+
2610
+ }
2611
+
2612
+ /**
2613
+ * Same as #navigatePrev() but navigates forwards.
2614
+ */
2615
+ function navigateNext() {
2616
+
2617
+ // Prioritize revealing fragments
2618
+ if( nextFragment() === false ) {
2619
+ availableRoutes().down ? navigateDown() : navigateRight();
2620
+ }
2621
+
2622
+ // If auto-sliding is enabled we need to cue up
2623
+ // another timeout
2624
+ cueAutoSlide();
2625
+
2626
+ }
2627
+
2628
+
2629
+ // --------------------------------------------------------------------//
2630
+ // ----------------------------- EVENTS -------------------------------//
2631
+ // --------------------------------------------------------------------//
2632
+
2633
+ /**
2634
+ * Called by all event handlers that are based on user
2635
+ * input.
2636
+ */
2637
+ function onUserInput( event ) {
2638
+
2639
+ if( config.autoSlideStoppable ) {
2640
+ pauseAutoSlide();
2641
+ }
2642
+
2643
+ }
2644
+
2645
+ /**
2646
+ * Handler for the document level 'keydown' event.
2647
+ */
2648
+ function onDocumentKeyDown( event ) {
2649
+
2650
+ onUserInput( event );
2651
+
2652
+ // Check if there's a focused element that could be using
2653
+ // the keyboard
2654
+ var activeElement = document.activeElement;
2655
+ var hasFocus = !!( document.activeElement && ( document.activeElement.type || document.activeElement.href || document.activeElement.contentEditable !== 'inherit' ) );
2656
+
2657
+ // Disregard the event if there's a focused element or a
2658
+ // keyboard modifier key is present
2659
+ if( hasFocus || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return;
2660
+
2661
+ // While paused only allow "unpausing" keyboard events (b and .)
2662
+ if( isPaused() && [66,190,191].indexOf( event.keyCode ) === -1 ) {
2663
+ return false;
2664
+ }
2665
+
2666
+ var triggered = false;
2667
+
2668
+ // 1. User defined key bindings
2669
+ if( typeof config.keyboard === 'object' ) {
2670
+
2671
+ for( var key in config.keyboard ) {
2672
+
2673
+ // Check if this binding matches the pressed key
2674
+ if( parseInt( key, 10 ) === event.keyCode ) {
2675
+
2676
+ var value = config.keyboard[ key ];
2677
+
2678
+ // Callback function
2679
+ if( typeof value === 'function' ) {
2680
+ value.apply( null, [ event ] );
2681
+ }
2682
+ // String shortcuts to reveal.js API
2683
+ else if( typeof value === 'string' && typeof Reveal[ value ] === 'function' ) {
2684
+ Reveal[ value ].call();
2685
+ }
2686
+
2687
+ triggered = true;
2688
+
2689
+ }
2690
+
2691
+ }
2692
+
2693
+ }
2694
+
2695
+ // 2. System defined key bindings
2696
+ if( triggered === false ) {
2697
+
2698
+ // Assume true and try to prove false
2699
+ triggered = true;
2700
+
2701
+ switch( event.keyCode ) {
2702
+ // p, page up
2703
+ case 80: case 33: navigatePrev(); break;
2704
+ // n, page down
2705
+ case 78: case 34: navigateNext(); break;
2706
+ // h, left
2707
+ case 72: case 37: navigateLeft(); break;
2708
+ // l, right
2709
+ case 76: case 39: navigateRight(); break;
2710
+ // k, up
2711
+ case 75: case 38: navigateUp(); break;
2712
+ // j, down
2713
+ case 74: case 40: navigateDown(); break;
2714
+ // home
2715
+ case 36: slide( 0 ); break;
2716
+ // end
2717
+ case 35: slide( Number.MAX_VALUE ); break;
2718
+ // space
2719
+ case 32: isOverview() ? deactivateOverview() : event.shiftKey ? navigatePrev() : navigateNext(); break;
2720
+ // return
2721
+ case 13: isOverview() ? deactivateOverview() : triggered = false; break;
2722
+ // b, period, Logitech presenter tools "black screen" button
2723
+ case 66: case 190: case 191: togglePause(); break;
2724
+ // f
2725
+ case 70: enterFullscreen(); break;
2726
+ default:
2727
+ triggered = false;
2728
+ }
2729
+
2730
+ }
2731
+
2732
+ // If the input resulted in a triggered action we should prevent
2733
+ // the browsers default behavior
2734
+ if( triggered ) {
2735
+ event.preventDefault();
2736
+ }
2737
+ // ESC or O key
2738
+ else if ( ( event.keyCode === 27 || event.keyCode === 79 ) && features.transforms3d ) {
2739
+ if( dom.preview ) {
2740
+ closePreview();
2741
+ }
2742
+ else {
2743
+ toggleOverview();
2744
+ }
2745
+
2746
+ event.preventDefault();
2747
+ }
2748
+
2749
+ // If auto-sliding is enabled we need to cue up
2750
+ // another timeout
2751
+ cueAutoSlide();
2752
+
2753
+ }
2754
+
2755
+ /**
2756
+ * Handler for the 'touchstart' event, enables support for
2757
+ * swipe and pinch gestures.
2758
+ */
2759
+ function onTouchStart( event ) {
2760
+
2761
+ touch.startX = event.touches[0].clientX;
2762
+ touch.startY = event.touches[0].clientY;
2763
+ touch.startCount = event.touches.length;
2764
+
2765
+ // If there's two touches we need to memorize the distance
2766
+ // between those two points to detect pinching
2767
+ if( event.touches.length === 2 && config.overview ) {
2768
+ touch.startSpan = distanceBetween( {
2769
+ x: event.touches[1].clientX,
2770
+ y: event.touches[1].clientY
2771
+ }, {
2772
+ x: touch.startX,
2773
+ y: touch.startY
2774
+ } );
2775
+ }
2776
+
2777
+ }
2778
+
2779
+ /**
2780
+ * Handler for the 'touchmove' event.
2781
+ */
2782
+ function onTouchMove( event ) {
2783
+
2784
+ // Each touch should only trigger one action
2785
+ if( !touch.captured ) {
2786
+ onUserInput( event );
2787
+
2788
+ var currentX = event.touches[0].clientX;
2789
+ var currentY = event.touches[0].clientY;
2790
+
2791
+ // If the touch started with two points and still has
2792
+ // two active touches; test for the pinch gesture
2793
+ if( event.touches.length === 2 && touch.startCount === 2 && config.overview ) {
2794
+
2795
+ // The current distance in pixels between the two touch points
2796
+ var currentSpan = distanceBetween( {
2797
+ x: event.touches[1].clientX,
2798
+ y: event.touches[1].clientY
2799
+ }, {
2800
+ x: touch.startX,
2801
+ y: touch.startY
2802
+ } );
2803
+
2804
+ // If the span is larger than the desire amount we've got
2805
+ // ourselves a pinch
2806
+ if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) {
2807
+ touch.captured = true;
2808
+
2809
+ if( currentSpan < touch.startSpan ) {
2810
+ activateOverview();
2811
+ }
2812
+ else {
2813
+ deactivateOverview();
2814
+ }
2815
+ }
2816
+
2817
+ event.preventDefault();
2818
+
2819
+ }
2820
+ // There was only one touch point, look for a swipe
2821
+ else if( event.touches.length === 1 && touch.startCount !== 2 ) {
2822
+
2823
+ var deltaX = currentX - touch.startX,
2824
+ deltaY = currentY - touch.startY;
2825
+
2826
+ if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
2827
+ touch.captured = true;
2828
+ navigateLeft();
2829
+ }
2830
+ else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
2831
+ touch.captured = true;
2832
+ navigateRight();
2833
+ }
2834
+ else if( deltaY > touch.threshold ) {
2835
+ touch.captured = true;
2836
+ navigateUp();
2837
+ }
2838
+ else if( deltaY < -touch.threshold ) {
2839
+ touch.captured = true;
2840
+ navigateDown();
2841
+ }
2842
+
2843
+ // If we're embedded, only block touch events if they have
2844
+ // triggered an action
2845
+ if( config.embedded ) {
2846
+ if( touch.captured || isVerticalSlide( currentSlide ) ) {
2847
+ event.preventDefault();
2848
+ }
2849
+ }
2850
+ // Not embedded? Block them all to avoid needless tossing
2851
+ // around of the viewport in iOS
2852
+ else {
2853
+ event.preventDefault();
2854
+ }
2855
+
2856
+ }
2857
+ }
2858
+ // There's a bug with swiping on some Android devices unless
2859
+ // the default action is always prevented
2860
+ else if( navigator.userAgent.match( /android/gi ) ) {
2861
+ event.preventDefault();
2862
+ }
2863
+
2864
+ }
2865
+
2866
+ /**
2867
+ * Handler for the 'touchend' event.
2868
+ */
2869
+ function onTouchEnd( event ) {
2870
+
2871
+ touch.captured = false;
2872
+
2873
+ }
2874
+
2875
+ /**
2876
+ * Convert pointer down to touch start.
2877
+ */
2878
+ function onPointerDown( event ) {
2879
+
2880
+ if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) {
2881
+ event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
2882
+ onTouchStart( event );
2883
+ }
2884
+
2885
+ }
2886
+
2887
+ /**
2888
+ * Convert pointer move to touch move.
2889
+ */
2890
+ function onPointerMove( event ) {
2891
+
2892
+ if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) {
2893
+ event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
2894
+ onTouchMove( event );
2895
+ }
2896
+
2897
+ }
2898
+
2899
+ /**
2900
+ * Convert pointer up to touch end.
2901
+ */
2902
+ function onPointerUp( event ) {
2903
+
2904
+ if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) {
2905
+ event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
2906
+ onTouchEnd( event );
2907
+ }
2908
+
2909
+ }
2910
+
2911
+ /**
2912
+ * Handles mouse wheel scrolling, throttled to avoid skipping
2913
+ * multiple slides.
2914
+ */
2915
+ function onDocumentMouseScroll( event ) {
2916
+
2917
+ if( Date.now() - lastMouseWheelStep > 600 ) {
2918
+
2919
+ lastMouseWheelStep = Date.now();
2920
+
2921
+ var delta = event.detail || -event.wheelDelta;
2922
+ if( delta > 0 ) {
2923
+ navigateNext();
2924
+ }
2925
+ else {
2926
+ navigatePrev();
2927
+ }
2928
+
2929
+ }
2930
+
2931
+ }
2932
+
2933
+ /**
2934
+ * Clicking on the progress bar results in a navigation to the
2935
+ * closest approximate horizontal slide using this equation:
2936
+ *
2937
+ * ( clickX / presentationWidth ) * numberOfSlides
2938
+ */
2939
+ function onProgressClicked( event ) {
2940
+
2941
+ onUserInput( event );
2942
+
2943
+ event.preventDefault();
2944
+
2945
+ var slidesTotal = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).length;
2946
+ var slideIndex = Math.floor( ( event.clientX / dom.wrapper.offsetWidth ) * slidesTotal );
2947
+
2948
+ slide( slideIndex );
2949
+
2950
+ }
2951
+
2952
+ /**
2953
+ * Event handler for navigation control buttons.
2954
+ */
2955
+ function onNavigateLeftClicked( event ) { event.preventDefault(); onUserInput(); navigateLeft(); }
2956
+ function onNavigateRightClicked( event ) { event.preventDefault(); onUserInput(); navigateRight(); }
2957
+ function onNavigateUpClicked( event ) { event.preventDefault(); onUserInput(); navigateUp(); }
2958
+ function onNavigateDownClicked( event ) { event.preventDefault(); onUserInput(); navigateDown(); }
2959
+ function onNavigatePrevClicked( event ) { event.preventDefault(); onUserInput(); navigatePrev(); }
2960
+ function onNavigateNextClicked( event ) { event.preventDefault(); onUserInput(); navigateNext(); }
2961
+
2962
+ /**
2963
+ * Handler for the window level 'hashchange' event.
2964
+ */
2965
+ function onWindowHashChange( event ) {
2966
+
2967
+ readURL();
2968
+
2969
+ }
2970
+
2971
+ /**
2972
+ * Handler for the window level 'resize' event.
2973
+ */
2974
+ function onWindowResize( event ) {
2975
+
2976
+ layout();
2977
+
2978
+ }
2979
+
2980
+ /**
2981
+ * Handle for the window level 'visibilitychange' event.
2982
+ */
2983
+ function onPageVisibilityChange( event ) {
2984
+
2985
+ var isHidden = document.webkitHidden ||
2986
+ document.msHidden ||
2987
+ document.hidden;
2988
+
2989
+ // If, after clicking a link or similar and we're coming back,
2990
+ // focus the document.body to ensure we can use keyboard shortcuts
2991
+ if( isHidden === false && document.activeElement !== document.body ) {
2992
+ document.activeElement.blur();
2993
+ document.body.focus();
2994
+ }
2995
+
2996
+ }
2997
+
2998
+ /**
2999
+ * Invoked when a slide is and we're in the overview.
3000
+ */
3001
+ function onOverviewSlideClicked( event ) {
3002
+
3003
+ // TODO There's a bug here where the event listeners are not
3004
+ // removed after deactivating the overview.
3005
+ if( eventsAreBound && isOverview() ) {
3006
+ event.preventDefault();
3007
+
3008
+ var element = event.target;
3009
+
3010
+ while( element && !element.nodeName.match( /section/gi ) ) {
3011
+ element = element.parentNode;
3012
+ }
3013
+
3014
+ if( element && !element.classList.contains( 'disabled' ) ) {
3015
+
3016
+ deactivateOverview();
3017
+
3018
+ if( element.nodeName.match( /section/gi ) ) {
3019
+ var h = parseInt( element.getAttribute( 'data-index-h' ), 10 ),
3020
+ v = parseInt( element.getAttribute( 'data-index-v' ), 10 );
3021
+
3022
+ slide( h, v );
3023
+ }
3024
+
3025
+ }
3026
+ }
3027
+
3028
+ }
3029
+
3030
+ /**
3031
+ * Handles clicks on links that are set to preview in the
3032
+ * iframe overlay.
3033
+ */
3034
+ function onPreviewLinkClicked( event ) {
3035
+
3036
+ var url = event.target.getAttribute( 'href' );
3037
+ if( url ) {
3038
+ openPreview( url );
3039
+ event.preventDefault();
3040
+ }
3041
+
3042
+ }
3043
+
3044
+ /**
3045
+ * Handles click on the auto-sliding controls element.
3046
+ */
3047
+ function onAutoSlidePlayerClick( event ) {
3048
+
3049
+ // Replay
3050
+ if( Reveal.isLastSlide() && config.loop === false ) {
3051
+ slide( 0, 0 );
3052
+ resumeAutoSlide();
3053
+ }
3054
+ // Resume
3055
+ else if( autoSlidePaused ) {
3056
+ resumeAutoSlide();
3057
+ }
3058
+ // Pause
3059
+ else {
3060
+ pauseAutoSlide();
3061
+ }
3062
+
3063
+ }
3064
+
3065
+
3066
+ // --------------------------------------------------------------------//
3067
+ // ------------------------ PLAYBACK COMPONENT ------------------------//
3068
+ // --------------------------------------------------------------------//
3069
+
3070
+
3071
+ /**
3072
+ * Constructor for the playback component, which displays
3073
+ * play/pause/progress controls.
3074
+ *
3075
+ * @param {HTMLElement} container The component will append
3076
+ * itself to this
3077
+ * @param {Function} progressCheck A method which will be
3078
+ * called frequently to get the current progress on a range
3079
+ * of 0-1
3080
+ */
3081
+ function Playback( container, progressCheck ) {
3082
+
3083
+ // Cosmetics
3084
+ this.diameter = 50;
3085
+ this.thickness = 3;
3086
+
3087
+ // Flags if we are currently playing
3088
+ this.playing = false;
3089
+
3090
+ // Current progress on a 0-1 range
3091
+ this.progress = 0;
3092
+
3093
+ // Used to loop the animation smoothly
3094
+ this.progressOffset = 1;
3095
+
3096
+ this.container = container;
3097
+ this.progressCheck = progressCheck;
3098
+
3099
+ this.canvas = document.createElement( 'canvas' );
3100
+ this.canvas.className = 'playback';
3101
+ this.canvas.width = this.diameter;
3102
+ this.canvas.height = this.diameter;
3103
+ this.context = this.canvas.getContext( '2d' );
3104
+
3105
+ this.container.appendChild( this.canvas );
3106
+
3107
+ this.render();
3108
+
3109
+ }
3110
+
3111
+ Playback.prototype.setPlaying = function( value ) {
3112
+
3113
+ var wasPlaying = this.playing;
3114
+
3115
+ this.playing = value;
3116
+
3117
+ // Start repainting if we weren't already
3118
+ if( !wasPlaying && this.playing ) {
3119
+ this.animate();
3120
+ }
3121
+ else {
3122
+ this.render();
3123
+ }
3124
+
3125
+ };
3126
+
3127
+ Playback.prototype.animate = function() {
3128
+
3129
+ var progressBefore = this.progress;
3130
+
3131
+ this.progress = this.progressCheck();
3132
+
3133
+ // When we loop, offset the progress so that it eases
3134
+ // smoothly rather than immediately resetting
3135
+ if( progressBefore > 0.8 && this.progress < 0.2 ) {
3136
+ this.progressOffset = this.progress;
3137
+ }
3138
+
3139
+ this.render();
3140
+
3141
+ if( this.playing ) {
3142
+ features.requestAnimationFrameMethod.call( window, this.animate.bind( this ) );
3143
+ }
3144
+
3145
+ };
3146
+
3147
+ /**
3148
+ * Renders the current progress and playback state.
3149
+ */
3150
+ Playback.prototype.render = function() {
3151
+
3152
+ var progress = this.playing ? this.progress : 0,
3153
+ radius = ( this.diameter / 2 ) - this.thickness,
3154
+ x = this.diameter / 2,
3155
+ y = this.diameter / 2,
3156
+ iconSize = 14;
3157
+
3158
+ // Ease towards 1
3159
+ this.progressOffset += ( 1 - this.progressOffset ) * 0.1;
3160
+
3161
+ var endAngle = ( - Math.PI / 2 ) + ( progress * ( Math.PI * 2 ) );
3162
+ var startAngle = ( - Math.PI / 2 ) + ( this.progressOffset * ( Math.PI * 2 ) );
3163
+
3164
+ this.context.save();
3165
+ this.context.clearRect( 0, 0, this.diameter, this.diameter );
3166
+
3167
+ // Solid background color
3168
+ this.context.beginPath();
3169
+ this.context.arc( x, y, radius + 2, 0, Math.PI * 2, false );
3170
+ this.context.fillStyle = 'rgba( 0, 0, 0, 0.4 )';
3171
+ this.context.fill();
3172
+
3173
+ // Draw progress track
3174
+ this.context.beginPath();
3175
+ this.context.arc( x, y, radius, 0, Math.PI * 2, false );
3176
+ this.context.lineWidth = this.thickness;
3177
+ this.context.strokeStyle = '#666';
3178
+ this.context.stroke();
3179
+
3180
+ if( this.playing ) {
3181
+ // Draw progress on top of track
3182
+ this.context.beginPath();
3183
+ this.context.arc( x, y, radius, startAngle, endAngle, false );
3184
+ this.context.lineWidth = this.thickness;
3185
+ this.context.strokeStyle = '#fff';
3186
+ this.context.stroke();
3187
+ }
3188
+
3189
+ this.context.translate( x - ( iconSize / 2 ), y - ( iconSize / 2 ) );
3190
+
3191
+ // Draw play/pause icons
3192
+ if( this.playing ) {
3193
+ this.context.fillStyle = '#fff';
3194
+ this.context.fillRect( 0, 0, iconSize / 2 - 2, iconSize );
3195
+ this.context.fillRect( iconSize / 2 + 2, 0, iconSize / 2 - 2, iconSize );
3196
+ }
3197
+ else {
3198
+ this.context.beginPath();
3199
+ this.context.translate( 2, 0 );
3200
+ this.context.moveTo( 0, 0 );
3201
+ this.context.lineTo( iconSize - 2, iconSize / 2 );
3202
+ this.context.lineTo( 0, iconSize );
3203
+ this.context.fillStyle = '#fff';
3204
+ this.context.fill();
3205
+ }
3206
+
3207
+ this.context.restore();
3208
+
3209
+ };
3210
+
3211
+ Playback.prototype.on = function( type, listener ) {
3212
+ this.canvas.addEventListener( type, listener, false );
3213
+ };
3214
+
3215
+ Playback.prototype.off = function( type, listener ) {
3216
+ this.canvas.removeEventListener( type, listener, false );
3217
+ };
3218
+
3219
+ Playback.prototype.destroy = function() {
3220
+
3221
+ this.playing = false;
3222
+
3223
+ if( this.canvas.parentNode ) {
3224
+ this.container.removeChild( this.canvas );
3225
+ }
3226
+
3227
+ };
3228
+
3229
+
3230
+ // --------------------------------------------------------------------//
3231
+ // ------------------------------- API --------------------------------//
3232
+ // --------------------------------------------------------------------//
3233
+
3234
+
3235
+ return {
3236
+ initialize: initialize,
3237
+ configure: configure,
3238
+ sync: sync,
3239
+
3240
+ // Navigation methods
3241
+ slide: slide,
3242
+ left: navigateLeft,
3243
+ right: navigateRight,
3244
+ up: navigateUp,
3245
+ down: navigateDown,
3246
+ prev: navigatePrev,
3247
+ next: navigateNext,
3248
+
3249
+ // Fragment methods
3250
+ navigateFragment: navigateFragment,
3251
+ prevFragment: previousFragment,
3252
+ nextFragment: nextFragment,
3253
+
3254
+ // Deprecated aliases
3255
+ navigateTo: slide,
3256
+ navigateLeft: navigateLeft,
3257
+ navigateRight: navigateRight,
3258
+ navigateUp: navigateUp,
3259
+ navigateDown: navigateDown,
3260
+ navigatePrev: navigatePrev,
3261
+ navigateNext: navigateNext,
3262
+
3263
+ // Forces an update in slide layout
3264
+ layout: layout,
3265
+
3266
+ // Returns an object with the available routes as booleans (left/right/top/bottom)
3267
+ availableRoutes: availableRoutes,
3268
+
3269
+ // Returns an object with the available fragments as booleans (prev/next)
3270
+ availableFragments: availableFragments,
3271
+
3272
+ // Toggles the overview mode on/off
3273
+ toggleOverview: toggleOverview,
3274
+
3275
+ // Toggles the "black screen" mode on/off
3276
+ togglePause: togglePause,
3277
+
3278
+ // State checks
3279
+ isOverview: isOverview,
3280
+ isPaused: isPaused,
3281
+
3282
+ // Adds or removes all internal event listeners (such as keyboard)
3283
+ addEventListeners: addEventListeners,
3284
+ removeEventListeners: removeEventListeners,
3285
+
3286
+ // Returns the indices of the current, or specified, slide
3287
+ getIndices: getIndices,
3288
+
3289
+ // Returns the slide at the specified index, y is optional
3290
+ getSlide: function( x, y ) {
3291
+ var horizontalSlide = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR )[ x ];
3292
+ var verticalSlides = horizontalSlide && horizontalSlide.querySelectorAll( 'section' );
3293
+
3294
+ if( typeof y !== 'undefined' ) {
3295
+ return verticalSlides ? verticalSlides[ y ] : undefined;
3296
+ }
3297
+
3298
+ return horizontalSlide;
3299
+ },
3300
+
3301
+ // Returns the previous slide element, may be null
3302
+ getPreviousSlide: function() {
3303
+ return previousSlide;
3304
+ },
3305
+
3306
+ // Returns the current slide element
3307
+ getCurrentSlide: function() {
3308
+ return currentSlide;
3309
+ },
3310
+
3311
+ // Returns the current scale of the presentation content
3312
+ getScale: function() {
3313
+ return scale;
3314
+ },
3315
+
3316
+ // Returns the current configuration object
3317
+ getConfig: function() {
3318
+ return config;
3319
+ },
3320
+
3321
+ // Helper method, retrieves query string as a key/value hash
3322
+ getQueryHash: function() {
3323
+ var query = {};
3324
+
3325
+ location.search.replace( /[A-Z0-9]+?=([\w\.%-]*)/gi, function(a) {
3326
+ query[ a.split( '=' ).shift() ] = a.split( '=' ).pop();
3327
+ } );
3328
+
3329
+ // Basic deserialization
3330
+ for( var i in query ) {
3331
+ var value = query[ i ];
3332
+
3333
+ query[ i ] = unescape( value );
3334
+
3335
+ if( value === 'null' ) query[ i ] = null;
3336
+ else if( value === 'true' ) query[ i ] = true;
3337
+ else if( value === 'false' ) query[ i ] = false;
3338
+ else if( value.match( /^\d+$/ ) ) query[ i ] = parseFloat( value );
3339
+ }
3340
+
3341
+ return query;
3342
+ },
3343
+
3344
+ // Returns true if we're currently on the first slide
3345
+ isFirstSlide: function() {
3346
+ return document.querySelector( SLIDES_SELECTOR + '.past' ) == null ? true : false;
3347
+ },
3348
+
3349
+ // Returns true if we're currently on the last slide
3350
+ isLastSlide: function() {
3351
+ if( currentSlide ) {
3352
+ // Does this slide has next a sibling?
3353
+ if( currentSlide.nextElementSibling ) return false;
3354
+
3355
+ // If it's vertical, does its parent have a next sibling?
3356
+ if( isVerticalSlide( currentSlide ) && currentSlide.parentNode.nextElementSibling ) return false;
3357
+
3358
+ return true;
3359
+ }
3360
+
3361
+ return false;
3362
+ },
3363
+
3364
+ // Checks if reveal.js has been loaded and is ready for use
3365
+ isReady: function() {
3366
+ return loaded;
3367
+ },
3368
+
3369
+ // Forward event binding to the reveal DOM element
3370
+ addEventListener: function( type, listener, useCapture ) {
3371
+ if( 'addEventListener' in window ) {
3372
+ ( dom.wrapper || document.querySelector( '.reveal' ) ).addEventListener( type, listener, useCapture );
3373
+ }
3374
+ },
3375
+ removeEventListener: function( type, listener, useCapture ) {
3376
+ if( 'addEventListener' in window ) {
3377
+ ( dom.wrapper || document.querySelector( '.reveal' ) ).removeEventListener( type, listener, useCapture );
3378
+ }
3379
+ }
3380
+ };
3381
+
3382
+ })();