├── LICENSE
├── README.md
├── apple-touch-icon.png
├── bower.json
├── css
├── impress-demo.css
├── impress.css
└── reverseEngineering.css
├── demo.html
├── favicon.png
├── images
├── Hopper-2.jpeg
├── Hopper.jpeg
├── IDA-1.jpeg
├── IDA-2.jpeg
├── MachOView-1.jpeg
├── MachOView-2.jpeg
├── class-dump.jpeg
├── crack-castle.png
├── cycript.jpeg
├── dumpdecrypt.jpeg
├── iOSOpenDev2.jpeg
├── ifile.PNG
├── iosOpenDev.jpeg
├── overview.jpeg
├── safe-castle.png
├── terminal.PNG
├── theos-1.jpeg
└── theos-2.jpeg
├── index.html
└── js
└── impress.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Alex Ao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iOS-Reverse-Engineering-presentation
2 |
3 | ## Overview
4 | iOS-Reverse-Engineering-presentation introduce the knowledge and tool chain of reverse engineering of iOS, which covering the basic use of reverse engineering of iOS.
5 |
6 | 
7 |
8 | ## Usage
9 | clone the repo and open the index.html to show the slides or click github io link.
10 |
11 | ## Resources
12 | * [class-dump](https://github.com/nygard/class-dump)
13 | * [Theos](https://github.com/theos/theos)
14 | * [iOSOpenDev](https://github.com/kokoabim/iOSOpenDev)
15 | * [IDA](https://www.hex-rays.com/products/ida/)
16 | * [Hopper](https://www.hopperapp.com/)
17 | * [MachOView](https://github.com/gdbinit/MachOView)
18 | * [dumpdecrypted](https://github.com/stefanesser/dumpdecrypted)
19 | * [Clutch](https://github.com/KJCracks/Clutch)
20 | * [cycript](http://www.cycript.org/)
21 | * [LLDB commands](https://objccn.io/issue-19-2/)
22 | * [chisel](https://github.com/facebook/chisel)
23 | * [iosre](http://iosre.com/)
24 |
25 | ## Workaround
26 | when you start iOS Reverse Engineering, you should install many tools, You may encounter a variety of problems. Here is a list of possible workarounds.
27 |
28 | * [The solution to the failure of the iOSOpenDev installation
29 | ](https://www.ianisme.com/ios/2319.html)
30 | * [The solution to the failure of make package](https://blog.sunnyyoung.net/post/ni-xiang/2017-01-11-theosan-zhuang-yu-pei-zhi)
31 |
32 | > when you execute `make package` command to package, you may encounter a failure situation, wrong prompt is "dpkg-deb: warning: deprecated compression type 'lzma'; use xz instead'", you should change value of `_THEOS_PLATFORM_DPKG_DEB_COMPRESSION` to xz
33 |
34 | ## Author
35 | Alex Ao, aozhimin0811@gmail.com
36 |
37 | ## License
38 |
39 | iOS-Reverse-Engineering-presentation is released under the MIT license. See LICENSE for details.
40 |
41 |
--------------------------------------------------------------------------------
/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/apple-touch-icon.png
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "impress-js",
3 | "version": "0.5.3",
4 | "main": "./js/impress.js",
5 | "description": "It's a presentation framework based on the power of CSS3 transforms and transitions in modern browsers and inspired by the idea behind prezi.com",
6 | "homepage": "https://github.com/bartaz/impress.js",
7 | "license" : ["http://bartaz.mit-license.org/", "http://www.gnu.org/licenses/"],
8 | "main": [
9 | "./js/impress.js"
10 | ],
11 | "keywords": [
12 | "slideshow",
13 | "css3"
14 | ],
15 | "author": {
16 | "name": "Bartek Szopka",
17 | "web": "http://bartaz.github.com"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/css/impress-demo.css:
--------------------------------------------------------------------------------
1 | /*
2 | So you like the style of impress.js demo?
3 | Or maybe you are just curious how it was done?
4 |
5 | You couldn't find a better place to find out!
6 |
7 | Welcome to the stylesheet impress.js demo presentation.
8 |
9 | Please remember that it is not meant to be a part of impress.js and is
10 | not required by impress.js.
11 | I expect that anyone creating a presentation for impress.js would create
12 | their own set of styles.
13 |
14 | But feel free to read through it and learn how to get the most of what
15 | impress.js provides.
16 |
17 | And let me be your guide.
18 |
19 | Shall we begin?
20 | */
21 |
22 |
23 | /*
24 | We start with a good ol' reset.
25 | That's the one by Eric Meyer http://meyerweb.com/eric/tools/css/reset/
26 |
27 | You can probably argue if it is needed here, or not, but for sure it
28 | doesn't do any harm and gives us a fresh start.
29 | */
30 |
31 | html, body, div, span, applet, object, iframe,
32 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
33 | a, abbr, acronym, address, big, cite, code,
34 | del, dfn, em, img, ins, kbd, q, s, samp,
35 | small, strike, strong, sub, sup, tt, var,
36 | b, u, i, center,
37 | dl, dt, dd, ol, ul, li,
38 | fieldset, form, label, legend,
39 | table, caption, tbody, tfoot, thead, tr, th, td,
40 | article, aside, canvas, details, embed,
41 | figure, figcaption, footer, header, hgroup,
42 | menu, nav, output, ruby, section, summary,
43 | time, mark, audio, video {
44 | margin: 0;
45 | padding: 0;
46 | border: 0;
47 | font-size: 100%;
48 | font: inherit;
49 | vertical-align: baseline;
50 | }
51 |
52 | /* HTML5 display-role reset for older browsers */
53 | article, aside, details, figcaption, figure,
54 | footer, header, hgroup, menu, nav, section {
55 | display: block;
56 | }
57 | body {
58 | line-height: 1;
59 | }
60 | ol, ul {
61 | list-style: none;
62 | }
63 | blockquote, q {
64 | quotes: none;
65 | }
66 | blockquote:before, blockquote:after,
67 | q:before, q:after {
68 | content: '';
69 | content: none;
70 | }
71 |
72 | table {
73 | border-collapse: collapse;
74 | border-spacing: 0;
75 | }
76 |
77 | /*
78 | Now here is when interesting things start to appear.
79 |
80 | We set up
styles with default font and nice gradient in the background.
81 | And yes, there is a lot of repetition there because of -prefixes but we don't
82 | want to leave anybody behind.
83 | */
84 | body {
85 | font-family: 'PT Sans', sans-serif;
86 | min-height: 740px;
87 |
88 | background: rgb(215, 215, 215);
89 | background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
90 | background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
91 | background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
92 | background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
93 | background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
94 | background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
95 | }
96 |
97 | /*
98 | Now let's bring some text styles back ...
99 | */
100 | b, strong { font-weight: bold }
101 | i, em { font-style: italic }
102 |
103 | /*
104 | ... and give links a nice look.
105 | */
106 | a {
107 | color: inherit;
108 | text-decoration: none;
109 | padding: 0 0.1em;
110 | background: rgba(255,255,255,0.5);
111 | text-shadow: -1px -1px 2px rgba(100,100,100,0.9);
112 | border-radius: 0.2em;
113 |
114 | -webkit-transition: 0.5s;
115 | -moz-transition: 0.5s;
116 | -ms-transition: 0.5s;
117 | -o-transition: 0.5s;
118 | transition: 0.5s;
119 | }
120 |
121 | a:hover,
122 | a:focus {
123 | background: rgba(255,255,255,1);
124 | text-shadow: -1px -1px 2px rgba(100,100,100,0.5);
125 | }
126 |
127 | /*
128 | Because the main point behind the impress.js demo is to demo impress.js
129 | we display a fallback message for users with browsers that don't support
130 | all the features required by it.
131 |
132 | All of the content will be still fully accessible for them, but I want
133 | them to know that they are missing something - that's what the demo is
134 | about, isn't it?
135 |
136 | And then we hide the message, when support is detected in the browser.
137 | */
138 |
139 | .fallback-message {
140 | font-family: sans-serif;
141 | line-height: 1.3;
142 |
143 | width: 780px;
144 | padding: 10px 10px 0;
145 | margin: 20px auto;
146 |
147 | border: 1px solid #E4C652;
148 | border-radius: 10px;
149 | background: #EEDC94;
150 | }
151 |
152 | .fallback-message p {
153 | margin-bottom: 10px;
154 | }
155 |
156 | .impress-supported .fallback-message {
157 | display: none;
158 | }
159 |
160 | /*
161 | Now let's style the presentation steps.
162 |
163 | We start with basics to make sure it displays correctly in everywhere ...
164 | */
165 |
166 | .step {
167 | position: relative;
168 | width: 900px;
169 | padding: 40px;
170 | margin: 20px auto;
171 |
172 | -webkit-box-sizing: border-box;
173 | -moz-box-sizing: border-box;
174 | -ms-box-sizing: border-box;
175 | -o-box-sizing: border-box;
176 | box-sizing: border-box;
177 |
178 | font-family: 'PT Serif', georgia, serif;
179 | font-size: 48px;
180 | line-height: 1.5;
181 | }
182 |
183 | /*
184 | ... and we enhance the styles for impress.js.
185 |
186 | Basically we remove the margin and make inactive steps a little bit transparent.
187 | */
188 | .impress-enabled .step {
189 | margin: 0;
190 | opacity: 0.3;
191 |
192 | -webkit-transition: opacity 1s;
193 | -moz-transition: opacity 1s;
194 | -ms-transition: opacity 1s;
195 | -o-transition: opacity 1s;
196 | transition: opacity 1s;
197 | }
198 |
199 | .impress-enabled .step.active { opacity: 1 }
200 |
201 | /*
202 | These 'slide' step styles were heavily inspired by HTML5 Slides:
203 | http://html5slides.googlecode.com/svn/trunk/styles.css
204 |
205 | ;)
206 |
207 | They cover everything what you see on first three steps of the demo.
208 | */
209 | .slide {
210 | display: block;
211 |
212 | width: 900px;
213 | height: 700px;
214 | padding: 40px 60px;
215 |
216 | background-color: white;
217 | border: 1px solid rgba(0, 0, 0, .3);
218 | border-radius: 10px;
219 | box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
220 |
221 | color: rgb(102, 102, 102);
222 | text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
223 |
224 | font-family: 'Open Sans', Arial, sans-serif;
225 | font-size: 30px;
226 | line-height: 36px;
227 | letter-spacing: -1px;
228 | }
229 |
230 | .slide q {
231 | display: block;
232 | font-size: 50px;
233 | line-height: 72px;
234 |
235 | margin-top: 100px;
236 | }
237 |
238 | .slide q strong {
239 | white-space: nowrap;
240 | }
241 |
242 | /*
243 | And now we start to style each step separately.
244 |
245 | I agree that this may be not the most efficient, object-oriented and
246 | scalable way of styling, but most of steps have quite a custom look
247 | and typography tricks here and there, so they had to be styled separately.
248 |
249 | First is the title step with a big
(no room for padding) and some
250 | 3D positioning along Z axis.
251 | */
252 |
253 | #title {
254 | padding: 0;
255 | }
256 |
257 | #title .try {
258 | font-size: 64px;
259 | position: absolute;
260 | top: -0.5em;
261 | left: 1.5em;
262 |
263 | -webkit-transform: translateZ(20px);
264 | -moz-transform: translateZ(20px);
265 | -ms-transform: translateZ(20px);
266 | -o-transform: translateZ(20px);
267 | transform: translateZ(20px);
268 | }
269 |
270 | #title h1 {
271 | font-size: 190px;
272 |
273 | -webkit-transform: translateZ(50px);
274 | -moz-transform: translateZ(50px);
275 | -ms-transform: translateZ(50px);
276 | -o-transform: translateZ(50px);
277 | transform: translateZ(50px);
278 | }
279 |
280 | #title .footnote {
281 | font-size: 32px;
282 | }
283 |
284 | /*
285 | Second step is nothing special, just a text with a link, so it doesn't need
286 | any special styling.
287 |
288 | Let's move to 'big thoughts' with centered text and custom font sizes.
289 | */
290 | #big {
291 | width: 600px;
292 | text-align: center;
293 | font-size: 60px;
294 | line-height: 1;
295 | }
296 |
297 | #big b {
298 | display: block;
299 | font-size: 250px;
300 | line-height: 250px;
301 | }
302 |
303 | #big .thoughts {
304 | font-size: 90px;
305 | line-height: 150px;
306 | }
307 |
308 | /*
309 | 'Tiny ideas' just need some tiny styling.
310 | */
311 | #tiny {
312 | width: 500px;
313 | text-align: center;
314 | }
315 |
316 | /*
317 | This step has some animated text ...
318 | */
319 | #ing { width: 500px }
320 |
321 | /*
322 | ... so we define display to `inline-block` to enable transforms and
323 | transition duration to 0.5s ...
324 | */
325 | #ing b {
326 | display: inline-block;
327 | -webkit-transition: 0.5s;
328 | -moz-transition: 0.5s;
329 | -ms-transition: 0.5s;
330 | -o-transition: 0.5s;
331 | transition: 0.5s;
332 | }
333 |
334 | /*
335 | ... and we want 'positioning` word to move up a bit when the step gets
336 | `present` class ...
337 | */
338 | #ing.present .positioning {
339 | -webkit-transform: translateY(-10px);
340 | -moz-transform: translateY(-10px);
341 | -ms-transform: translateY(-10px);
342 | -o-transform: translateY(-10px);
343 | transform: translateY(-10px);
344 | }
345 |
346 | /*
347 | ... 'rotating' to rotate a quarter of a second later ...
348 | */
349 | #ing.present .rotating {
350 | -webkit-transform: rotate(-10deg);
351 | -moz-transform: rotate(-10deg);
352 | -ms-transform: rotate(-10deg);
353 | -o-transform: rotate(-10deg);
354 | transform: rotate(-10deg);
355 |
356 | -webkit-transition-delay: 0.25s;
357 | -moz-transition-delay: 0.25s;
358 | -ms-transition-delay: 0.25s;
359 | -o-transition-delay: 0.25s;
360 | transition-delay: 0.25s;
361 | }
362 |
363 | /*
364 | ... and 'scaling' to scale down after another quarter of a second.
365 | */
366 | #ing.present .scaling {
367 | -webkit-transform: scale(0.7);
368 | -moz-transform: scale(0.7);
369 | -ms-transform: scale(0.7);
370 | -o-transform: scale(0.7);
371 | transform: scale(0.7);
372 |
373 | -webkit-transition-delay: 0.5s;
374 | -moz-transition-delay: 0.5s;
375 | -ms-transition-delay: 0.5s;
376 | -o-transition-delay: 0.5s;
377 | transition-delay: 0.5s;
378 | }
379 |
380 | /*
381 | The 'imagination' step is again some boring font-sizing.
382 | */
383 |
384 | #imagination {
385 | width: 600px;
386 | }
387 |
388 | #imagination .imagination {
389 | font-size: 78px;
390 | }
391 |
392 | /*
393 | There is nothing really special about 'use the source, Luke' step, too,
394 | except maybe of the Yoda background.
395 |
396 | As you can see below I've 'hard-coded' it in data URL.
397 | That's not the best way to serve images, but because that's just this one
398 | I decided it will be OK to have it this way.
399 |
400 | Just make sure you don't blindly copy this approach.
401 | */
402 | #source {
403 | width: 700px;
404 | padding-bottom: 300px;
405 |
406 | /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */
407 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==);
408 | background-position: bottom right;
409 | background-repeat: no-repeat;
410 | }
411 |
412 | #source q {
413 | font-size: 60px;
414 | }
415 |
416 | /*
417 | And the "it's in 3D" step again brings some 3D typography - just for fun.
418 |
419 | Because we want to position elements in 3D we set transform-style to
420 | `preserve-3d` on the paragraph.
421 | It is not needed by webkit browsers, but it is in Firefox. It's hard to say
422 | which behaviour is correct as 3D transforms spec is not very clear about it.
423 | */
424 | #its-in-3d p {
425 | -webkit-transform-style: preserve-3d;
426 | -moz-transform-style: preserve-3d; /* Y U need this Firefox?! */
427 | -ms-transform-style: preserve-3d;
428 | -o-transform-style: preserve-3d;
429 | transform-style: preserve-3d;
430 | }
431 |
432 | /*
433 | Below we position each word separately along Z axis and we want it to transition
434 | to default position in 0.5s when the step gets `present` class.
435 |
436 | Quite a simple idea, but lot's of styles and prefixes.
437 | */
438 | #its-in-3d span,
439 | #its-in-3d b {
440 | display: inline-block;
441 | -webkit-transform: translateZ(40px);
442 | -moz-transform: translateZ(40px);
443 | -ms-transform: translateZ(40px);
444 | -o-transform: translateZ(40px);
445 | transform: translateZ(40px);
446 |
447 | -webkit-transition: 0.5s;
448 | -moz-transition: 0.5s;
449 | -ms-transition: 0.5s;
450 | -o-transition: 0.5s;
451 | transition: 0.5s;
452 | }
453 |
454 | #its-in-3d .have {
455 | -webkit-transform: translateZ(-40px);
456 | -moz-transform: translateZ(-40px);
457 | -ms-transform: translateZ(-40px);
458 | -o-transform: translateZ(-40px);
459 | transform: translateZ(-40px);
460 | }
461 |
462 | #its-in-3d .you {
463 | -webkit-transform: translateZ(20px);
464 | -moz-transform: translateZ(20px);
465 | -ms-transform: translateZ(20px);
466 | -o-transform: translateZ(20px);
467 | transform: translateZ(20px);
468 | }
469 |
470 | #its-in-3d .noticed {
471 | -webkit-transform: translateZ(-40px);
472 | -moz-transform: translateZ(-40px);
473 | -ms-transform: translateZ(-40px);
474 | -o-transform: translateZ(-40px);
475 | transform: translateZ(-40px);
476 | }
477 |
478 | #its-in-3d .its {
479 | -webkit-transform: translateZ(60px);
480 | -moz-transform: translateZ(60px);
481 | -ms-transform: translateZ(60px);
482 | -o-transform: translateZ(60px);
483 | transform: translateZ(60px);
484 | }
485 |
486 | #its-in-3d .in {
487 | -webkit-transform: translateZ(-10px);
488 | -moz-transform: translateZ(-10px);
489 | -ms-transform: translateZ(-10px);
490 | -o-transform: translateZ(-10px);
491 | transform: translateZ(-10px);
492 | }
493 |
494 | #its-in-3d .footnote {
495 | font-size: 32px;
496 |
497 | -webkit-transform: translateZ(-10px);
498 | -moz-transform: translateZ(-10px);
499 | -ms-transform: translateZ(-10px);
500 | -o-transform: translateZ(-10px);
501 | transform: translateZ(-10px);
502 | }
503 |
504 | #its-in-3d.present span,
505 | #its-in-3d.present b {
506 | -webkit-transform: translateZ(0px);
507 | -moz-transform: translateZ(0px);
508 | -ms-transform: translateZ(0px);
509 | -o-transform: translateZ(0px);
510 | transform: translateZ(0px);
511 | }
512 |
513 | /*
514 | The last step is an overview.
515 | There is no content in it, so we make sure it's not visible because we want
516 | to be able to click on other steps.
517 |
518 | */
519 | #overview { display: none }
520 |
521 | /*
522 | We also make other steps visible and give them a pointer cursor using the
523 | `impress-on-` class.
524 | */
525 | .impress-on-overview .step {
526 | opacity: 1;
527 | cursor: pointer;
528 | }
529 |
530 |
531 | /*
532 | Now, when we have all the steps styled let's give users a hint how to navigate
533 | around the presentation.
534 |
535 | The best way to do this would be to use JavaScript, show a delayed hint for a
536 | first time users, then hide it and store a status in cookie or localStorage...
537 |
538 | But I wanted to have some CSS fun and avoid additional scripting...
539 |
540 | Let me explain it first, so maybe the transition magic will be more readable
541 | when you read the code.
542 |
543 | First of all I wanted the hint to appear only when user is idle for a while.
544 | You can't detect the 'idle' state in CSS, but I delayed a appearing of the
545 | hint by 5s using transition-delay.
546 |
547 | You also can't detect in CSS if the user is a first-time visitor, so I had to
548 | make an assumption that I'll only show the hint on the first step. And when
549 | the step is changed hide the hint, because I can assume that user already
550 | knows how to navigate.
551 |
552 | To summarize it - hint is shown when the user is on the first step for longer
553 | than 5 seconds.
554 |
555 | The other problem I had was caused by the fact that I wanted the hint to fade
556 | in and out. It can be easily achieved by transitioning the opacity property.
557 | But that also meant that the hint was always on the screen, even if totally
558 | transparent. It covered part of the screen and you couldn't correctly clicked
559 | through it.
560 | Unfortunately you cannot transition between display `block` and `none` in pure
561 | CSS, so I needed a way to not only fade out the hint but also move it out of
562 | the screen.
563 |
564 | I solved this problem by positioning the hint below the bottom of the screen
565 | with CSS transform and moving it up to show it. But I also didn't want this move
566 | to be visible. I wanted the hint only to fade in and out visually, so I delayed
567 | the fade in transition, so it starts when the hint is already in its correct
568 | position on the screen.
569 |
570 | I know, it sounds complicated ... maybe it would be easier with the code?
571 | */
572 |
573 | .hint {
574 | /*
575 | We hide the hint until presentation is started and from browsers not supporting
576 | impress.js, as they will have a linear scrollable view ...
577 | */
578 | display: none;
579 |
580 | /*
581 | ... and give it some fixed position and nice styles.
582 | */
583 | position: fixed;
584 | left: 0;
585 | right: 0;
586 | bottom: 200px;
587 |
588 | background: rgba(0,0,0,0.5);
589 | color: #EEE;
590 | text-align: center;
591 |
592 | font-size: 50px;
593 | padding: 20px;
594 |
595 | z-index: 100;
596 |
597 | /*
598 | By default we don't want the hint to be visible, so we make it transparent ...
599 | */
600 | opacity: 0;
601 |
602 | /*
603 | ... and position it below the bottom of the screen (relative to it's fixed position)
604 | */
605 | -webkit-transform: translateY(400px);
606 | -moz-transform: translateY(400px);
607 | -ms-transform: translateY(400px);
608 | -o-transform: translateY(400px);
609 | transform: translateY(400px);
610 |
611 | /*
612 | Now let's imagine that the hint is visible and we want to fade it out and move out
613 | of the screen.
614 |
615 | So we define the transition on the opacity property with 1s duration and another
616 | transition on transform property delayed by 1s so it will happen after the fade out
617 | on opacity finished.
618 |
619 | This way user will not see the hint moving down.
620 | */
621 | -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s;
622 | -moz-transition: opacity 1s, -moz-transform 0.5s 1s;
623 | -ms-transition: opacity 1s, -ms-transform 0.5s 1s;
624 | -o-transition: opacity 1s, -o-transform 0.5s 1s;
625 | transition: opacity 1s, transform 0.5s 1s;
626 | }
627 |
628 | /*
629 | Now we 'enable' the hint when presentation is initialized ...
630 | */
631 | .impress-enabled .hint { display: block }
632 |
633 | /*
634 | ... and we will show it when the first step (with id 'bored') is active.
635 | */
636 | .impress-on-bored .hint {
637 | /*
638 | We remove the transparency and position the hint in its default fixed
639 | position.
640 | */
641 | opacity: 1;
642 |
643 | -webkit-transform: translateY(0px);
644 | -moz-transform: translateY(0px);
645 | -ms-transform: translateY(0px);
646 | -o-transform: translateY(0px);
647 | transform: translateY(0px);
648 |
649 | /*
650 | Now for fade in transition we have the oposite situation from the one
651 | above.
652 |
653 | First after 4.5s delay we animate the transform property to move the hint
654 | into its correct position and after that we fade it in with opacity
655 | transition.
656 | */
657 | -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s 4.5s;
658 | -moz-transition: opacity 1s 5s, -moz-transform 0.5s 4.5s;
659 | -ms-transition: opacity 1s 5s, -ms-transform 0.5s 4.5s;
660 | -o-transition: opacity 1s 5s, -o-transform 0.5s 4.5s;
661 | transition: opacity 1s 5s, transform 0.5s 4.5s;
662 | }
663 |
664 | /*
665 | And as the last thing there is a workaround for quite strange bug.
666 | It happens a lot in Chrome. I don't remember if I've seen it in Firefox.
667 |
668 | Sometimes the element positioned in 3D (especially when it's moved back
669 | along Z axis) is not clickable, because it falls 'behind' the
670 | element.
671 |
672 | To prevent this, I decided to make non clickable by setting
673 | pointer-events property to `none` value.
674 | Value if this property is inherited, so to make everything else clickable
675 | I bring it back on the #impress element.
676 |
677 | If you want to know more about `pointer-events` here are some docs:
678 | https://developer.mozilla.org/en/CSS/pointer-events
679 |
680 | There is one very important thing to notice about this workaround - it makes
681 | everything 'unclickable' except what's in #impress element.
682 |
683 | So use it wisely ... or don't use at all.
684 | */
685 | .impress-enabled { pointer-events: none }
686 | .impress-enabled #impress { pointer-events: auto }
687 |
688 | /*
689 | There is one funny thing I just realized.
690 |
691 | Thanks to this workaround above everything except #impress element is invisible
692 | for click events. That means that the hint element is also not clickable.
693 | So basically all of this transforms and delayed transitions trickery was probably
694 | not needed at all...
695 |
696 | But it was fun to learn about it, wasn't it?
697 | */
698 |
699 | /*
700 | That's all I have for you in this file.
701 | Thanks for reading. I hope you enjoyed it at least as much as I enjoyed writing it
702 | for you.
703 | */
704 |
--------------------------------------------------------------------------------
/css/impress.css:
--------------------------------------------------------------------------------
1 | /*
2 | So you like the style of impress.js demo?
3 | Or maybe you are just curious how it was done?
4 |
5 | You couldn't find a better place to find out!
6 |
7 | Welcome to the stylesheet impress.js demo presentation.
8 |
9 | Please remember that it is not meant to be a part of impress.js and is
10 | not required by impress.js.
11 | I expect that anyone creating a presentation for impress.js would create
12 | their own set of styles.
13 |
14 | But feel free to read through it and learn how to get the most of what
15 | impress.js provides.
16 |
17 | And let me be your guide.
18 |
19 | Shall we begin?
20 | */
21 |
22 |
23 | /*
24 | We start with a good ol' reset.
25 | That's the one by Eric Meyer http://meyerweb.com/eric/tools/css/reset/
26 |
27 | You can probably argue if it is needed here, or not, but for sure it
28 | doesn't do any harm and gives us a fresh start.
29 | */
30 |
31 | html, body, div, span, applet, object, iframe,
32 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
33 | a, abbr, acronym, address, big, cite, code,
34 | del, dfn, em, img, ins, kbd, q, s, samp,
35 | small, strike, strong, sub, sup, tt, var,
36 | b, u, i, center,
37 | dl, dt, dd, ol, ul, li,
38 | fieldset, form, label, legend,
39 | table, caption, tbody, tfoot, thead, tr, th, td,
40 | article, aside, canvas, details, embed,
41 | figure, figcaption, footer, header, hgroup,
42 | menu, nav, output, ruby, section, summary,
43 | time, mark, audio, video {
44 | margin: 0;
45 | padding: 0;
46 | border: 0;
47 | font-size: 100%;
48 | font: inherit;
49 | vertical-align: baseline;
50 | }
51 |
52 | /* HTML5 display-role reset for older browsers */
53 | article, aside, details, figcaption, figure,
54 | footer, header, hgroup, menu, nav, section {
55 | display: block;
56 | }
57 | body {
58 | line-height: 1;
59 | }
60 | ol, ul {
61 | list-style: none;
62 | }
63 | blockquote, q {
64 | quotes: none;
65 | }
66 | blockquote:before, blockquote:after,
67 | q:before, q:after {
68 | content: '';
69 | content: none;
70 | }
71 |
72 | table {
73 | border-collapse: collapse;
74 | border-spacing: 0;
75 | }
76 |
77 | /*
78 | Now here is when interesting things start to appear.
79 |
80 | We set up styles with default font and nice gradient in the background.
81 | And yes, there is a lot of repetition there because of -prefixes but we don't
82 | want to leave anybody behind.
83 | */
84 | body {
85 | font-family: 'PT Sans', sans-serif;
86 | min-height: 740px;
87 |
88 | background: rgb(215, 215, 215);
89 | background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
90 | background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
91 | background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
92 | background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
93 | background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
94 | background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
95 | }
96 |
97 | /*
98 | Now let's bring some text styles back ...
99 | */
100 | b, strong { font-weight: bold }
101 | i, em { font-style: italic }
102 |
103 | /*
104 | ... and give links a nice look.
105 | */
106 | a {
107 | color: inherit;
108 | text-decoration: none;
109 | padding: 0 0.1em;
110 | background: rgba(255,255,255,0.5);
111 | text-shadow: -1px -1px 2px rgba(100,100,100,0.9);
112 | border-radius: 0.2em;
113 |
114 | -webkit-transition: 0.5s;
115 | -moz-transition: 0.5s;
116 | -ms-transition: 0.5s;
117 | -o-transition: 0.5s;
118 | transition: 0.5s;
119 | }
120 |
121 | a:hover,
122 | a:focus {
123 | background: rgba(255,255,255,1);
124 | text-shadow: -1px -1px 2px rgba(100,100,100,0.5);
125 | }
126 |
127 | /*
128 | Because the main point behind the impress.js demo is to demo impress.js
129 | we display a fallback message for users with browsers that don't support
130 | all the features required by it.
131 |
132 | All of the content will be still fully accessible for them, but I want
133 | them to know that they are missing something - that's what the demo is
134 | about, isn't it?
135 |
136 | And then we hide the message, when support is detected in the browser.
137 | */
138 |
139 | .fallback-message {
140 | font-family: sans-serif;
141 | line-height: 1.3;
142 |
143 | width: 780px;
144 | padding: 10px 10px 0;
145 | margin: 20px auto;
146 |
147 | border: 1px solid #E4C652;
148 | border-radius: 10px;
149 | background: #EEDC94;
150 | }
151 |
152 | .fallback-message p {
153 | margin-bottom: 10px;
154 | }
155 |
156 | .impress-supported .fallback-message {
157 | display: none;
158 | }
159 |
160 | /*
161 | Now let's style the presentation steps.
162 |
163 | We start with basics to make sure it displays correctly in everywhere ...
164 | */
165 |
166 | .step {
167 | position: relative;
168 | width: 900px;
169 | padding: 40px;
170 | margin: 20px auto;
171 |
172 | -webkit-box-sizing: border-box;
173 | -moz-box-sizing: border-box;
174 | -ms-box-sizing: border-box;
175 | -o-box-sizing: border-box;
176 | box-sizing: border-box;
177 |
178 | font-family: 'PT Serif', georgia, serif;
179 | font-size: 48px;
180 | line-height: 1.5;
181 | }
182 |
183 | /*
184 | ... and we enhance the styles for impress.js.
185 |
186 | Basically we remove the margin and make inactive steps a little bit transparent.
187 | */
188 | .impress-enabled .step {
189 | margin: 0;
190 | opacity: 0.3;
191 |
192 | -webkit-transition: opacity 1s;
193 | -moz-transition: opacity 1s;
194 | -ms-transition: opacity 1s;
195 | -o-transition: opacity 1s;
196 | transition: opacity 1s;
197 | }
198 |
199 | .impress-enabled .step.active { opacity: 1 }
200 |
201 | /*
202 | These 'slide' step styles were heavily inspired by HTML5 Slides:
203 | http://html5slides.googlecode.com/svn/trunk/styles.css
204 |
205 | ;)
206 |
207 | They cover everything what you see on first three steps of the demo.
208 | */
209 | .slide {
210 | display: block;
211 |
212 | width: 900px;
213 | height: 700px;
214 | padding: 40px 60px;
215 |
216 | background-color: white;
217 | border: 1px solid rgba(0, 0, 0, .3);
218 | border-radius: 10px;
219 | box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
220 |
221 | color: rgb(102, 102, 102);
222 | text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
223 |
224 | font-family: 'Open Sans', Arial, sans-serif;
225 | font-size: 30px;
226 | line-height: 36px;
227 | letter-spacing: -1px;
228 | }
229 |
230 | .slide q {
231 | display: block;
232 | font-size: 50px;
233 | line-height: 72px;
234 |
235 | margin-top: 100px;
236 | }
237 |
238 | .slide q strong {
239 | white-space: nowrap;
240 | }
241 |
242 | /*
243 | And now we start to style each step separately.
244 |
245 | I agree that this may be not the most efficient, object-oriented and
246 | scalable way of styling, but most of steps have quite a custom look
247 | and typography tricks here and there, so they had to be styled separately.
248 |
249 | First is the title step with a big
(no room for padding) and some
250 | 3D positioning along Z axis.
251 | */
252 |
253 | /*
254 | We also make other steps visible and give them a pointer cursor using the
255 | `impress-on-` class.
256 | */
257 | .impress-on-overview .step {
258 | opacity: 1;
259 | cursor: pointer;
260 | }
261 |
262 |
263 | /*
264 | Now, when we have all the steps styled let's give users a hint how to navigate
265 | around the presentation.
266 |
267 | The best way to do this would be to use JavaScript, show a delayed hint for a
268 | first time users, then hide it and store a status in cookie or localStorage...
269 |
270 | But I wanted to have some CSS fun and avoid additional scripting...
271 |
272 | Let me explain it first, so maybe the transition magic will be more readable
273 | when you read the code.
274 |
275 | First of all I wanted the hint to appear only when user is idle for a while.
276 | You can't detect the 'idle' state in CSS, but I delayed a appearing of the
277 | hint by 5s using transition-delay.
278 |
279 | You also can't detect in CSS if the user is a first-time visitor, so I had to
280 | make an assumption that I'll only show the hint on the first step. And when
281 | the step is changed hide the hint, because I can assume that user already
282 | knows how to navigate.
283 |
284 | To summarize it - hint is shown when the user is on the first step for longer
285 | than 5 seconds.
286 |
287 | The other problem I had was caused by the fact that I wanted the hint to fade
288 | in and out. It can be easily achieved by transitioning the opacity property.
289 | But that also meant that the hint was always on the screen, even if totally
290 | transparent. It covered part of the screen and you couldn't correctly clicked
291 | through it.
292 | Unfortunately you cannot transition between display `block` and `none` in pure
293 | CSS, so I needed a way to not only fade out the hint but also move it out of
294 | the screen.
295 |
296 | I solved this problem by positioning the hint below the bottom of the screen
297 | with CSS transform and moving it up to show it. But I also didn't want this move
298 | to be visible. I wanted the hint only to fade in and out visually, so I delayed
299 | the fade in transition, so it starts when the hint is already in its correct
300 | position on the screen.
301 |
302 | I know, it sounds complicated ... maybe it would be easier with the code?
303 | */
304 |
305 | .hint {
306 | /*
307 | We hide the hint until presentation is started and from browsers not supporting
308 | impress.js, as they will have a linear scrollable view ...
309 | */
310 | display: none;
311 |
312 | /*
313 | ... and give it some fixed position and nice styles.
314 | */
315 | position: fixed;
316 | left: 0;
317 | right: 0;
318 | bottom: 200px;
319 |
320 | background: rgba(0,0,0,0.5);
321 | color: #EEE;
322 | text-align: center;
323 |
324 | font-size: 50px;
325 | padding: 20px;
326 |
327 | z-index: 100;
328 |
329 | /*
330 | By default we don't want the hint to be visible, so we make it transparent ...
331 | */
332 | opacity: 0;
333 |
334 | /*
335 | ... and position it below the bottom of the screen (relative to it's fixed position)
336 | */
337 | -webkit-transform: translateY(400px);
338 | -moz-transform: translateY(400px);
339 | -ms-transform: translateY(400px);
340 | -o-transform: translateY(400px);
341 | transform: translateY(400px);
342 |
343 | /*
344 | Now let's imagine that the hint is visible and we want to fade it out and move out
345 | of the screen.
346 |
347 | So we define the transition on the opacity property with 1s duration and another
348 | transition on transform property delayed by 1s so it will happen after the fade out
349 | on opacity finished.
350 |
351 | This way user will not see the hint moving down.
352 | */
353 | -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s;
354 | -moz-transition: opacity 1s, -moz-transform 0.5s 1s;
355 | -ms-transition: opacity 1s, -ms-transform 0.5s 1s;
356 | -o-transition: opacity 1s, -o-transform 0.5s 1s;
357 | transition: opacity 1s, transform 0.5s 1s;
358 | }
359 |
360 | /*
361 | Now we 'enable' the hint when presentation is initialized ...
362 | */
363 | .impress-enabled .hint { display: block }
364 |
365 | /*
366 | And as the last thing there is a workaround for quite strange bug.
367 | It happens a lot in Chrome. I don't remember if I've seen it in Firefox.
368 |
369 | Sometimes the element positioned in 3D (especially when it's moved back
370 | along Z axis) is not clickable, because it falls 'behind' the
371 | element.
372 |
373 | To prevent this, I decided to make non clickable by setting
374 | pointer-events property to `none` value.
375 | Value if this property is inherited, so to make everything else clickable
376 | I bring it back on the #impress element.
377 |
378 | If you want to know more about `pointer-events` here are some docs:
379 | https://developer.mozilla.org/en/CSS/pointer-events
380 |
381 | There is one very important thing to notice about this workaround - it makes
382 | everything 'unclickable' except what's in #impress element.
383 |
384 | So use it wisely ... or don't use at all.
385 | */
386 | .impress-enabled { pointer-events: none }
387 | .impress-enabled #impress { pointer-events: auto }
388 |
389 | /*
390 | There is one funny thing I just realized.
391 |
392 | Thanks to this workaround above everything except #impress element is invisible
393 | for click events. That means that the hint element is also not clickable.
394 | So basically all of this transforms and delayed transitions trickery was probably
395 | not needed at all...
396 |
397 | But it was fun to learn about it, wasn't it?
398 | */
399 |
400 | /*
401 | That's all I have for you in this file.
402 | Thanks for reading. I hope you enjoyed it at least as much as I enjoyed writing it
403 | for you.
404 | */
405 |
--------------------------------------------------------------------------------
/css/reverseEngineering.css:
--------------------------------------------------------------------------------
1 | /*
2 | ... and we will show it when the first step (with id 'index') is active.
3 | */
4 | .impress-on-title .hint {
5 | /*
6 | We remove the transparency and position the hint in its default fixed
7 | position.
8 | */
9 | opacity: 1;
10 |
11 | -webkit-transform: translateY(0px);
12 | -moz-transform: translateY(0px);
13 | -ms-transform: translateY(0px);
14 | -o-transform: translateY(0px);
15 | transform: translateY(0px);
16 |
17 | /*
18 | Now for fade in transition we have the oposite situation from the one
19 | above.
20 |
21 | First after 4.5s delay we animate the transform property to move the hint
22 | into its correct position and after that we fade it in with opacity
23 | transition.
24 | */
25 | -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s 4.5s;
26 | -moz-transition: opacity 1s 5s, -moz-transform 0.5s 4.5s;
27 | -ms-transition: opacity 1s 5s, -ms-transform 0.5s 4.5s;
28 | -o-transition: opacity 1s 5s, -o-transform 0.5s 4.5s;
29 | transition: opacity 1s 5s, transform 0.5s 4.5s;
30 | }
31 |
32 | .step h1 {
33 | font-weight: bold;
34 | text-align: center;
35 | }
36 |
37 | .step p, .step q, .step ul {
38 | font-size: 25px;
39 | }
40 |
41 | .step ul ul {
42 | font-size: 22px;
43 | padding-left: 30px;
44 | }
45 |
46 | .step ul li {
47 | list-style-type: disc;
48 | list-style-position: inside;
49 | }
50 |
51 | .step u {
52 | font-size: 30px;
53 | padding: 10px;
54 | border: 1px solid black;
55 | border-radius: 10px;
56 | text-decoration: none;
57 | }
58 |
59 | #title {
60 | padding: 0;
61 | }
62 |
63 | #title .try {
64 | font-size: 30px;
65 | position: absolute;
66 | top: -0.5em;
67 | left: 1.5em;
68 |
69 | -webkit-transform: translateZ(20px);
70 | -moz-transform: translateZ(20px);
71 | -ms-transform: translateZ(20px);
72 | -o-transform: translateZ(20px);
73 | transform: translateZ(20px);
74 | }
75 |
76 | #title h1 {
77 | font-size: 80px;
78 |
79 | -webkit-transform: translateZ(50px);
80 | -moz-transform: translateZ(50px);
81 | -ms-transform: translateZ(50px);
82 | -o-transform: translateZ(50px);
83 | transform: translateZ(50px);
84 | }
85 |
86 | #title .footnote {
87 | font-size: 32px;
88 | }
89 |
90 | #family p {
91 | margin-bottom: 30px;
92 | }
93 |
94 | /*
95 | Second step is nothing special, just a text with a link, so it doesn't need
96 | any special styling.
97 |
98 | Let's move to 'big thoughts' with centered text and custom font sizes.
99 | */
100 | #big {
101 | width: 600px;
102 | text-align: center;
103 | font-size: 60px;
104 | line-height: 1;
105 | }
106 |
107 | #big b {
108 | display: block;
109 | font-size: 250px;
110 | line-height: 250px;
111 | }
112 |
113 | #big .thoughts {
114 | font-size: 90px;
115 | line-height: 150px;
116 | }
117 |
118 | /*
119 | 'Tiny ideas' just need some tiny styling.
120 | */
121 | #tiny {
122 | width: 500px;
123 | text-align: center;
124 | }
125 |
126 | /*
127 | This step has some animated text ...
128 | */
129 | #ing { width: 500px }
130 |
131 | /*
132 | ... so we define display to `inline-block` to enable transforms and
133 | transition duration to 0.5s ...
134 | */
135 | #ing b {
136 | display: inline-block;
137 | -webkit-transition: 0.5s;
138 | -moz-transition: 0.5s;
139 | -ms-transition: 0.5s;
140 | -o-transition: 0.5s;
141 | transition: 0.5s;
142 | }
143 |
144 | /*
145 | ... and we want 'positioning` word to move up a bit when the step gets
146 | `present` class ...
147 | */
148 | #ing.present .positioning {
149 | -webkit-transform: translateY(-10px);
150 | -moz-transform: translateY(-10px);
151 | -ms-transform: translateY(-10px);
152 | -o-transform: translateY(-10px);
153 | transform: translateY(-10px);
154 | }
155 |
156 | /*
157 | ... 'rotating' to rotate a quarter of a second later ...
158 | */
159 | #ing.present .rotating {
160 | -webkit-transform: rotate(-10deg);
161 | -moz-transform: rotate(-10deg);
162 | -ms-transform: rotate(-10deg);
163 | -o-transform: rotate(-10deg);
164 | transform: rotate(-10deg);
165 |
166 | -webkit-transition-delay: 0.25s;
167 | -moz-transition-delay: 0.25s;
168 | -ms-transition-delay: 0.25s;
169 | -o-transition-delay: 0.25s;
170 | transition-delay: 0.25s;
171 | }
172 |
173 | /*
174 | ... and 'scaling' to scale down after another quarter of a second.
175 | */
176 | #ing.present .scaling {
177 | -webkit-transform: scale(0.7);
178 | -moz-transform: scale(0.7);
179 | -ms-transform: scale(0.7);
180 | -o-transform: scale(0.7);
181 | transform: scale(0.7);
182 |
183 | -webkit-transition-delay: 0.5s;
184 | -moz-transition-delay: 0.5s;
185 | -ms-transition-delay: 0.5s;
186 | -o-transition-delay: 0.5s;
187 | transition-delay: 0.5s;
188 | }
189 |
190 | /*
191 | The 'imagination' step is again some boring font-sizing.
192 | */
193 |
194 | #imagination {
195 | width: 600px;
196 | }
197 |
198 | #imagination .imagination {
199 | font-size: 78px;
200 | }
201 |
202 | /*
203 | There is nothing really special about 'use the source, Luke' step, too,
204 | except maybe of the Yoda background.
205 |
206 | As you can see below I've 'hard-coded' it in data URL.
207 | That's not the best way to serve images, but because that's just this one
208 | I decided it will be OK to have it this way.
209 |
210 | Just make sure you don't blindly copy this approach.
211 | */
212 | #source {
213 | width: 700px;
214 | padding-bottom: 300px;
215 |
216 | /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */
217 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==);
218 | background-position: bottom right;
219 | background-repeat: no-repeat;
220 | }
221 |
222 | #source q {
223 | font-size: 60px;
224 | }
225 |
226 | /*
227 | And the "it's in 3D" step again brings some 3D typography - just for fun.
228 |
229 | Because we want to position elements in 3D we set transform-style to
230 | `preserve-3d` on the paragraph.
231 | It is not needed by webkit browsers, but it is in Firefox. It's hard to say
232 | which behaviour is correct as 3D transforms spec is not very clear about it.
233 | */
234 | #its-in-3d p {
235 | -webkit-transform-style: preserve-3d;
236 | -moz-transform-style: preserve-3d; /* Y U need this Firefox?! */
237 | -ms-transform-style: preserve-3d;
238 | -o-transform-style: preserve-3d;
239 | transform-style: preserve-3d;
240 | }
241 |
242 | /*
243 | Below we position each word separately along Z axis and we want it to transition
244 | to default position in 0.5s when the step gets `present` class.
245 |
246 | Quite a simple idea, but lot's of styles and prefixes.
247 | */
248 | #its-in-3d span,
249 | #its-in-3d b {
250 | display: inline-block;
251 | -webkit-transform: translateZ(40px);
252 | -moz-transform: translateZ(40px);
253 | -ms-transform: translateZ(40px);
254 | -o-transform: translateZ(40px);
255 | transform: translateZ(40px);
256 |
257 | -webkit-transition: 0.5s;
258 | -moz-transition: 0.5s;
259 | -ms-transition: 0.5s;
260 | -o-transition: 0.5s;
261 | transition: 0.5s;
262 | }
263 |
264 | #its-in-3d .have {
265 | -webkit-transform: translateZ(-40px);
266 | -moz-transform: translateZ(-40px);
267 | -ms-transform: translateZ(-40px);
268 | -o-transform: translateZ(-40px);
269 | transform: translateZ(-40px);
270 | }
271 |
272 | #its-in-3d .you {
273 | -webkit-transform: translateZ(20px);
274 | -moz-transform: translateZ(20px);
275 | -ms-transform: translateZ(20px);
276 | -o-transform: translateZ(20px);
277 | transform: translateZ(20px);
278 | }
279 |
280 | #its-in-3d .noticed {
281 | -webkit-transform: translateZ(-40px);
282 | -moz-transform: translateZ(-40px);
283 | -ms-transform: translateZ(-40px);
284 | -o-transform: translateZ(-40px);
285 | transform: translateZ(-40px);
286 | }
287 |
288 | #its-in-3d .its {
289 | -webkit-transform: translateZ(60px);
290 | -moz-transform: translateZ(60px);
291 | -ms-transform: translateZ(60px);
292 | -o-transform: translateZ(60px);
293 | transform: translateZ(60px);
294 | }
295 |
296 | #its-in-3d .in {
297 | -webkit-transform: translateZ(-10px);
298 | -moz-transform: translateZ(-10px);
299 | -ms-transform: translateZ(-10px);
300 | -o-transform: translateZ(-10px);
301 | transform: translateZ(-10px);
302 | }
303 |
304 | #its-in-3d .footnote {
305 | font-size: 32px;
306 |
307 | -webkit-transform: translateZ(-10px);
308 | -moz-transform: translateZ(-10px);
309 | -ms-transform: translateZ(-10px);
310 | -o-transform: translateZ(-10px);
311 | transform: translateZ(-10px);
312 | }
313 |
314 | #its-in-3d.present span,
315 | #its-in-3d.present b {
316 | -webkit-transform: translateZ(0px);
317 | -moz-transform: translateZ(0px);
318 | -ms-transform: translateZ(0px);
319 | -o-transform: translateZ(0px);
320 | transform: translateZ(0px);
321 | }
322 |
323 | /*
324 | The last step is an overview.
325 | There is no content in it, so we make sure it's not visible because we want
326 | to be able to click on other steps.
327 |
328 | */
329 | #overview { display: none }
330 |
--------------------------------------------------------------------------------
/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
46 |
47 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | impress.js | presentation tool based on the power of CSS3 transforms and transitions in modern browsers | by Bartek Szopka @bartaz
72 |
73 |
74 |
75 |
76 |
77 |
78 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
117 |
118 |
119 |
122 |
123 |
Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.
124 |
For the best experience please use the latest Chrome, Safari or Firefox browser.
125 |
126 |
127 |
148 |
149 |
150 |
167 |
168 | Aren't you just bored with all those slides-based presentations?
169 |
170 |
171 |
186 |
187 | Don't you think that presentations given in modern browsers shouldn't copy the limits of 'classic' slide decks?
188 |
189 |
190 |
191 | Would you like to impress your audience with stunning visualization of your talk?
192 |
193 |
194 |
204 |
205 | then you should try
206 |
impress.js*
207 | * no rhyme intended
208 |
209 |
210 |
218 |
219 |
It's a presentation tool
220 | inspired by the idea behind prezi.com
221 | and based on the power of CSS3 transforms and transitions in modern browsers.
222 |
223 |
224 |
225 |
visualize your bigthoughts
226 |
227 |
228 |
237 |
238 |
and tiny ideas
239 |
240 |
241 |
261 |
262 |
by positioning, rotating and scaling them on an infinite canvas
385 |
390 |
397 |
398 |
399 |
400 |
--------------------------------------------------------------------------------
/js/impress.js:
--------------------------------------------------------------------------------
1 | /**
2 | * impress.js
3 | *
4 | * impress.js is a presentation tool based on the power of CSS3 transforms and transitions
5 | * in modern browsers and inspired by the idea behind prezi.com.
6 | *
7 | *
8 | * Copyright 2011-2012 Bartek Szopka (@bartaz)
9 | *
10 | * Released under the MIT and GPL Licenses.
11 | *
12 | * ------------------------------------------------
13 | * author: Bartek Szopka
14 | * version: 0.5.3
15 | * url: http://bartaz.github.com/impress.js/
16 | * source: http://github.com/bartaz/impress.js/
17 | */
18 |
19 | /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, latedef:true, newcap:true,
20 | noarg:true, noempty:true, undef:true, strict:true, browser:true */
21 |
22 | // You are one of those who like to know how things work inside?
23 | // Let me show you the cogs that make impress.js run...
24 | (function ( document, window ) {
25 | 'use strict';
26 |
27 | // HELPER FUNCTIONS
28 |
29 | // `pfx` is a function that takes a standard CSS property name as a parameter
30 | // and returns it's prefixed version valid for current browser it runs in.
31 | // The code is heavily inspired by Modernizr http://www.modernizr.com/
32 | var pfx = (function () {
33 |
34 | var style = document.createElement('dummy').style,
35 | prefixes = 'Webkit Moz O ms Khtml'.split(' '),
36 | memory = {};
37 |
38 | return function ( prop ) {
39 | if ( typeof memory[ prop ] === "undefined" ) {
40 |
41 | var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
42 | props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');
43 |
44 | memory[ prop ] = null;
45 | for ( var i in props ) {
46 | if ( style[ props[i] ] !== undefined ) {
47 | memory[ prop ] = props[i];
48 | break;
49 | }
50 | }
51 |
52 | }
53 |
54 | return memory[ prop ];
55 | };
56 |
57 | })();
58 |
59 | // `arraify` takes an array-like object and turns it into real Array
60 | // to make all the Array.prototype goodness available.
61 | var arrayify = function ( a ) {
62 | return [].slice.call( a );
63 | };
64 |
65 | // `css` function applies the styles given in `props` object to the element
66 | // given as `el`. It runs all property names through `pfx` function to make
67 | // sure proper prefixed version of the property is used.
68 | var css = function ( el, props ) {
69 | var key, pkey;
70 | for ( key in props ) {
71 | if ( props.hasOwnProperty(key) ) {
72 | pkey = pfx(key);
73 | if ( pkey !== null ) {
74 | el.style[pkey] = props[key];
75 | }
76 | }
77 | }
78 | return el;
79 | };
80 |
81 | // `toNumber` takes a value given as `numeric` parameter and tries to turn
82 | // it into a number. If it is not possible it returns 0 (or other value
83 | // given as `fallback`).
84 | var toNumber = function (numeric, fallback) {
85 | return isNaN(numeric) ? (fallback || 0) : Number(numeric);
86 | };
87 |
88 | // `byId` returns element with given `id` - you probably have guessed that ;)
89 | var byId = function ( id ) {
90 | return document.getElementById(id);
91 | };
92 |
93 | // `$` returns first element for given CSS `selector` in the `context` of
94 | // the given element or whole document.
95 | var $ = function ( selector, context ) {
96 | context = context || document;
97 | return context.querySelector(selector);
98 | };
99 |
100 | // `$$` return an array of elements for given CSS `selector` in the `context` of
101 | // the given element or whole document.
102 | var $$ = function ( selector, context ) {
103 | context = context || document;
104 | return arrayify( context.querySelectorAll(selector) );
105 | };
106 |
107 | // `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data
108 | // and triggers it on element given as `el`.
109 | var triggerEvent = function (el, eventName, detail) {
110 | var event = document.createEvent("CustomEvent");
111 | event.initCustomEvent(eventName, true, true, detail);
112 | el.dispatchEvent(event);
113 | };
114 |
115 | // `translate` builds a translate transform string for given data.
116 | var translate = function ( t ) {
117 | return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) ";
118 | };
119 |
120 | // `rotate` builds a rotate transform string for given data.
121 | // By default the rotations are in X Y Z order that can be reverted by passing `true`
122 | // as second parameter.
123 | var rotate = function ( r, revert ) {
124 | var rX = " rotateX(" + r.x + "deg) ",
125 | rY = " rotateY(" + r.y + "deg) ",
126 | rZ = " rotateZ(" + r.z + "deg) ";
127 |
128 | return revert ? rZ+rY+rX : rX+rY+rZ;
129 | };
130 |
131 | // `scale` builds a scale transform string for given data.
132 | var scale = function ( s ) {
133 | return " scale(" + s + ") ";
134 | };
135 |
136 | // `perspective` builds a perspective transform string for given data.
137 | var perspective = function ( p ) {
138 | return " perspective(" + p + "px) ";
139 | };
140 |
141 | // `getElementFromHash` returns an element located by id from hash part of
142 | // window location.
143 | var getElementFromHash = function () {
144 | // get id from url # by removing `#` or `#/` from the beginning,
145 | // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
146 | return byId( window.location.hash.replace(/^#\/?/,"") );
147 | };
148 |
149 | // `computeWindowScale` counts the scale factor between window size and size
150 | // defined for the presentation in the config.
151 | var computeWindowScale = function ( config ) {
152 | var hScale = window.innerHeight / config.height,
153 | wScale = window.innerWidth / config.width,
154 | scale = hScale > wScale ? wScale : hScale;
155 |
156 | if (config.maxScale && scale > config.maxScale) {
157 | scale = config.maxScale;
158 | }
159 |
160 | if (config.minScale && scale < config.minScale) {
161 | scale = config.minScale;
162 | }
163 |
164 | return scale;
165 | };
166 |
167 | // CHECK SUPPORT
168 | var body = document.body;
169 |
170 | var ua = navigator.userAgent.toLowerCase();
171 | var impressSupported =
172 | // browser should support CSS 3D transtorms
173 | ( pfx("perspective") !== null ) &&
174 |
175 | // and `classList` and `dataset` APIs
176 | ( body.classList ) &&
177 | ( body.dataset ) &&
178 |
179 | // but some mobile devices need to be blacklisted,
180 | // because their CSS 3D support or hardware is not
181 | // good enough to run impress.js properly, sorry...
182 | ( ua.search(/(iphone)|(ipod)|(android)/) === -1 );
183 |
184 | if (!impressSupported) {
185 | // we can't be sure that `classList` is supported
186 | body.className += " impress-not-supported ";
187 | } else {
188 | body.classList.remove("impress-not-supported");
189 | body.classList.add("impress-supported");
190 | }
191 |
192 | // GLOBALS AND DEFAULTS
193 |
194 | // This is were the root elements of all impress.js instances will be kept.
195 | // Yes, this means you can have more than one instance on a page, but I'm not
196 | // sure if it makes any sense in practice ;)
197 | var roots = {};
198 |
199 | // some default config values.
200 | var defaults = {
201 | width: 1024,
202 | height: 768,
203 | maxScale: 1,
204 | minScale: 0,
205 |
206 | perspective: 1000,
207 |
208 | transitionDuration: 1000
209 | };
210 |
211 | // it's just an empty function ... and a useless comment.
212 | var empty = function () { return false; };
213 |
214 | // IMPRESS.JS API
215 |
216 | // And that's where interesting things will start to happen.
217 | // It's the core `impress` function that returns the impress.js API
218 | // for a presentation based on the element with given id ('impress'
219 | // by default).
220 | var impress = window.impress = function ( rootId ) {
221 |
222 | // If impress.js is not supported by the browser return a dummy API
223 | // it may not be a perfect solution but we return early and avoid
224 | // running code that may use features not implemented in the browser.
225 | if (!impressSupported) {
226 | return {
227 | init: empty,
228 | goto: empty,
229 | prev: empty,
230 | next: empty
231 | };
232 | }
233 |
234 | rootId = rootId || "impress";
235 |
236 | // if given root is already initialized just return the API
237 | if (roots["impress-root-" + rootId]) {
238 | return roots["impress-root-" + rootId];
239 | }
240 |
241 | // data of all presentation steps
242 | var stepsData = {};
243 |
244 | // element of currently active step
245 | var activeStep = null;
246 |
247 | // current state (position, rotation and scale) of the presentation
248 | var currentState = null;
249 |
250 | // array of step elements
251 | var steps = null;
252 |
253 | // configuration options
254 | var config = null;
255 |
256 | // scale factor of the browser window
257 | var windowScale = null;
258 |
259 | // root presentation elements
260 | var root = byId( rootId );
261 | var canvas = document.createElement("div");
262 |
263 | var initialized = false;
264 |
265 | // STEP EVENTS
266 | //
267 | // There are currently two step events triggered by impress.js
268 | // `impress:stepenter` is triggered when the step is shown on the
269 | // screen (the transition from the previous one is finished) and
270 | // `impress:stepleave` is triggered when the step is left (the
271 | // transition to next step just starts).
272 |
273 | // reference to last entered step
274 | var lastEntered = null;
275 |
276 | // `onStepEnter` is called whenever the step element is entered
277 | // but the event is triggered only if the step is different than
278 | // last entered step.
279 | var onStepEnter = function (step) {
280 | if (lastEntered !== step) {
281 | triggerEvent(step, "impress:stepenter");
282 | lastEntered = step;
283 | }
284 | };
285 |
286 | // `onStepLeave` is called whenever the step element is left
287 | // but the event is triggered only if the step is the same as
288 | // last entered step.
289 | var onStepLeave = function (step) {
290 | if (lastEntered === step) {
291 | triggerEvent(step, "impress:stepleave");
292 | lastEntered = null;
293 | }
294 | };
295 |
296 | // `initStep` initializes given step element by reading data from its
297 | // data attributes and setting correct styles.
298 | var initStep = function ( el, idx ) {
299 | var data = el.dataset,
300 | step = {
301 | translate: {
302 | x: toNumber(data.x),
303 | y: toNumber(data.y),
304 | z: toNumber(data.z)
305 | },
306 | rotate: {
307 | x: toNumber(data.rotateX),
308 | y: toNumber(data.rotateY),
309 | z: toNumber(data.rotateZ || data.rotate)
310 | },
311 | scale: toNumber(data.scale, 1),
312 | el: el
313 | };
314 |
315 | if ( !el.id ) {
316 | el.id = "step-" + (idx + 1);
317 | }
318 |
319 | stepsData["impress-" + el.id] = step;
320 |
321 | css(el, {
322 | position: "absolute",
323 | transform: "translate(-50%,-50%)" +
324 | translate(step.translate) +
325 | rotate(step.rotate) +
326 | scale(step.scale),
327 | transformStyle: "preserve-3d"
328 | });
329 | };
330 |
331 | // `init` API function that initializes (and runs) the presentation.
332 | var init = function () {
333 | if (initialized) { return; }
334 |
335 | // First we set up the viewport for mobile devices.
336 | // For some reason iPad goes nuts when it is not done properly.
337 | var meta = $("meta[name='viewport']") || document.createElement("meta");
338 | meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no";
339 | if (meta.parentNode !== document.head) {
340 | meta.name = 'viewport';
341 | document.head.appendChild(meta);
342 | }
343 |
344 | // initialize configuration object
345 | var rootData = root.dataset;
346 | config = {
347 | width: toNumber( rootData.width, defaults.width ),
348 | height: toNumber( rootData.height, defaults.height ),
349 | maxScale: toNumber( rootData.maxScale, defaults.maxScale ),
350 | minScale: toNumber( rootData.minScale, defaults.minScale ),
351 | perspective: toNumber( rootData.perspective, defaults.perspective ),
352 | transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration )
353 | };
354 |
355 | windowScale = computeWindowScale( config );
356 |
357 | // wrap steps with "canvas" element
358 | arrayify( root.childNodes ).forEach(function ( el ) {
359 | canvas.appendChild( el );
360 | });
361 | root.appendChild(canvas);
362 |
363 | // set initial styles
364 | document.documentElement.style.height = "100%";
365 |
366 | css(body, {
367 | height: "100%",
368 | overflow: "hidden"
369 | });
370 |
371 | var rootStyles = {
372 | position: "absolute",
373 | transformOrigin: "top left",
374 | transition: "all 0s ease-in-out",
375 | transformStyle: "preserve-3d"
376 | };
377 |
378 | css(root, rootStyles);
379 | css(root, {
380 | top: "50%",
381 | left: "50%",
382 | transform: perspective( config.perspective/windowScale ) + scale( windowScale )
383 | });
384 | css(canvas, rootStyles);
385 |
386 | body.classList.remove("impress-disabled");
387 | body.classList.add("impress-enabled");
388 |
389 | // get and init steps
390 | steps = $$(".step", root);
391 | steps.forEach( initStep );
392 |
393 | // set a default initial state of the canvas
394 | currentState = {
395 | translate: { x: 0, y: 0, z: 0 },
396 | rotate: { x: 0, y: 0, z: 0 },
397 | scale: 1
398 | };
399 |
400 | initialized = true;
401 |
402 | triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] });
403 | };
404 |
405 | // `getStep` is a helper function that returns a step element defined by parameter.
406 | // If a number is given, step with index given by the number is returned, if a string
407 | // is given step element with such id is returned, if DOM element is given it is returned
408 | // if it is a correct step element.
409 | var getStep = function ( step ) {
410 | if (typeof step === "number") {
411 | step = step < 0 ? steps[ steps.length + step] : steps[ step ];
412 | } else if (typeof step === "string") {
413 | step = byId(step);
414 | }
415 | return (step && step.id && stepsData["impress-" + step.id]) ? step : null;
416 | };
417 |
418 | // used to reset timeout for `impress:stepenter` event
419 | var stepEnterTimeout = null;
420 |
421 | // `goto` API function that moves to step given with `el` parameter (by index, id or element),
422 | // with a transition `duration` optionally given as second parameter.
423 | var goto = function ( el, duration ) {
424 |
425 | if ( !initialized || !(el = getStep(el)) ) {
426 | // presentation not initialized or given element is not a step
427 | return false;
428 | }
429 |
430 | // Sometimes it's possible to trigger focus on first link with some keyboard action.
431 | // Browser in such a case tries to scroll the page to make this element visible
432 | // (even that body overflow is set to hidden) and it breaks our careful positioning.
433 | //
434 | // So, as a lousy (and lazy) workaround we will make the page scroll back to the top
435 | // whenever slide is selected
436 | //
437 | // If you are reading this and know any better way to handle it, I'll be glad to hear about it!
438 | window.scrollTo(0, 0);
439 |
440 | var step = stepsData["impress-" + el.id];
441 |
442 | if ( activeStep ) {
443 | activeStep.classList.remove("active");
444 | body.classList.remove("impress-on-" + activeStep.id);
445 | }
446 | el.classList.add("active");
447 |
448 | body.classList.add("impress-on-" + el.id);
449 |
450 | // compute target state of the canvas based on given step
451 | var target = {
452 | rotate: {
453 | x: -step.rotate.x,
454 | y: -step.rotate.y,
455 | z: -step.rotate.z
456 | },
457 | translate: {
458 | x: -step.translate.x,
459 | y: -step.translate.y,
460 | z: -step.translate.z
461 | },
462 | scale: 1 / step.scale
463 | };
464 |
465 | // Check if the transition is zooming in or not.
466 | //
467 | // This information is used to alter the transition style:
468 | // when we are zooming in - we start with move and rotate transition
469 | // and the scaling is delayed, but when we are zooming out we start
470 | // with scaling down and move and rotation are delayed.
471 | var zoomin = target.scale >= currentState.scale;
472 |
473 | duration = toNumber(duration, config.transitionDuration);
474 | var delay = (duration / 2);
475 |
476 | // if the same step is re-selected, force computing window scaling,
477 | // because it is likely to be caused by window resize
478 | if (el === activeStep) {
479 | windowScale = computeWindowScale(config);
480 | }
481 |
482 | var targetScale = target.scale * windowScale;
483 |
484 | // trigger leave of currently active element (if it's not the same step again)
485 | if (activeStep && activeStep !== el) {
486 | onStepLeave(activeStep);
487 | }
488 |
489 | // Now we alter transforms of `root` and `canvas` to trigger transitions.
490 | //
491 | // And here is why there are two elements: `root` and `canvas` - they are
492 | // being animated separately:
493 | // `root` is used for scaling and `canvas` for translate and rotations.
494 | // Transitions on them are triggered with different delays (to make
495 | // visually nice and 'natural' looking transitions), so we need to know
496 | // that both of them are finished.
497 | css(root, {
498 | // to keep the perspective look similar for different scales
499 | // we need to 'scale' the perspective, too
500 | transform: perspective( config.perspective / targetScale ) + scale( targetScale ),
501 | transitionDuration: duration + "ms",
502 | transitionDelay: (zoomin ? delay : 0) + "ms"
503 | });
504 |
505 | css(canvas, {
506 | transform: rotate(target.rotate, true) + translate(target.translate),
507 | transitionDuration: duration + "ms",
508 | transitionDelay: (zoomin ? 0 : delay) + "ms"
509 | });
510 |
511 | // Here is a tricky part...
512 | //
513 | // If there is no change in scale or no change in rotation and translation, it means there was actually
514 | // no delay - because there was no transition on `root` or `canvas` elements.
515 | // We want to trigger `impress:stepenter` event in the correct moment, so here we compare the current
516 | // and target values to check if delay should be taken into account.
517 | //
518 | // I know that this `if` statement looks scary, but it's pretty simple when you know what is going on
519 | // - it's simply comparing all the values.
520 | if ( currentState.scale === target.scale ||
521 | (currentState.rotate.x === target.rotate.x && currentState.rotate.y === target.rotate.y &&
522 | currentState.rotate.z === target.rotate.z && currentState.translate.x === target.translate.x &&
523 | currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) {
524 | delay = 0;
525 | }
526 |
527 | // store current state
528 | currentState = target;
529 | activeStep = el;
530 |
531 | // And here is where we trigger `impress:stepenter` event.
532 | // We simply set up a timeout to fire it taking transition duration (and possible delay) into account.
533 | //
534 | // I really wanted to make it in more elegant way. The `transitionend` event seemed to be the best way
535 | // to do it, but the fact that I'm using transitions on two separate elements and that the `transitionend`
536 | // event is only triggered when there was a transition (change in the values) caused some bugs and
537 | // made the code really complicated, cause I had to handle all the conditions separately. And it still
538 | // needed a `setTimeout` fallback for the situations when there is no transition at all.
539 | // So I decided that I'd rather make the code simpler than use shiny new `transitionend`.
540 | //
541 | // If you want learn something interesting and see how it was done with `transitionend` go back to
542 | // version 0.5.2 of impress.js: http://github.com/bartaz/impress.js/blob/0.5.2/js/impress.js
543 | window.clearTimeout(stepEnterTimeout);
544 | stepEnterTimeout = window.setTimeout(function() {
545 | onStepEnter(activeStep);
546 | }, duration + delay);
547 |
548 | return el;
549 | };
550 |
551 | // `prev` API function goes to previous step (in document order)
552 | var prev = function () {
553 | var prev = steps.indexOf( activeStep ) - 1;
554 | prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ];
555 |
556 | return goto(prev);
557 | };
558 |
559 | // `next` API function goes to next step (in document order)
560 | var next = function () {
561 | var next = steps.indexOf( activeStep ) + 1;
562 | next = next < steps.length ? steps[ next ] : steps[ 0 ];
563 |
564 | return goto(next);
565 | };
566 |
567 | // Adding some useful classes to step elements.
568 | //
569 | // All the steps that have not been shown yet are given `future` class.
570 | // When the step is entered the `future` class is removed and the `present`
571 | // class is given. When the step is left `present` class is replaced with
572 | // `past` class.
573 | //
574 | // So every step element is always in one of three possible states:
575 | // `future`, `present` and `past`.
576 | //
577 | // There classes can be used in CSS to style different types of steps.
578 | // For example the `present` class can be used to trigger some custom
579 | // animations when step is shown.
580 | root.addEventListener("impress:init", function(){
581 | // STEP CLASSES
582 | steps.forEach(function (step) {
583 | step.classList.add("future");
584 | });
585 |
586 | root.addEventListener("impress:stepenter", function (event) {
587 | event.target.classList.remove("past");
588 | event.target.classList.remove("future");
589 | event.target.classList.add("present");
590 | }, false);
591 |
592 | root.addEventListener("impress:stepleave", function (event) {
593 | event.target.classList.remove("present");
594 | event.target.classList.add("past");
595 | }, false);
596 |
597 | }, false);
598 |
599 | // Adding hash change support.
600 | root.addEventListener("impress:init", function(){
601 |
602 | // last hash detected
603 | var lastHash = "";
604 |
605 | // `#/step-id` is used instead of `#step-id` to prevent default browser
606 | // scrolling to element in hash.
607 | //
608 | // And it has to be set after animation finishes, because in Chrome it
609 | // makes transtion laggy.
610 | // BUG: http://code.google.com/p/chromium/issues/detail?id=62820
611 | root.addEventListener("impress:stepenter", function (event) {
612 | window.location.hash = lastHash = "#/" + event.target.id;
613 | }, false);
614 |
615 | window.addEventListener("hashchange", function () {
616 | // When the step is entered hash in the location is updated
617 | // (just few lines above from here), so the hash change is
618 | // triggered and we would call `goto` again on the same element.
619 | //
620 | // To avoid this we store last entered hash and compare.
621 | if (window.location.hash !== lastHash) {
622 | goto( getElementFromHash() );
623 | }
624 | }, false);
625 |
626 | // START
627 | // by selecting step defined in url or first step of the presentation
628 | goto(getElementFromHash() || steps[0], 0);
629 | }, false);
630 |
631 | body.classList.add("impress-disabled");
632 |
633 | // store and return API for given impress.js root element
634 | return (roots[ "impress-root-" + rootId ] = {
635 | init: init,
636 | goto: goto,
637 | next: next,
638 | prev: prev
639 | });
640 |
641 | };
642 |
643 | // flag that can be used in JS to check if browser have passed the support test
644 | impress.supported = impressSupported;
645 |
646 | })(document, window);
647 |
648 | // NAVIGATION EVENTS
649 |
650 | // As you can see this part is separate from the impress.js core code.
651 | // It's because these navigation actions only need what impress.js provides with
652 | // its simple API.
653 | //
654 | // In future I think about moving it to make them optional, move to separate files
655 | // and treat more like a 'plugins'.
656 | (function ( document, window ) {
657 | 'use strict';
658 |
659 | // throttling function calls, by Remy Sharp
660 | // http://remysharp.com/2010/07/21/throttling-function-calls/
661 | var throttle = function (fn, delay) {
662 | var timer = null;
663 | return function () {
664 | var context = this, args = arguments;
665 | clearTimeout(timer);
666 | timer = setTimeout(function () {
667 | fn.apply(context, args);
668 | }, delay);
669 | };
670 | };
671 |
672 | // wait for impress.js to be initialized
673 | document.addEventListener("impress:init", function (event) {
674 | // Getting API from event data.
675 | // So you don't event need to know what is the id of the root element
676 | // or anything. `impress:init` event data gives you everything you
677 | // need to control the presentation that was just initialized.
678 | var api = event.detail.api;
679 |
680 | // KEYBOARD NAVIGATION HANDLERS
681 |
682 | // Prevent default keydown action when one of supported key is pressed.
683 | document.addEventListener("keydown", function ( event ) {
684 | if ( event.keyCode === 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) {
685 | event.preventDefault();
686 | }
687 | }, false);
688 |
689 | // Trigger impress action (next or prev) on keyup.
690 |
691 | // Supported keys are:
692 | // [space] - quite common in presentation software to move forward
693 | // [up] [right] / [down] [left] - again common and natural addition,
694 | // [pgdown] / [pgup] - often triggered by remote controllers,
695 | // [tab] - this one is quite controversial, but the reason it ended up on
696 | // this list is quite an interesting story... Remember that strange part
697 | // in the impress.js code where window is scrolled to 0,0 on every presentation
698 | // step, because sometimes browser scrolls viewport because of the focused element?
699 | // Well, the [tab] key by default navigates around focusable elements, so clicking
700 | // it very often caused scrolling to focused element and breaking impress.js
701 | // positioning. I didn't want to just prevent this default action, so I used [tab]
702 | // as another way to moving to next step... And yes, I know that for the sake of
703 | // consistency I should add [shift+tab] as opposite action...
704 | document.addEventListener("keyup", function ( event ) {
705 | if ( event.keyCode === 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) {
706 | switch( event.keyCode ) {
707 | case 33: // pg up
708 | case 37: // left
709 | case 38: // up
710 | api.prev();
711 | break;
712 | case 9: // tab
713 | case 32: // space
714 | case 34: // pg down
715 | case 39: // right
716 | case 40: // down
717 | api.next();
718 | break;
719 | }
720 |
721 | event.preventDefault();
722 | }
723 | }, false);
724 |
725 | // delegated handler for clicking on the links to presentation steps
726 | document.addEventListener("click", function ( event ) {
727 | // event delegation with "bubbling"
728 | // check if event target (or any of its parents is a link)
729 | var target = event.target;
730 | while ( (target.tagName !== "A") &&
731 | (target !== document.documentElement) ) {
732 | target = target.parentNode;
733 | }
734 |
735 | if ( target.tagName === "A" ) {
736 | var href = target.getAttribute("href");
737 |
738 | // if it's a link to presentation step, target this step
739 | if ( href && href[0] === '#' ) {
740 | target = document.getElementById( href.slice(1) );
741 | }
742 | }
743 |
744 | if ( api.goto(target) ) {
745 | event.stopImmediatePropagation();
746 | event.preventDefault();
747 | }
748 | }, false);
749 |
750 | // delegated handler for clicking on step elements
751 | document.addEventListener("click", function ( event ) {
752 | var target = event.target;
753 | // find closest step element that is not active
754 | while ( !(target.classList.contains("step") && !target.classList.contains("active")) &&
755 | (target !== document.documentElement) ) {
756 | target = target.parentNode;
757 | }
758 |
759 | if ( api.goto(target) ) {
760 | event.preventDefault();
761 | }
762 | }, false);
763 |
764 | // touch handler to detect taps on the left and right side of the screen
765 | // based on awesome work of @hakimel: https://github.com/hakimel/reveal.js
766 | document.addEventListener("touchstart", function ( event ) {
767 | if (event.touches.length === 1) {
768 | var x = event.touches[0].clientX,
769 | width = window.innerWidth * 0.3,
770 | result = null;
771 |
772 | if ( x < width ) {
773 | result = api.prev();
774 | } else if ( x > window.innerWidth - width ) {
775 | result = api.next();
776 | }
777 |
778 | if (result) {
779 | event.preventDefault();
780 | }
781 | }
782 | }, false);
783 |
784 | // rescale presentation when window is resized
785 | window.addEventListener("resize", throttle(function () {
786 | // force going to active step again, to trigger rescaling
787 | api.goto( document.querySelector(".step.active"), 500 );
788 | }, 250), false);
789 |
790 | }, false);
791 |
792 | })(document, window);
793 |
794 | // THAT'S ALL FOLKS!
795 | //
796 | // Thanks for reading it all.
797 | // Or thanks for scrolling down and reading the last part.
798 | //
799 | // I've learnt a lot when building impress.js and I hope this code and comments
800 | // will help somebody learn at least some part of it.
801 |
--------------------------------------------------------------------------------