├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── code-box ├── code-box.html └── code-example.html ├── css ├── print │ ├── paper.css │ └── pdf.css ├── reveal.css ├── reveal.min.css └── theme │ ├── README.md │ ├── firebase.css │ ├── firebase.css.map │ ├── font │ └── Gotham-Thin.otf │ ├── source │ └── firebase.scss │ └── template │ ├── mixins.scss │ ├── settings.scss │ └── theme.scss ├── full-stack-clients.psd ├── full-stack.png ├── full-stack.psd ├── images ├── HTML5.png ├── Realtime-sync-anim.gif ├── angularjs.png ├── api.png ├── auth.png ├── backbonejs.png ├── backend-reqs (1).gif ├── backend-reqs.gif ├── clients.png ├── clients.psd ├── data-box.png ├── data-box.psd ├── database.png ├── docs-site.png ├── emberjs.png ├── fb-arch.png ├── firebase-logo.png ├── full-stack-clients.png ├── full-stack.png ├── gdocs4.gif ├── hosting-box.png ├── iphone.png ├── laptop.png ├── login-box.png ├── nexus.png ├── offline.png ├── platforms.png ├── react.png ├── realtime-api.png ├── security-box.png ├── server.png └── simple-api.png ├── index.html ├── js ├── .gitignore └── reveal.js ├── lib ├── css │ └── zenburn.css ├── font │ ├── league_gothic-webfont.eot │ ├── league_gothic-webfont.svg │ ├── league_gothic-webfont.ttf │ ├── league_gothic-webfont.woff │ └── league_gothic_license └── js │ ├── classList.js │ ├── head.min.js │ └── html5shiv.js ├── package.json ├── plugin ├── highlight │ └── highlight.js ├── leap │ └── leap.js ├── markdown │ ├── example.html │ ├── example.md │ ├── markdown.js │ └── marked.js ├── math │ └── math.js ├── multiplex │ ├── client.js │ ├── index.js │ └── master.js ├── notes-server │ ├── client.js │ ├── index.js │ └── notes.html ├── notes │ ├── notes.html │ └── notes.js ├── postmessage │ ├── example.html │ └── postmessage.js ├── print-pdf │ └── print-pdf.js ├── remotes │ └── remotes.js ├── search │ └── search.js └── zoom-js │ └── zoom.js └── test ├── examples ├── assets │ ├── image1.png │ └── image2.png ├── barebones.html ├── embedded-media.html ├── math.html └── slide-backgrounds.html ├── qunit-1.12.0.css ├── qunit-1.12.0.js ├── test-markdown-element-attributes.html ├── test-markdown-element-attributes.js ├── test-markdown-slide-attributes.html ├── test-markdown-slide-attributes.js ├── test-markdown.html ├── test-markdown.js ├── test.html └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .svn 3 | log/*.log 4 | tmp/** 5 | node_modules/ 6 | .sass-cache 7 | firebase.json 8 | bower_components 9 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* global module:false */ 2 | module.exports = function(grunt) { 3 | var port = grunt.option('port') || 8000; 4 | // Project configuration 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON('package.json'), 7 | meta: { 8 | banner: 9 | '/*!\n' + 10 | ' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' + 11 | ' * http://lab.hakim.se/reveal-js\n' + 12 | ' * MIT licensed\n' + 13 | ' *\n' + 14 | ' * Copyright (C) 2014 Hakim El Hattab, http://hakim.se\n' + 15 | ' */' 16 | }, 17 | 18 | qunit: { 19 | files: [ 'test/*.html' ] 20 | }, 21 | 22 | uglify: { 23 | options: { 24 | banner: '<%= meta.banner %>\n' 25 | }, 26 | build: { 27 | src: 'js/reveal.js', 28 | dest: 'js/reveal.min.js' 29 | } 30 | }, 31 | 32 | cssmin: { 33 | compress: { 34 | files: { 35 | 'css/reveal.min.css': [ 'css/reveal.css' ] 36 | } 37 | } 38 | }, 39 | 40 | sass: { 41 | main: { 42 | files: { 43 | 'css/theme/firebase.css': 'css/theme/source/firebase.scss' 44 | } 45 | } 46 | }, 47 | 48 | jshint: { 49 | options: { 50 | curly: false, 51 | eqeqeq: true, 52 | immed: true, 53 | latedef: true, 54 | newcap: true, 55 | noarg: true, 56 | sub: true, 57 | undef: true, 58 | eqnull: true, 59 | browser: true, 60 | expr: true, 61 | globals: { 62 | head: false, 63 | module: false, 64 | console: false, 65 | unescape: false 66 | } 67 | }, 68 | files: [ 'Gruntfile.js', 'js/reveal.js' ] 69 | }, 70 | 71 | connect: { 72 | server: { 73 | options: { 74 | port: port, 75 | base: '.' 76 | } 77 | } 78 | }, 79 | 80 | zip: { 81 | 'reveal-js-presentation.zip': [ 82 | 'index.html', 83 | 'css/**', 84 | 'js/**', 85 | 'lib/**', 86 | 'images/**', 87 | 'plugin/**' 88 | ] 89 | }, 90 | 91 | watch: { 92 | main: { 93 | files: [ 'Gruntfile.js', 'js/reveal.js', 'css/reveal.css' ], 94 | tasks: 'default' 95 | }, 96 | theme: { 97 | files: [ 'css/theme/source/*.scss', 'css/theme/template/*.scss' ], 98 | tasks: 'themes' 99 | } 100 | } 101 | 102 | }); 103 | 104 | // Dependencies 105 | grunt.loadNpmTasks( 'grunt-contrib-qunit' ); 106 | grunt.loadNpmTasks( 'grunt-contrib-jshint' ); 107 | grunt.loadNpmTasks( 'grunt-contrib-cssmin' ); 108 | grunt.loadNpmTasks( 'grunt-contrib-uglify' ); 109 | grunt.loadNpmTasks( 'grunt-contrib-watch' ); 110 | grunt.loadNpmTasks( 'grunt-contrib-sass' ); 111 | grunt.loadNpmTasks( 'grunt-contrib-connect' ); 112 | grunt.loadNpmTasks( 'grunt-zip' ); 113 | 114 | // Default task 115 | grunt.registerTask( 'default', [ 'jshint', 'cssmin', 'uglify', 'qunit' ] ); 116 | 117 | // Theme task 118 | grunt.registerTask( 'themes', [ 'sass' ] ); 119 | 120 | // Package presentation to archive 121 | grunt.registerTask( 'package', [ 'default', 'zip' ] ); 122 | 123 | // Serve presentation locally 124 | grunt.registerTask( 'serve', [ 'connect', 'watch' ] ); 125 | 126 | // Run tests 127 | grunt.registerTask( 'test', [ 'jshint', 'qunit' ] ); 128 | 129 | }; 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Hakim El Hattab, http://hakim.se 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Status: Archived 2 | This repository has been archived and is no longer maintained. 3 | 4 | ![status: inactive](https://img.shields.io/badge/status-inactive-red.svg) 5 | 6 | # Intro to Firebase 7 | 8 | This slide deck can be customized to give an introductory talk about Firebase. It is powered by [reveal.js](http://lab.hakim.se/reveal-js), a wonderful and powerful framework for creating HTML presentations. 9 | 10 | ### [Boilerplate Site - https://intro-to-firebase.firebaseapp.com/](https://intro-to-firebase.firebaseapp.com/) 11 | 12 | ## Presentations 13 | 14 | Here are some past presentations made with this desk: 15 | 16 | | Presenter | Event | Date | Contact | 17 | | --------- | ----- | ---- | ------- | 18 | | Mike Koss | [Seattle GDG](https://plus.sandbox.google.com/events/cp5b162fccbtrk4l3ah2blb5je8) | April 20, 2015 | koss@firebase.com | 19 | | Jacob Wenger | GDG Leads Summit | May 27, 2015 | jacob@firebase.com | 20 | 21 | If you end up using this deck, please let us know so we can share the presentation here! 22 | 23 | ## Setup 24 | 25 | This repo uses npm and Grunt to install dependencies, build the deck, run tests, and run a local web 26 | server for the content. To get started, run the following commands: 27 | 28 | ```bash 29 | $ git clone git@github.com:firebase/firebase-intro-presentation.git 30 | $ cd firebase-intro-presentation # go to the firebase-intro directory 31 | $ npm install -g grunt-cli # globally install grunt task runner 32 | $ npm install # install local npm build / test dependencies 33 | $ bower install # uses bower for polymer dependency 34 | ``` 35 | 36 | To build the slide deck and run the test suite: 37 | 38 | ```bash 39 | $ grunt 40 | ``` 41 | 42 | To open the slide deck using a local web server: 43 | 44 | ```bash 45 | $ grunt serve 46 | ``` 47 | 48 | ## Deploy 49 | 50 | Ideally, you'll want to run this on a public website for viewers to see after the presentation. You can use [Firebase Hosting](https://www.firebase.com/docs/hosting/) to deploy this presentation for free! 51 | 52 | You can then view the slide deck at [http://localhost:8000](http://localhost:8000). 53 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Firebase-Deck", 3 | "version": "0.0.5", 4 | "homepage": "https://github.com/davideast/firebase-intro", 5 | "authors": [ 6 | "David East " 7 | ], 8 | "description": "An introduction to Firebase Deck", 9 | "main": "index.html", 10 | "keywords": [ 11 | "Firebase" 12 | ], 13 | "license": "MIT", 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests" 20 | ], 21 | "dependencies": { 22 | "polymer": "Polymer/polymer#^1.0.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /code-box/code-box.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | 22 | 51 | -------------------------------------------------------------------------------- /code-box/code-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 38 | -------------------------------------------------------------------------------- /css/print/paper.css: -------------------------------------------------------------------------------- 1 | /* Default Print Stylesheet Template 2 | by Rob Glazebrook of CSSnewbie.com 3 | Last Updated: June 4, 2008 4 | 5 | Feel free (nay, compelled) to edit, append, and 6 | manipulate this file as you see fit. */ 7 | 8 | 9 | /* SECTION 1: Set default width, margin, float, and 10 | background. This prevents elements from extending 11 | beyond the edge of the printed page, and prevents 12 | unnecessary background images from printing */ 13 | body { 14 | background: #fff; 15 | font-size: 13pt; 16 | width: auto; 17 | height: auto; 18 | border: 0; 19 | margin: 0 5%; 20 | padding: 0; 21 | float: none !important; 22 | overflow: visible; 23 | } 24 | html { 25 | background: #fff; 26 | width: auto; 27 | height: auto; 28 | overflow: visible; 29 | } 30 | 31 | /* SECTION 2: Remove any elements not needed in print. 32 | This would include navigation, ads, sidebars, etc. */ 33 | .nestedarrow, 34 | .controls, 35 | .reveal .progress, 36 | .reveal.overview, 37 | .fork-reveal, 38 | .share-reveal, 39 | .state-background { 40 | display: none !important; 41 | } 42 | 43 | /* SECTION 3: Set body font face, size, and color. 44 | Consider using a serif font for readability. */ 45 | body, p, td, li, div, a { 46 | font-size: 16pt!important; 47 | font-family: Georgia, "Times New Roman", Times, serif !important; 48 | color: #000; 49 | } 50 | 51 | /* SECTION 4: Set heading font face, sizes, and color. 52 | Differentiate your headings from your body text. 53 | Perhaps use a large sans-serif for distinction. */ 54 | h1,h2,h3,h4,h5,h6 { 55 | color: #000!important; 56 | height: auto; 57 | line-height: normal; 58 | font-family: Georgia, "Times New Roman", Times, serif !important; 59 | text-shadow: 0 0 0 #000 !important; 60 | text-align: left; 61 | letter-spacing: normal; 62 | } 63 | /* Need to reduce the size of the fonts for printing */ 64 | h1 { font-size: 26pt !important; } 65 | h2 { font-size: 22pt !important; } 66 | h3 { font-size: 20pt !important; } 67 | h4 { font-size: 20pt !important; font-variant: small-caps; } 68 | h5 { font-size: 19pt !important; } 69 | h6 { font-size: 18pt !important; font-style: italic; } 70 | 71 | /* SECTION 5: Make hyperlinks more usable. 72 | Ensure links are underlined, and consider appending 73 | the URL to the end of the link for usability. */ 74 | a:link, 75 | a:visited { 76 | color: #000 !important; 77 | font-weight: bold; 78 | text-decoration: underline; 79 | } 80 | /* 81 | .reveal a:link:after, 82 | .reveal a:visited:after { 83 | content: " (" attr(href) ") "; 84 | color: #222 !important; 85 | font-size: 90%; 86 | } 87 | */ 88 | 89 | 90 | /* SECTION 6: more reveal.js specific additions by @skypanther */ 91 | ul, ol, div, p { 92 | visibility: visible; 93 | position: static; 94 | width: auto; 95 | height: auto; 96 | display: block; 97 | overflow: visible; 98 | margin: auto; 99 | text-align: left !important; 100 | } 101 | .reveal .slides { 102 | position: static; 103 | width: auto; 104 | height: auto; 105 | 106 | left: auto; 107 | top: auto; 108 | margin-left: auto; 109 | margin-top: auto; 110 | padding: auto; 111 | 112 | overflow: visible; 113 | display: block; 114 | 115 | text-align: center; 116 | -webkit-perspective: none; 117 | -moz-perspective: none; 118 | -ms-perspective: none; 119 | perspective: none; 120 | 121 | -webkit-perspective-origin: 50% 50%; /* there isn't a none/auto value but 50-50 is the default */ 122 | -moz-perspective-origin: 50% 50%; 123 | -ms-perspective-origin: 50% 50%; 124 | perspective-origin: 50% 50%; 125 | } 126 | .reveal .slides>section, 127 | .reveal .slides>section>section { 128 | 129 | visibility: visible !important; 130 | position: static !important; 131 | width: 90% !important; 132 | height: auto !important; 133 | display: block !important; 134 | overflow: visible !important; 135 | 136 | left: 0% !important; 137 | top: 0% !important; 138 | margin-left: 0px !important; 139 | margin-top: 0px !important; 140 | padding: 20px 0px !important; 141 | 142 | opacity: 1 !important; 143 | 144 | -webkit-transform-style: flat !important; 145 | -moz-transform-style: flat !important; 146 | -ms-transform-style: flat !important; 147 | transform-style: flat !important; 148 | 149 | -webkit-transform: none !important; 150 | -moz-transform: none !important; 151 | -ms-transform: none !important; 152 | transform: none !important; 153 | } 154 | .reveal section { 155 | page-break-after: always !important; 156 | display: block !important; 157 | } 158 | .reveal section .fragment { 159 | opacity: 1 !important; 160 | visibility: visible !important; 161 | 162 | -webkit-transform: none !important; 163 | -moz-transform: none !important; 164 | -ms-transform: none !important; 165 | transform: none !important; 166 | } 167 | .reveal section:last-of-type { 168 | page-break-after: avoid !important; 169 | } 170 | .reveal section img { 171 | display: block; 172 | margin: 15px 0px; 173 | background: rgba(255,255,255,1); 174 | border: 1px solid #666; 175 | box-shadow: none; 176 | } -------------------------------------------------------------------------------- /css/print/pdf.css: -------------------------------------------------------------------------------- 1 | /* Default Print Stylesheet Template 2 | by Rob Glazebrook of CSSnewbie.com 3 | Last Updated: June 4, 2008 4 | 5 | Feel free (nay, compelled) to edit, append, and 6 | manipulate this file as you see fit. */ 7 | 8 | 9 | /* SECTION 1: Set default width, margin, float, and 10 | background. This prevents elements from extending 11 | beyond the edge of the printed page, and prevents 12 | unnecessary background images from printing */ 13 | 14 | * { 15 | -webkit-print-color-adjust: exact; 16 | } 17 | 18 | body { 19 | font-size: 18pt; 20 | width: 297mm; 21 | height: 229mm; 22 | margin: 0 auto !important; 23 | border: 0; 24 | padding: 0; 25 | float: none !important; 26 | overflow: visible; 27 | } 28 | 29 | html { 30 | width: 100%; 31 | height: 100%; 32 | overflow: visible; 33 | } 34 | 35 | @page { 36 | size: letter landscape; 37 | margin: 0; 38 | } 39 | 40 | /* SECTION 2: Remove any elements not needed in print. 41 | This would include navigation, ads, sidebars, etc. */ 42 | .nestedarrow, 43 | .controls, 44 | .reveal .progress, 45 | .reveal.overview, 46 | .fork-reveal, 47 | .share-reveal, 48 | .state-background { 49 | display: none !important; 50 | } 51 | 52 | /* SECTION 3: Set body font face, size, and color. 53 | Consider using a serif font for readability. */ 54 | body, p, td, li, div { 55 | font-size: 18pt; 56 | } 57 | 58 | /* SECTION 4: Set heading font face, sizes, and color. 59 | Differentiate your headings from your body text. 60 | Perhaps use a large sans-serif for distinction. */ 61 | h1,h2,h3,h4,h5,h6 { 62 | text-shadow: 0 0 0 #000 !important; 63 | } 64 | 65 | /* SECTION 5: Make hyperlinks more usable. 66 | Ensure links are underlined, and consider appending 67 | the URL to the end of the link for usability. */ 68 | a:link, 69 | a:visited { 70 | font-weight: normal; 71 | text-decoration: underline; 72 | } 73 | 74 | .reveal pre code { 75 | overflow: hidden !important; 76 | font-family: monospace !important; 77 | } 78 | 79 | 80 | /* SECTION 6: more reveal.js specific additions by @skypanther */ 81 | ul, ol, div, p { 82 | visibility: visible; 83 | position: static; 84 | width: auto; 85 | height: auto; 86 | display: block; 87 | overflow: visible; 88 | margin: auto; 89 | } 90 | .reveal { 91 | width: auto !important; 92 | height: auto !important; 93 | overflow: hidden !important; 94 | } 95 | .reveal .slides { 96 | position: static; 97 | width: 100%; 98 | height: auto; 99 | 100 | left: auto; 101 | top: auto; 102 | margin: 0 !important; 103 | padding: 0 !important; 104 | 105 | overflow: visible; 106 | display: block; 107 | 108 | text-align: center; 109 | 110 | -webkit-perspective: none; 111 | -moz-perspective: none; 112 | -ms-perspective: none; 113 | perspective: none; 114 | 115 | -webkit-perspective-origin: 50% 50%; /* there isn't a none/auto value but 50-50 is the default */ 116 | -moz-perspective-origin: 50% 50%; 117 | -ms-perspective-origin: 50% 50%; 118 | perspective-origin: 50% 50%; 119 | } 120 | .reveal .slides section { 121 | 122 | page-break-after: always !important; 123 | 124 | visibility: visible !important; 125 | position: relative !important; 126 | width: 100% !important; 127 | height: 229mm !important; 128 | min-height: 229mm !important; 129 | display: block !important; 130 | overflow: hidden !important; 131 | 132 | left: 0 !important; 133 | top: 0 !important; 134 | margin: 0 !important; 135 | padding: 2cm 2cm 0 2cm !important; 136 | box-sizing: border-box !important; 137 | 138 | opacity: 1 !important; 139 | 140 | -webkit-transform-style: flat !important; 141 | -moz-transform-style: flat !important; 142 | -ms-transform-style: flat !important; 143 | transform-style: flat !important; 144 | 145 | -webkit-transform: none !important; 146 | -moz-transform: none !important; 147 | -ms-transform: none !important; 148 | transform: none !important; 149 | } 150 | .reveal section.stack { 151 | margin: 0 !important; 152 | padding: 0 !important; 153 | page-break-after: avoid !important; 154 | height: auto !important; 155 | min-height: auto !important; 156 | } 157 | .reveal .absolute-element { 158 | margin-left: 2.2cm; 159 | margin-top: 1.8cm; 160 | } 161 | .reveal section .fragment { 162 | opacity: 1 !important; 163 | visibility: visible !important; 164 | 165 | -webkit-transform: none !important; 166 | -moz-transform: none !important; 167 | -ms-transform: none !important; 168 | transform: none !important; 169 | } 170 | .reveal section .slide-background { 171 | position: absolute; 172 | top: 0; 173 | left: 0; 174 | width: 100%; 175 | z-index: 0; 176 | } 177 | .reveal section>* { 178 | position: relative; 179 | z-index: 1; 180 | } 181 | .reveal img { 182 | box-shadow: none; 183 | } 184 | .reveal .roll { 185 | overflow: visible; 186 | line-height: 1em; 187 | } 188 | .reveal small a { 189 | font-size: 16pt !important; 190 | } 191 | -------------------------------------------------------------------------------- /css/theme/README.md: -------------------------------------------------------------------------------- 1 | ## Dependencies 2 | 3 | Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment including the Grunt dependencies installed before proceding: https://github.com/hakimel/reveal.js#full-setup 4 | 5 | You also need to install Ruby and then Sass (with `gem install sass`). 6 | 7 | ## Creating a Theme 8 | 9 | To create your own theme, start by duplicating any ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source) and adding it to the compilation list in the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/Gruntfile.js). 10 | 11 | Each theme file does four things in the following order: 12 | 13 | 1. **Include [/css/theme/template/mixins.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/mixins.scss)** 14 | Shared utility functions. 15 | 16 | 2. **Include [/css/theme/template/settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss)** 17 | Declares a set of custom variables that the template file (step 4) expects. Can be overridden in step 3. 18 | 19 | 3. **Override** 20 | This is where you override the default theme. Either by specifying variables (see [settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss) for reference) or by adding full selectors with hardcoded styles. 21 | 22 | 4. **Include [/css/theme/template/theme.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/theme.scss)** 23 | The template theme file which will generate final CSS output based on the currently defined variables. 24 | 25 | When you are done, run `grunt themes` to compile the Sass file to CSS and you are ready to use your new theme. 26 | -------------------------------------------------------------------------------- /css/theme/firebase.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Gotham Thin'; 3 | src: url("font/Gotham-Thin.otf") format("opentype"); } 4 | .text-discs { 5 | color: #fec324 !important; } 6 | 7 | .text-tarheel { 8 | color: #50b1ff !important; } 9 | 10 | .text-rebel { 11 | color: #ff6450 !important; } 12 | 13 | .text-cream { 14 | color: #fffbf3 !important; } 15 | 16 | .slides section > h1 { 17 | margin-bottom: 100px; } 18 | .slides section > h2 { 19 | margin-bottom: 80px; } 20 | .slides section > h3 { 21 | margin-bottom: 80px; } 22 | .slides section > h4 { 23 | margin-bottom: 80px; } 24 | .slides section p:nth-child(even) { 25 | color: #fec324; } 26 | .slides section ol li:nth-child(odd) { 27 | color: #fec324; } 28 | .slides section ol li, .slides section p { 29 | font-size: 38px; } 30 | .slides blockquote { 31 | font-style: normal; } 32 | .slides pre { 33 | box-shadow: none !important; } 34 | .slides pre code { 35 | font-size: 20px; 36 | line-height: 30px; 37 | width: 100%; } 38 | .slides .era-point { 39 | margin-bottom: 40px; } 40 | .slides .api-point { 41 | font-family: Menlo, Courier; 42 | color: #fffbf3 !important; 43 | text-align: left; } 44 | .slides #json-graph { 45 | text-align: left; } 46 | .slides #json-graph h4 { 47 | text-align: left; } 48 | .slides #json-graph h4:nth-child(2) { 49 | margin-left: 140px; } 50 | .slides #json-graph h4:nth-child(3) { 51 | margin-left: 540px; } 52 | .slides #json-graph h4:nth-child(4) { 53 | margin-left: 800px; } 54 | 55 | .framework-logo { 56 | height: 24% !important; 57 | width: 24% !important; } 58 | 59 | .text-large { 60 | font-size: 104px !important; 61 | line-height: 208px !important; } 62 | 63 | .text-medium { 64 | font-size: 62px !important; 65 | line-height: 124px !important; } 66 | 67 | .text-small { 68 | font-size: 42px !important; } 69 | 70 | #era { 71 | padding: 15px; 72 | text-transform: uppercase; 73 | font-size: 16px; } 74 | 75 | img.no-style { 76 | background: transparent !important; 77 | border: none !important; 78 | box-shadow: none !important; } 79 | 80 | img.white-bg { 81 | background: #f0f0f0 !important; 82 | padding: 10px !important; } 83 | 84 | .button { 85 | border: none !important; 86 | text-align: left !important; 87 | display: inline-block; 88 | line-height: 32px; 89 | padding: 0 16px; 90 | font-size: 14px; 91 | font-weight: 400; 92 | border-radius: 3px; 93 | text-decoration: none; 94 | text-transform: uppercase; 95 | overflow: hidden; } 96 | 97 | .code-box { 98 | border-radius: 4px; 99 | background: #191919; 100 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); 101 | margin-bottom: 8px; } 102 | 103 | .code-box header { 104 | background: #191919; 105 | color: #FFF; 106 | padding: 8px; 107 | border-radius: 4px 4px 0 0; 108 | text-align: left; } 109 | 110 | .code-box nav button { 111 | border: none; 112 | line-height: 24px; 113 | padding: 12px 24px; 114 | margin-right: 8px; 115 | font-size: 16px; 116 | background: #3f3f3f; 117 | color: #DCDCDC; 118 | font-weight: 500; 119 | text-transform: none; 120 | font-family: 'Gotham Thin'; 121 | cursor: pointer; 122 | outline: none; } 123 | 124 | .code-box nav button:active { 125 | background: #353535; 126 | color: #DADADA; } 127 | 128 | .code-box nav button.is-selected, .code-box nav button.selected { 129 | background: #353535; 130 | color: #DADADA; } 131 | 132 | .code-box .prettyprint { 133 | box-shadow: none; 134 | margin: 0; } 135 | 136 | .active { 137 | display: block; } 138 | 139 | .code-box-examples pre { 140 | display: none; } 141 | 142 | .code-box-examples pre.isActive { 143 | display: block; } 144 | 145 | code { 146 | display: none; 147 | margin: 8px; } 148 | 149 | .reveal pre { 150 | margin: 0; 151 | width: 99%; } 152 | 153 | .reveal pre code { 154 | max-height: 500px; } 155 | 156 | section.present.hasCode { 157 | height: 500px; } 158 | 159 | pre.code-example.isActive code.code-example { 160 | position: absolute !important; 161 | top: 0 !important; } 162 | 163 | /********************************************* 164 | * GLOBAL STYLES 165 | *********************************************/ 166 | body { 167 | background: #322f31; 168 | background-color: #322f31; } 169 | 170 | .reveal { 171 | font-family: "Gotham Thin", Arial; 172 | font-size: 32px; 173 | font-weight: normal; 174 | letter-spacing: -0.02em; 175 | color: #fffbf3; } 176 | 177 | ::selection { 178 | color: #fff; 179 | background: #50b1ff; 180 | text-shadow: none; } 181 | 182 | /********************************************* 183 | * HEADERS 184 | *********************************************/ 185 | .reveal h1, 186 | .reveal h2, 187 | .reveal h3, 188 | .reveal h4, 189 | .reveal h5, 190 | .reveal h6 { 191 | margin: 0 0 20px 0; 192 | color: #fffbf3; 193 | font-family: "Gotham Thin", Arial; 194 | line-height: 1em; 195 | letter-spacing: 0.02em; 196 | text-transform: uppercase; 197 | text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); 198 | font-weight: bold; } 199 | 200 | .reveal h1 { 201 | text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); } 202 | 203 | /********************************************* 204 | * LINKS 205 | *********************************************/ 206 | .reveal a:not(.image) { 207 | color: #fec324; 208 | text-decoration: none; 209 | -webkit-transition: color .15s ease; 210 | -moz-transition: color .15s ease; 211 | -ms-transition: color .15s ease; 212 | -o-transition: color .15s ease; 213 | transition: color .15s ease; } 214 | 215 | .reveal a:not(.image):hover { 216 | color: #50b1ff; 217 | text-shadow: none; 218 | border: none; } 219 | 220 | .reveal .roll span:after { 221 | color: #fff; 222 | background: #d59b01; } 223 | 224 | /********************************************* 225 | * IMAGES 226 | *********************************************/ 227 | .reveal section img { 228 | margin: 15px 0px; 229 | background: rgba(255, 255, 255, 0.12); 230 | border: 4px solid #fffbf3; 231 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); 232 | -webkit-transition: all .2s linear; 233 | -moz-transition: all .2s linear; 234 | -ms-transition: all .2s linear; 235 | -o-transition: all .2s linear; 236 | transition: all .2s linear; } 237 | 238 | .reveal a:hover img { 239 | background: rgba(255, 255, 255, 0.2); 240 | border-color: #fec324; 241 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } 242 | 243 | /********************************************* 244 | * NAVIGATION CONTROLS 245 | *********************************************/ 246 | .reveal .controls div.navigate-left, 247 | .reveal .controls div.navigate-left.enabled { 248 | border-right-color: #fec324; } 249 | 250 | .reveal .controls div.navigate-right, 251 | .reveal .controls div.navigate-right.enabled { 252 | border-left-color: #fec324; } 253 | 254 | .reveal .controls div.navigate-up, 255 | .reveal .controls div.navigate-up.enabled { 256 | border-bottom-color: #fec324; } 257 | 258 | .reveal .controls div.navigate-down, 259 | .reveal .controls div.navigate-down.enabled { 260 | border-top-color: #fec324; } 261 | 262 | .reveal .controls div.navigate-left.enabled:hover { 263 | border-right-color: #50b1ff; } 264 | 265 | .reveal .controls div.navigate-right.enabled:hover { 266 | border-left-color: #50b1ff; } 267 | 268 | .reveal .controls div.navigate-up.enabled:hover { 269 | border-bottom-color: #50b1ff; } 270 | 271 | .reveal .controls div.navigate-down.enabled:hover { 272 | border-top-color: #50b1ff; } 273 | 274 | /********************************************* 275 | * PROGRESS BAR 276 | *********************************************/ 277 | .reveal .progress { 278 | background: rgba(0, 0, 0, 0.2); } 279 | 280 | .reveal .progress span { 281 | background: #fec324; 282 | -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 283 | -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 284 | -ms-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 285 | -o-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 286 | transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } 287 | 288 | /********************************************* 289 | * SLIDE NUMBER 290 | *********************************************/ 291 | .reveal .slide-number { 292 | color: #fec324; } 293 | 294 | /*# sourceMappingURL=firebase.css.map */ 295 | -------------------------------------------------------------------------------- /css/theme/firebase.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAMA,UAGC;EAFC,WAAW,EAAE,aAAa;EAC1B,GAAG,EAAE,8CAA8C;AAuErD,WAAY;EACV,KAAK,EAAE,kBAAiB;;AAG1B,aAAc;EACZ,KAAK,EAAE,kBAAmB;;AAG5B,WAAY;EACV,KAAK,EAAE,kBAAiB;;AAG1B,WAAY;EACV,KAAK,EAAE,kBAAiB;;AAKtB,oBAAK;EACH,aAAa,EAAE,KAAK;AAEtB,oBAAI;EACF,aAAa,EAAE,IAAI;AAErB,oBAAI;EACF,aAAa,EAAE,IAAI;AAErB,oBAAI;EACF,aAAa,EAAE,IAAI;AAErB,iCAAkB;EAChB,KAAK,EA5CC,OAAM;AA8Cd,oCAAqB;EACnB,KAAK,EA/CC,OAAM;AAiDd,wCAAS;EACP,SAAS,EAAE,IAAI;AAInB,kBAAW;EACT,UAAU,EAAE,MAAM;AAGpB,WAAI;EACF,UAAU,EAAE,eAAe;AAG7B,gBAAS;EACP,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,IAAI;EACjB,KAAK,EAAE,IAAI;AAGb,kBAAW;EACT,aAAa,EAAE,IAAI;AAGrB,kBAAW;EACT,WAAW,EAAE,cAAc;EAC3B,KAAK,EAAE,kBAAiB;EACxB,UAAU,EAAE,IAAI;AAGlB,mBAAY;EACV,UAAU,EAAE,IAAI;EAChB,sBAAG;IACD,UAAU,EAAE,IAAI;EAElB,mCAAgB;IACd,WAAW,EAAE,KAAK;EAEpB,mCAAgB;IACd,WAAW,EAAE,KAAK;EAEpB,mCAAgB;IACd,WAAW,EAAE,KAAK;;AAKxB,eAAgB;EACd,MAAM,EAAE,cAAc;EACtB,KAAK,EAAE,cAAc;;AAGvB,WAAY;EACV,SAAS,EAAE,gBAAgB;EAC3B,WAAW,EAAE,gBAAgB;;AAG/B,YAAa;EACX,SAAS,EAAE,eAAe;EAC1B,WAAW,EAAE,gBAAgB;;AAG/B,WAAY;EACV,SAAS,EAAE,eAAe;;AAG5B,IAAK;EACH,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,SAAS;EACzB,SAAS,EAAE,IAAI;;AAGjB,YAAa;EACX,UAAU,EAAE,sBAAsB;EAClC,MAAM,EAAE,eAAe;EACvB,UAAU,EAAE,eAAe;;AAG7B,YAAa;EACX,UAAU,EAAE,kBAAkB;EAC9B,OAAO,EAAE,eAAe;;AAIxB,OAAQ;EACN,MAAM,EAAE,eAAe;EACvB,UAAU,EAAE,eAAe;EAC3B,OAAO,EAAE,YAAY;EACrB,WAAW,EAAE,IAAI;EACjB,OAAO,EAAE,MAAM;EACf,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,GAAG;EAChB,aAAa,EAAE,GAAG;EAClB,eAAe,EAAE,IAAI;EACrB,cAAc,EAAE,SAAS;EACzB,QAAQ,EAAE,MAAM;;AAGpB,SAAU;EACN,aAAa,EAAE,GAAG;EAClB,UAAU,EAAE,OAAO;EACnB,UAAU,EAAE,4BAAyB;EACrC,aAAa,EAAE,GAAG;;AAGtB,gBAAiB;EACb,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,IAAI;EACX,OAAO,EAAE,GAAG;EACZ,aAAa,EAAE,WAAW;EAC1B,UAAU,EAAE,IAAI;;AAGpB,oBAAqB;EACnB,MAAM,EAAE,IAAI;EACZ,WAAW,EAAE,IAAI;EACjB,OAAO,EAAE,SAAS;EAClB,YAAY,EAAE,GAAG;EACjB,SAAS,EAAE,IAAI;EACf,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,OAAO;EACd,WAAW,EAAE,GAAG;EAChB,cAAc,EAAE,IAAI;EACpB,WAAW,EAAE,aAAa;EAC1B,MAAM,EAAE,OAAO;EACf,OAAO,EAAE,IAAI;;AAGf,2BAA4B;EAC1B,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,OAAO;;AAGhB,+DAA+D;EAC7D,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,OAAO;;AAGhB,sBAAuB;EACnB,UAAU,EAAE,IAAI;EAChB,MAAM,EAAE,CAAC;;AAGb,OAAQ;EACN,OAAO,EAAE,KAAK;;AAGhB,sBAAuB;EACrB,OAAO,EAAE,IAAI;;AAGf,+BAAgC;EAC9B,OAAO,EAAE,KAAK;;AAGhB,IAAK;EACH,OAAO,EAAE,IAAI;EACb,MAAM,EAAE,GAAG;;AAGb,WAAY;EACV,MAAM,EAAE,CAAC;EACT,KAAK,EAAE,GAAG;;AAGZ,gBAAiB;EACf,UAAU,EAAE,KAAK;;AAGnB,uBAAwB;EACtB,MAAM,EAAE,KAAK;;AAIb,2CAAkB;EAChB,QAAQ,EAAE,mBAAmB;EAC7B,GAAG,EAAE,YAAY;;;;;AC5RrB,IAAK;EDsEH,UAAU,EA7BM,OAAM;ECvCvB,gBAAgB,EDuCC,OAAM;;ACpCxB,OAAQ;EACP,WAAW,ED4CE,oBAAK;EC3ClB,SAAS,EDsCK,IAAI;ECrClB,WAAW,EAAE,MAAM;EACnB,cAAc,EAAE,OAAO;EACvB,KAAK,EDyCS,OAAM;;ACtCrB,WAAY;EACX,KAAK,EDmDW,IAAI;EClDpB,UAAU,EDiDgB,OAAQ;EChDlC,WAAW,EAAE,IAAI;;;;;AAOlB;;;;;UAKW;EACV,MAAM,EDoBS,UAAU;ECnBzB,KAAK,EDqBS,OAAM;ECnBpB,WAAW,EDkBE,oBAAK;ECjBlB,WAAW,EDmBQ,GAAG;EClBtB,cAAc,EDmBQ,MAAM;ECjB5B,cAAc,EDkBQ,SAAS;ECjB/B,WAAW,EDmBS,8BAAkB;EClBrC,WAAW,EDmBO,IAAI;;AChBxB,UAAW;EACV,WAAW,EDcS,8BAAkB;;;;;ACNvC,qBAAsB;EACrB,KAAK,EDSM,OAAM;ECRjB,eAAe,EAAE,IAAI;EAErB,kBAAkB,EAAE,eAAe;EAChC,eAAe,EAAE,eAAe;EAC/B,cAAc,EAAE,eAAe;EAC9B,aAAa,EAAE,eAAe;EAC3B,UAAU,EAAE,eAAe;;AAEnC,2BAA4B;EAC3B,KAAK,EDGoB,OAAQ;ECDjC,WAAW,EAAE,IAAI;EACjB,MAAM,EAAE,IAAI;;AAGd,wBAAyB;EACxB,KAAK,EAAE,IAAI;EACX,UAAU,EAAE,OAAyB;;;;;AAQtC,mBAAoB;EACnB,MAAM,EAAE,QAAQ;EAChB,UAAU,EAAE,yBAAsB;EAClC,MAAM,EAAE,iBAAoB;EAE5B,UAAU,EAAE,4BAA4B;EAExC,kBAAkB,EAAE,cAAc;EAC/B,eAAe,EAAE,cAAc;EAC9B,cAAc,EAAE,cAAc;EAC7B,aAAa,EAAE,cAAc;EAC1B,UAAU,EAAE,cAAc;;AAGlC,mBAAoB;EACnB,UAAU,EAAE,wBAAqB;EACjC,YAAY,EDjCF,OAAM;ECmChB,UAAU,EAAE,4BAA4B;;;;;AAQ1C;2CAC4C;EAC3C,kBAAkB,ED7CP,OAAM;;ACgDlB;4CAC6C;EAC5C,iBAAiB,EDlDN,OAAM;;ACqDlB;yCAC0C;EACzC,mBAAmB,EDvDR,OAAM;;AC0DlB;2CAC4C;EAC3C,gBAAgB,ED5DL,OAAM;;AC+DlB,iDAAkD;EACjD,kBAAkB,ED5DQ,OAAQ;;AC+DnC,kDAAmD;EAClD,iBAAiB,EDhES,OAAQ;;ACmEnC,+CAAgD;EAC/C,mBAAmB,EDpEO,OAAQ;;ACuEnC,iDAAkD;EACjD,gBAAgB,EDxEU,OAAQ;;;;;ACgFnC,iBAAkB;EACjB,UAAU,EAAE,kBAAe;;AAE3B,sBAAuB;EACtB,UAAU,EDxFA,OAAM;EC0FhB,kBAAkB,EAAE,iDAAoD;EACrE,eAAe,EAAE,iDAAoD;EACpE,cAAc,EAAE,iDAAoD;EACnE,aAAa,EAAE,iDAAoD;EAChE,UAAU,EAAE,iDAAoD;;;;;AAM1E,qBAAsB;EACpB,KAAK,EDrGK,OAAM", 4 | "sources": ["source/firebase.scss","template/theme.scss"], 5 | "names": [], 6 | "file": "firebase.css" 7 | } 8 | -------------------------------------------------------------------------------- /css/theme/font/Gotham-Thin.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/css/theme/font/Gotham-Thin.otf -------------------------------------------------------------------------------- /css/theme/source/firebase.scss: -------------------------------------------------------------------------------- 1 | 2 | // Default mixins and settings ----------------- 3 | @import "../template/mixins"; 4 | @import "../template/settings"; 5 | // --------------------------------------------- 6 | 7 | @font-face { 8 | font-family: 'Gotham Thin'; 9 | src: url("font/Gotham-Thin.otf") format("opentype"); 10 | } 11 | 12 | $white: #ffffff; 13 | $black: #000000; 14 | 15 | $azucar: #185689; 16 | $darkcar: #1b5e96; 17 | $darkercar: #154a75; 18 | $bumble-bee: #ffd641; 19 | 20 | $slate: #322f31; 21 | $shale: #1d1d1e; 22 | $discs: #fec324; 23 | $keylime: #acfd5b; 24 | $tarheel: #50b1ff; 25 | $rebel: #ff6450; 26 | $pride: #b650ff; 27 | $cream: #fffbf3; 28 | 29 | $slate-dark: #154a75; 30 | $bumble-mid: #fedd7b; 31 | $bumble-dark: #c5a530; 32 | 33 | 34 | $font: 'Gotham Thin', Arial; 35 | $mainFont: "proxima-nova",sans-serif; 36 | 37 | $brand-primary: $tarheel; 38 | $brand-warning: $discs; 39 | $brand-danger: $rebel; 40 | 41 | /////////////////////////////////////////// 42 | 43 | 44 | // Base settings for all themes that can optionally be 45 | // overridden by the super-theme 46 | 47 | // Background of the presentation 48 | $backgroundColor: $slate; 49 | 50 | // Primary/body text 51 | $mainFont: $font; 52 | $mainFontSize: 32px; 53 | $mainColor: $cream; 54 | 55 | // Headings 56 | $headingMargin: 0 0 20px 0; 57 | $headingFont: $font; 58 | $headingColor: $cream; 59 | $headingLineHeight: 1em; 60 | $headingLetterSpacing: 0.02em; 61 | $headingTextTransform: uppercase; 62 | $headingTextShadow: 0px 0px 6px rgba(0,0,0,0.2); 63 | $heading1TextShadow: $headingTextShadow; 64 | $headingFontWeight: bold; 65 | 66 | // Links and actions 67 | $linkColor: $discs; 68 | $linkColorHover: $tarheel; 69 | 70 | // Text selection 71 | $selectionBackgroundColor: $tarheel; 72 | $selectionColor: #fff; 73 | 74 | // Generates the presentation background, can be overridden 75 | // to return a background image or gradient 76 | @mixin bodyBackground() { 77 | background: $backgroundColor; 78 | } 79 | 80 | .text-discs { 81 | color: $discs !important; 82 | } 83 | 84 | .text-tarheel { 85 | color: $tarheel !important; 86 | } 87 | 88 | .text-rebel { 89 | color: $rebel !important; 90 | } 91 | 92 | .text-cream { 93 | color: $cream !important; 94 | } 95 | 96 | .slides { 97 | section { 98 | > h1 { 99 | margin-bottom: 100px; 100 | } 101 | >h2 { 102 | margin-bottom: 80px; 103 | } 104 | >h3 { 105 | margin-bottom: 80px; 106 | } 107 | >h4 { 108 | margin-bottom: 80px; 109 | } 110 | p:nth-child(even) { 111 | color: $discs; 112 | } 113 | ol li:nth-child(odd) { 114 | color: $discs; 115 | } 116 | ol li, p { 117 | font-size: 38px; 118 | } 119 | } 120 | 121 | blockquote { 122 | font-style: normal; 123 | } 124 | 125 | pre { 126 | box-shadow: none !important; 127 | } 128 | 129 | pre code { 130 | font-size: 20px; 131 | line-height: 30px; 132 | width: 100%; 133 | } 134 | 135 | .era-point { 136 | margin-bottom: 40px; 137 | } 138 | 139 | .api-point { 140 | font-family: Menlo, Courier; 141 | color: $cream !important; 142 | text-align: left; 143 | } 144 | 145 | #json-graph { 146 | text-align: left; 147 | h4 { 148 | text-align: left; 149 | } 150 | h4:nth-child(2) { 151 | margin-left: 140px; 152 | } 153 | h4:nth-child(3) { 154 | margin-left: 540px; 155 | } 156 | h4:nth-child(4) { 157 | margin-left: 800px; 158 | } 159 | } 160 | } 161 | 162 | .framework-logo { 163 | height: 24% !important; 164 | width: 24% !important; 165 | } 166 | 167 | .text-large { 168 | font-size: 104px !important; 169 | line-height: 208px !important; 170 | } 171 | 172 | .text-medium { 173 | font-size: 62px !important; 174 | line-height: 124px !important; 175 | } 176 | 177 | .text-small { 178 | font-size: 42px !important; 179 | } 180 | 181 | #era { 182 | padding: 15px; 183 | text-transform: uppercase; 184 | font-size: 16px; 185 | } 186 | 187 | img.no-style { 188 | background: transparent !important; 189 | border: none !important; 190 | box-shadow: none !important; 191 | } 192 | 193 | img.white-bg { 194 | background: #f0f0f0 !important; 195 | padding: 10px !important; 196 | } 197 | 198 | // code-box ------------------------------ 199 | .button { 200 | border: none !important; 201 | text-align: left !important; 202 | display: inline-block; 203 | line-height: 32px; 204 | padding: 0 16px; 205 | font-size: 14px; 206 | font-weight: 400; 207 | border-radius: 3px; 208 | text-decoration: none; 209 | text-transform: uppercase; 210 | overflow: hidden; 211 | } 212 | 213 | .code-box { 214 | border-radius: 4px; 215 | background: #191919; 216 | box-shadow: 0 2px 5px rgba(0,0,0,0.3); 217 | margin-bottom: 8px; 218 | } 219 | 220 | .code-box header { 221 | background: #191919; 222 | color: #FFF; 223 | padding: 8px; 224 | border-radius: 4px 4px 0 0; 225 | text-align: left; 226 | } 227 | 228 | .code-box nav button { 229 | border: none; 230 | line-height: 24px; 231 | padding: 12px 24px; 232 | margin-right: 8px; 233 | font-size: 16px; 234 | background: #3f3f3f; 235 | color: #DCDCDC; 236 | font-weight: 500; 237 | text-transform: none; 238 | font-family: 'Gotham Thin'; 239 | cursor: pointer; 240 | outline: none; 241 | } 242 | 243 | .code-box nav button:active { 244 | background: #353535; 245 | color: #DADADA; 246 | } 247 | 248 | .code-box nav button.is-selected,.code-box nav button.selected { 249 | background: #353535; 250 | color: #DADADA; 251 | } 252 | 253 | .code-box .prettyprint { 254 | box-shadow: none; 255 | margin: 0 256 | } 257 | 258 | .active { 259 | display: block; 260 | } 261 | 262 | .code-box-examples pre { 263 | display: none; 264 | } 265 | 266 | .code-box-examples pre.isActive { 267 | display: block; 268 | } 269 | 270 | code { 271 | display: none; 272 | margin: 8px; 273 | } 274 | 275 | .reveal pre { 276 | margin: 0; 277 | width: 99%; 278 | } 279 | 280 | .reveal pre code { 281 | max-height: 500px; 282 | } 283 | 284 | section.present.hasCode { 285 | height: 500px; 286 | } 287 | 288 | pre.code-example.isActive { 289 | code.code-example { 290 | position: absolute !important; 291 | top: 0 !important; 292 | } 293 | } 294 | 295 | // Theme template ------------------------------ 296 | @import "../template/theme"; 297 | // --------------------------------------------- 298 | -------------------------------------------------------------------------------- /css/theme/template/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin vertical-gradient( $top, $bottom ) { 2 | background: $top; 3 | background: -moz-linear-gradient( top, $top 0%, $bottom 100% ); 4 | background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) ); 5 | background: -webkit-linear-gradient( top, $top 0%, $bottom 100% ); 6 | background: -o-linear-gradient( top, $top 0%, $bottom 100% ); 7 | background: -ms-linear-gradient( top, $top 0%, $bottom 100% ); 8 | background: linear-gradient( top, $top 0%, $bottom 100% ); 9 | } 10 | 11 | @mixin horizontal-gradient( $top, $bottom ) { 12 | background: $top; 13 | background: -moz-linear-gradient( left, $top 0%, $bottom 100% ); 14 | background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) ); 15 | background: -webkit-linear-gradient( left, $top 0%, $bottom 100% ); 16 | background: -o-linear-gradient( left, $top 0%, $bottom 100% ); 17 | background: -ms-linear-gradient( left, $top 0%, $bottom 100% ); 18 | background: linear-gradient( left, $top 0%, $bottom 100% ); 19 | } 20 | 21 | @mixin radial-gradient( $outer, $inner, $type: circle ) { 22 | background: $outer; 23 | background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 24 | background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) ); 25 | background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 26 | background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 27 | background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 28 | background: radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 29 | } -------------------------------------------------------------------------------- /css/theme/template/settings.scss: -------------------------------------------------------------------------------- 1 | // Base settings for all themes that can optionally be 2 | // overridden by the super-theme 3 | 4 | // Background of the presentation 5 | $backgroundColor: #2b2b2b; 6 | 7 | // Primary/body text 8 | $mainFont: 'Lato', sans-serif; 9 | $mainFontSize: 36px; 10 | $mainColor: #eee; 11 | 12 | // Headings 13 | $headingMargin: 0 0 20px 0; 14 | $headingFont: 'League Gothic', Impact, sans-serif; 15 | $headingColor: #eee; 16 | $headingLineHeight: 0.9em; 17 | $headingLetterSpacing: 0.02em; 18 | $headingTextTransform: uppercase; 19 | $headingTextShadow: 0px 0px 6px rgba(0,0,0,0.2); 20 | $heading1TextShadow: $headingTextShadow; 21 | $headingFontWeight: normal; 22 | 23 | // Links and actions 24 | $linkColor: #13DAEC; 25 | $linkColorHover: lighten( $linkColor, 20% ); 26 | 27 | // Text selection 28 | $selectionBackgroundColor: #FF5E99; 29 | $selectionColor: #fff; 30 | 31 | // Generates the presentation background, can be overridden 32 | // to return a background image or gradient 33 | @mixin bodyBackground() { 34 | background: $backgroundColor; 35 | } 36 | -------------------------------------------------------------------------------- /css/theme/template/theme.scss: -------------------------------------------------------------------------------- 1 | // Base theme template for reveal.js 2 | 3 | /********************************************* 4 | * GLOBAL STYLES 5 | *********************************************/ 6 | 7 | body { 8 | @include bodyBackground(); 9 | background-color: $backgroundColor; 10 | } 11 | 12 | .reveal { 13 | font-family: $mainFont; 14 | font-size: $mainFontSize; 15 | font-weight: normal; 16 | letter-spacing: -0.02em; 17 | color: $mainColor; 18 | } 19 | 20 | ::selection { 21 | color: $selectionColor; 22 | background: $selectionBackgroundColor; 23 | text-shadow: none; 24 | } 25 | 26 | /********************************************* 27 | * HEADERS 28 | *********************************************/ 29 | 30 | .reveal h1, 31 | .reveal h2, 32 | .reveal h3, 33 | .reveal h4, 34 | .reveal h5, 35 | .reveal h6 { 36 | margin: $headingMargin; 37 | color: $headingColor; 38 | 39 | font-family: $headingFont; 40 | line-height: $headingLineHeight; 41 | letter-spacing: $headingLetterSpacing; 42 | 43 | text-transform: $headingTextTransform; 44 | text-shadow: $headingTextShadow; 45 | font-weight: $headingFontWeight; 46 | } 47 | 48 | .reveal h1 { 49 | text-shadow: $heading1TextShadow; 50 | } 51 | 52 | 53 | /********************************************* 54 | * LINKS 55 | *********************************************/ 56 | 57 | .reveal a:not(.image) { 58 | color: $linkColor; 59 | text-decoration: none; 60 | 61 | -webkit-transition: color .15s ease; 62 | -moz-transition: color .15s ease; 63 | -ms-transition: color .15s ease; 64 | -o-transition: color .15s ease; 65 | transition: color .15s ease; 66 | } 67 | .reveal a:not(.image):hover { 68 | color: $linkColorHover; 69 | 70 | text-shadow: none; 71 | border: none; 72 | } 73 | 74 | .reveal .roll span:after { 75 | color: #fff; 76 | background: darken( $linkColor, 15% ); 77 | } 78 | 79 | 80 | /********************************************* 81 | * IMAGES 82 | *********************************************/ 83 | 84 | .reveal section img { 85 | margin: 15px 0px; 86 | background: rgba(255,255,255,0.12); 87 | border: 4px solid $mainColor; 88 | 89 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); 90 | 91 | -webkit-transition: all .2s linear; 92 | -moz-transition: all .2s linear; 93 | -ms-transition: all .2s linear; 94 | -o-transition: all .2s linear; 95 | transition: all .2s linear; 96 | } 97 | 98 | .reveal a:hover img { 99 | background: rgba(255,255,255,0.2); 100 | border-color: $linkColor; 101 | 102 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); 103 | } 104 | 105 | 106 | /********************************************* 107 | * NAVIGATION CONTROLS 108 | *********************************************/ 109 | 110 | .reveal .controls div.navigate-left, 111 | .reveal .controls div.navigate-left.enabled { 112 | border-right-color: $linkColor; 113 | } 114 | 115 | .reveal .controls div.navigate-right, 116 | .reveal .controls div.navigate-right.enabled { 117 | border-left-color: $linkColor; 118 | } 119 | 120 | .reveal .controls div.navigate-up, 121 | .reveal .controls div.navigate-up.enabled { 122 | border-bottom-color: $linkColor; 123 | } 124 | 125 | .reveal .controls div.navigate-down, 126 | .reveal .controls div.navigate-down.enabled { 127 | border-top-color: $linkColor; 128 | } 129 | 130 | .reveal .controls div.navigate-left.enabled:hover { 131 | border-right-color: $linkColorHover; 132 | } 133 | 134 | .reveal .controls div.navigate-right.enabled:hover { 135 | border-left-color: $linkColorHover; 136 | } 137 | 138 | .reveal .controls div.navigate-up.enabled:hover { 139 | border-bottom-color: $linkColorHover; 140 | } 141 | 142 | .reveal .controls div.navigate-down.enabled:hover { 143 | border-top-color: $linkColorHover; 144 | } 145 | 146 | 147 | /********************************************* 148 | * PROGRESS BAR 149 | *********************************************/ 150 | 151 | .reveal .progress { 152 | background: rgba(0,0,0,0.2); 153 | } 154 | .reveal .progress span { 155 | background: $linkColor; 156 | 157 | -webkit-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 158 | -moz-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 159 | -ms-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 160 | -o-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 161 | transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 162 | } 163 | 164 | /********************************************* 165 | * SLIDE NUMBER 166 | *********************************************/ 167 | .reveal .slide-number { 168 | color: $linkColor; 169 | } 170 | -------------------------------------------------------------------------------- /full-stack-clients.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/full-stack-clients.psd -------------------------------------------------------------------------------- /full-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/full-stack.png -------------------------------------------------------------------------------- /full-stack.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/full-stack.psd -------------------------------------------------------------------------------- /images/HTML5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/HTML5.png -------------------------------------------------------------------------------- /images/Realtime-sync-anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/Realtime-sync-anim.gif -------------------------------------------------------------------------------- /images/angularjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/angularjs.png -------------------------------------------------------------------------------- /images/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/api.png -------------------------------------------------------------------------------- /images/auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/auth.png -------------------------------------------------------------------------------- /images/backbonejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/backbonejs.png -------------------------------------------------------------------------------- /images/backend-reqs (1).gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/backend-reqs (1).gif -------------------------------------------------------------------------------- /images/backend-reqs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/backend-reqs.gif -------------------------------------------------------------------------------- /images/clients.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/clients.png -------------------------------------------------------------------------------- /images/clients.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/clients.psd -------------------------------------------------------------------------------- /images/data-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/data-box.png -------------------------------------------------------------------------------- /images/data-box.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/data-box.psd -------------------------------------------------------------------------------- /images/database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/database.png -------------------------------------------------------------------------------- /images/docs-site.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/docs-site.png -------------------------------------------------------------------------------- /images/emberjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/emberjs.png -------------------------------------------------------------------------------- /images/fb-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/fb-arch.png -------------------------------------------------------------------------------- /images/firebase-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/firebase-logo.png -------------------------------------------------------------------------------- /images/full-stack-clients.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/full-stack-clients.png -------------------------------------------------------------------------------- /images/full-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/full-stack.png -------------------------------------------------------------------------------- /images/gdocs4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/gdocs4.gif -------------------------------------------------------------------------------- /images/hosting-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/hosting-box.png -------------------------------------------------------------------------------- /images/iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/iphone.png -------------------------------------------------------------------------------- /images/laptop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/laptop.png -------------------------------------------------------------------------------- /images/login-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/login-box.png -------------------------------------------------------------------------------- /images/nexus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/nexus.png -------------------------------------------------------------------------------- /images/offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/offline.png -------------------------------------------------------------------------------- /images/platforms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/platforms.png -------------------------------------------------------------------------------- /images/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/react.png -------------------------------------------------------------------------------- /images/realtime-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/realtime-api.png -------------------------------------------------------------------------------- /images/security-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/security-box.png -------------------------------------------------------------------------------- /images/server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/server.png -------------------------------------------------------------------------------- /images/simple-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/images/simple-api.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Building Realtime Apps with Firebase 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 51 | 52 | 55 | 56 | 57 | 58 | 59 |
60 | 61 | 62 | 63 | 64 | 65 |
66 | 67 | 70 |
71 |

72 |

Intro to Firebase

73 | 74 |

75 |

76 |

YOUR_NAME / @YOUR_TWITTER_HANDLE
77 | 78 |

79 |
80 | 81 |
82 |

The Realtime Backend

83 |

for your App

84 |
85 | 86 |
87 |

Creating an app shouldn't require managing complex infrastructure.

88 |
89 | 90 |
91 |

The Full Stack

92 | The Full Stack 93 |
94 | 95 |
96 |

The full stack is never really that simple

97 | 98 |
99 | 100 |
101 |

... and neither is the client side

102 |

103 | gdocs 104 |
105 | 106 |
107 |

The user doesn't care about your backend

108 |
109 | 110 |
111 |

The user expects

112 |

Speed

113 |

Offline

114 |

Multi-platform

115 |

Simple Authentication

116 |
117 | 118 | 121 |
122 |

Firebase

123 | 124 |
125 |

The Realtime Backend

126 |

for your App

127 |
128 | 129 |
130 |

The Firebase Backend

131 | 132 | 133 | 134 | 135 |
136 | 137 |
138 |

Forget the Server

139 |

Firebase apps can run on client-side code only

140 |
141 | 142 |
143 | 144 |
145 | 146 |
147 | 148 |
149 |

Cloud database

150 |

NoSQL data-store

151 |

Returns JSON from a RESTful API

152 |
153 | 154 |
155 | 156 |
157 | 158 |
159 |

Realtime Data

160 |

161 | Whenever data is updated in Firebase, 162 | it sends the update down to every listening client 163 |

164 |
165 | 166 |
167 | 168 |
169 | 170 |
171 |

SDKs

172 | 173 |
174 | 175 |
176 |

Store Data

177 | 178 | 179 | var ref = new Firebase("https://<your-firebase-app>.firebaseio.com/message"); 180 | ref.set('All the things'); 181 | 182 | 183 | let ref = Firebase(url: "https://<your-firebase-app>.firebaseio.com/message") 184 | ref.setValue("All the things") 185 | 186 | 187 | Firebase ref = new Firebase("https://<your-firebase-app>.firebaseio.com/message"); 188 | ref.setValue("All the things"); 189 | 190 | 191 | 192 |
193 | 194 |
195 |

Sync Data

196 | 197 | 198 | 199 | var ref = new Firebase("https://<your-firebase-app>.firebaseio.com/message"); 200 | ref.on("value", function(snap) { 201 | console.log(snap.val()); 202 | }); 203 | 204 | 205 | let ref = Firebase(url: "https://<your-firebase-app>.firebaseio.com/message") 206 | ref.observeEventType(.Value, withBlock: { snap in 207 | println(snap.value) 208 | }) 209 | 210 | 211 | Firebase ref = new Firebase("https://<your-firebase-app>.firebaseio.com/message"); 212 | ref.addValueEventListener(new ValueEventListener() { 213 | @Override 214 | public void onDataChange(DataSnapshot snapshot) { 215 | System.out.println(snapshot.getValue()); 216 | } 217 | @Override public void onCancelled(FirebaseError error) { } 218 | }); 219 | 220 | 221 | 222 |
223 | 224 |
225 |

Offline

226 | 227 |
228 | 229 |
230 |

Intermittent Offline

231 | 232 |

What happens when you go through a tunnel?

233 |

234 |

235 | Firebase clients store a local cache of your data. When the user 236 | goes offline, the app still works as expected with the local cache. 237 |

238 | 239 |
240 | 241 |
242 |

Extended Offline

243 | 244 |

What happens when you go on a flight?

245 |

246 |

247 | On mobile, Firebase persists data to a local store. The app will continue 248 | to work even across app restarts. 249 |

250 | 251 |
252 | 253 |
254 |

One line of code

255 | 256 | 257 | 258 | Firebase.defaultConfig().persistenceEnabled = true 259 | 260 | 261 | Firebase.getDefaultConfig().setPersistenceEnabled(true); 262 | 263 | 264 | 265 |
266 | 267 |
268 | 269 |
270 | 271 |
272 | 273 |

274 | Firebase supports several forms of authentication 275 |

276 | 277 |

278 | Email & Password 279 |

280 | 281 |

282 | Google 283 |

284 | 285 |

286 | Twitter 287 |

288 | 289 |

290 | Facebook 291 |

292 | 293 |

294 | Github 295 |

296 | 297 |

298 | Anonymous 299 |

300 | 301 |

302 | ...and even custom backends 303 |

304 | 305 |
306 | 307 |
308 | 309 |

Authentication

310 | 311 | 312 | 313 | // Login with Google 314 | ref.authWithOAuthPopup('google', function(error, authData) { 315 | console.log(authData); 316 | }); 317 | 318 | 319 | 320 | ref.authWithOAuthProvider("google", token: "<OAuth Token>", 321 | withCompletionBlock: { error, authData in 322 | if error != nil { 323 | // Error authenticating with Firebase with OAuth token 324 | } else { 325 | // User is now logged in! 326 | println("Successfully logged in! \(authData)") 327 | } 328 | }) 329 | 330 | 331 | 332 | ref.authWithOAuthToken("google", "<OAuth Token>", new Firebase.AuthResultHandler() { 333 | @Override 334 | public void onAuthenticated(AuthData authData) { 335 | // the Google user is now authenticated with your Firebase app 336 | } 337 | 338 | @Override 339 | public void onAuthenticationError(FirebaseError firebaseError) { 340 | // there was an error 341 | } 342 | }); 343 | 344 | 345 | 346 | 347 |
348 | 349 |
350 | 351 |

Authentication

352 | 353 | 354 | 355 | // Listen for authentcated users 356 | ref.onAuth(function(authData) { 357 | if(authData) { 358 | console.log('User is logged in!'); 359 | } 360 | }); 361 | 362 | 363 | 364 | // Listen for authenticated users 365 | ref.observeAuthEventWithBlock { authData in 366 | if authData != nil { 367 | println("User is logged in! \(authData)") 368 | } 369 | } 370 | 371 | 372 | 373 | // Listen for authentcated users 374 | ref.addAuthStateListener(new Firebase.AuthStateListener() { 375 | @Override 376 | public void onAuthStateChanged(AuthData authData) { 377 | if (authData != null) { 378 | System.out.println("User is logged in!"); 379 | } 380 | } 381 | }); 382 | 383 | 384 | 385 | 386 |
387 | 388 |
389 | 390 |
391 | 392 |
393 |

Security

394 |

395 | By default everyone can write and read 396 |

397 |
398 | 						
399 | {
400 |   "rules": {
401 |     ".read": true,
402 |     ".write": true,
403 |   }
404 | }
405 | 						
406 | 					
407 |
408 | 409 |
410 |

411 | Everyone can read, but no one can write 412 |

413 |
414 | 						
415 | {
416 |   "rules": {
417 |     ".read": true,
418 |     ".write": false,
419 |   }
420 | }
421 | 						
422 | 					
423 |
424 | 425 |
426 |

427 | Secure specific parts of your Firebase 428 |

429 |
430 | 						
431 | {
432 |   "rules": {
433 |     ".read": true,
434 |     ".write": false,
435 |     "message": {
436 |       ".write": true 
437 |     }
438 |   }
439 | }
440 | 						
441 | 					
442 |
443 | 444 |
445 |

446 | Special variables to power authentication 447 |

448 |
449 | 						
450 | {
451 |   "rules": {
452 |     ".read": true,
453 |     "message": {
454 |       ".write": "auth !== null",
455 |       ".validate": "!data.exists()"
456 |     }
457 |   }
458 | }
459 | 						
460 | 					
461 |
462 | 463 | 464 |
465 |

Security

466 |

Use $wildcards as route parameters

467 |
468 | 						
469 | {
470 |   "rules": {
471 |     ".read": false,
472 |     ".write": false,
473 |     "users": {
474 |       "$userid": {
475 |         ".read": "true",
476 |         ".write": "auth.uid === $userid"
477 |       }
478 |     }
479 |   }
480 | }
481 | 						
482 | 					
483 | 484 |
485 | 486 |
487 | 488 |
489 | 490 |
491 |

Hosting

492 |

When you're ready to launch

493 |
494 |   						
495 | firebase init
496 | # select your firebase
497 | firebase deploy
498 | # party time
499 |   						
500 |   					
501 |
502 | 503 |
504 |

Hosting

505 |

Production-grade

506 |

Static Asset

507 |

Free SSL

508 |

CDN cached assets

509 |

One click rollbacks

510 |
511 | 512 |
513 |

(Optional) Live Coding

514 |
515 | 516 |
517 |

Tooling & Integration

518 |
519 | 520 | 521 |
522 |

Bindings

523 | 524 | 525 | 526 | 527 |
528 | 529 |
530 |

AngularFire

531 |

A chat app in AngularFire

532 |
533 | 						
534 | var app = app.module('app', ['firebase']);
535 | app.controller('ChatCtrl', function($scope, $firebaseArray) {
536 |   var ref = new Firebase('https://<your-firebase>.firebaseio.com/messages');
537 |   $scope.messages = $firebaseArray(ref);
538 | });
539 | 						
540 | 					
541 |
542 | 						
543 | <html>
544 |   <body ng-app="app">
545 |     <div ng-controller="ChatCtrl">
546 |       <p ng-repeat="message in messages">message.text</p>
547 |     </div>
548 |   </body>
549 | </html>
550 | 						
551 | 					
552 |
553 | 554 |
555 |

Geofire

556 |

Store and query items based on their geographic location in realtime

557 | 558 |
559 | 560 |
561 |

Well Documented

562 | 563 | 564 | 565 |
566 | 567 |
568 |

Firebase meta-data

569 |

570 | 170,000+ registered developers 571 |

572 |

573 | 1M concurrent users on Firebase sites 574 |

575 |

576 | Joined Google October, 2014 577 |

578 |
579 | 580 |
581 |

Firebase in Production

582 |
    583 |
  • 584 | Twitch.tv - Notifying users when realtime streams go live 585 |
  • 586 |
  • 587 | Citrix - Manages presence in GoTo Meeting 588 |
  • 589 |
  • 590 | CBS - Powers the chat for Big Brother's Live Stream 591 |
  • 592 |
  • 593 | Warby Parker - Powering pneumatic tubes 594 |
  • 595 |
596 |
597 | 598 |
599 |

Focus on your app

600 |

The user doesn't care about the backend

601 |

What matters is that your app is 602 | fast and enjoyable 603 |

604 |
605 | 606 |
607 |

Complete Platform

608 | 609 | 610 | 611 | 612 |
613 | 614 |
615 |

ASK ME QUESTIONS

616 |
YOUR_NAME / @YOUR_TWITTER_HANDLE
617 |
YOUR_JOB / @YOUR_COMPANY
618 |
619 | 620 |
621 |

Assets Used

622 | 623 | 632 | 633 |
634 | 635 |
636 | 637 |
638 | 639 | 640 | 641 | 642 | 643 | 672 | 673 | 693 | 694 | 695 | -------------------------------------------------------------------------------- /js/.gitignore: -------------------------------------------------------------------------------- 1 | reveal.min.js 2 | -------------------------------------------------------------------------------- /lib/css/zenburn.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Zenburn style from voldmar.ru (c) Vladimir Epifanov 4 | based on dark.css by Ivan Sagalaev 5 | 6 | */ 7 | 8 | pre code { 9 | display: block; padding: 0.5em; 10 | background: #3F3F3F; 11 | color: #DCDCDC; 12 | } 13 | 14 | pre .keyword, 15 | pre .tag, 16 | pre .css .class, 17 | pre .css .id, 18 | pre .lisp .title, 19 | pre .nginx .title, 20 | pre .request, 21 | pre .status, 22 | pre .clojure .attribute { 23 | color: #E3CEAB; 24 | } 25 | 26 | pre .django .template_tag, 27 | pre .django .variable, 28 | pre .django .filter .argument { 29 | color: #DCDCDC; 30 | } 31 | 32 | pre .number, 33 | pre .date { 34 | color: #8CD0D3; 35 | } 36 | 37 | pre .dos .envvar, 38 | pre .dos .stream, 39 | pre .variable, 40 | pre .apache .sqbracket { 41 | color: #EFDCBC; 42 | } 43 | 44 | pre .dos .flow, 45 | pre .diff .change, 46 | pre .python .exception, 47 | pre .python .built_in, 48 | pre .literal, 49 | pre .tex .special { 50 | color: #EFEFAF; 51 | } 52 | 53 | pre .diff .chunk, 54 | pre .subst { 55 | color: #8F8F8F; 56 | } 57 | 58 | pre .dos .keyword, 59 | pre .python .decorator, 60 | pre .title, 61 | pre .haskell .type, 62 | pre .diff .header, 63 | pre .ruby .class .parent, 64 | pre .apache .tag, 65 | pre .nginx .built_in, 66 | pre .tex .command, 67 | pre .prompt { 68 | color: #efef8f; 69 | } 70 | 71 | pre .dos .winutils, 72 | pre .ruby .symbol, 73 | pre .ruby .symbol .string, 74 | pre .ruby .string { 75 | color: #DCA3A3; 76 | } 77 | 78 | pre .diff .deletion, 79 | pre .string, 80 | pre .tag .value, 81 | pre .preprocessor, 82 | pre .built_in, 83 | pre .sql .aggregate, 84 | pre .javadoc, 85 | pre .smalltalk .class, 86 | pre .smalltalk .localvars, 87 | pre .smalltalk .array, 88 | pre .css .rules .value, 89 | pre .attr_selector, 90 | pre .pseudo, 91 | pre .apache .cbracket, 92 | pre .tex .formula { 93 | color: #CC9393; 94 | } 95 | 96 | pre .shebang, 97 | pre .diff .addition, 98 | pre .comment, 99 | pre .java .annotation, 100 | pre .template_comment, 101 | pre .pi, 102 | pre .doctype { 103 | color: #7F9F7F; 104 | } 105 | 106 | pre .coffeescript .javascript, 107 | pre .javascript .xml, 108 | pre .tex .formula, 109 | pre .xml .javascript, 110 | pre .xml .vbscript, 111 | pre .xml .css, 112 | pre .xml .cdata { 113 | opacity: 0.5; 114 | } -------------------------------------------------------------------------------- /lib/font/league_gothic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/lib/font/league_gothic-webfont.eot -------------------------------------------------------------------------------- /lib/font/league_gothic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/lib/font/league_gothic-webfont.ttf -------------------------------------------------------------------------------- /lib/font/league_gothic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/lib/font/league_gothic-webfont.woff -------------------------------------------------------------------------------- /lib/font/league_gothic_license: -------------------------------------------------------------------------------- 1 | SIL Open Font License (OFL) 2 | http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL 3 | -------------------------------------------------------------------------------- /lib/js/classList.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/ 2 | if(typeof document!=="undefined"&&!("classList" in document.createElement("a"))){(function(j){var a="classList",f="prototype",m=(j.HTMLElement||j.Element)[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p 3 | Copyright Tero Piirainen (tipiirai) 4 | License MIT / http://bit.ly/mit-license 5 | Version 0.96 6 | 7 | http://headjs.com 8 | */(function(a){function z(){d||(d=!0,s(e,function(a){p(a)}))}function y(c,d){var e=a.createElement("script");e.type="text/"+(c.type||"javascript"),e.src=c.src||c,e.async=!1,e.onreadystatechange=e.onload=function(){var a=e.readyState;!d.done&&(!a||/loaded|complete/.test(a))&&(d.done=!0,d())},(a.body||b).appendChild(e)}function x(a,b){if(a.state==o)return b&&b();if(a.state==n)return k.ready(a.name,b);if(a.state==m)return a.onpreload.push(function(){x(a,b)});a.state=n,y(a.url,function(){a.state=o,b&&b(),s(g[a.name],function(a){p(a)}),u()&&d&&s(g.ALL,function(a){p(a)})})}function w(a,b){a.state===undefined&&(a.state=m,a.onpreload=[],y({src:a.url,type:"cache"},function(){v(a)}))}function v(a){a.state=l,s(a.onpreload,function(a){a.call()})}function u(a){a=a||h;var b;for(var c in a){if(a.hasOwnProperty(c)&&a[c].state!=o)return!1;b=!0}return b}function t(a){return Object.prototype.toString.call(a)=="[object Function]"}function s(a,b){if(!!a){typeof a=="object"&&(a=[].slice.call(a));for(var c=0;c (https://www.firebase.com)", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/davideast/firebase-intro.git" 9 | }, 10 | "licenses": [ 11 | { 12 | "type": "MIT", 13 | "url": "https://github.com/hakimel/reveal.js/blob/master/LICENSE" 14 | } 15 | ], 16 | "dependencies": { 17 | "underscore": "~1.5.1", 18 | "express": "~2.5.9", 19 | "mustache": "~0.7.2", 20 | "socket.io": "~0.9.13" 21 | }, 22 | "devDependencies": { 23 | "grunt-contrib-qunit": "~0.2.2", 24 | "grunt-contrib-jshint": "~0.6.4", 25 | "grunt-contrib-cssmin": "~0.4.1", 26 | "grunt-contrib-uglify": "~0.2.4", 27 | "grunt-contrib-watch": "~0.5.3", 28 | "grunt-contrib-sass": "~0.5.0", 29 | "grunt-contrib-connect": "~0.4.1", 30 | "grunt-zip": "~0.7.0", 31 | "grunt": "~0.4.0" 32 | }, 33 | "private": true 34 | } 35 | -------------------------------------------------------------------------------- /plugin/markdown/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Markdown Demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 |
26 | 36 |
37 | 38 | 39 |
40 | 54 |
55 | 56 | 57 |
58 | 69 |
70 | 71 | 72 |
73 | 77 |
78 | 79 | 80 |
81 | 86 |
87 | 88 | 89 |
90 | 100 |
101 | 102 |
103 |
104 | 105 | 106 | 107 | 108 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /plugin/markdown/example.md: -------------------------------------------------------------------------------- 1 | # Markdown Demo 2 | 3 | 4 | 5 | ## External 1.1 6 | 7 | Content 1.1 8 | 9 | Note: This will only appear in the speaker notes window. 10 | 11 | 12 | ## External 1.2 13 | 14 | Content 1.2 15 | 16 | 17 | 18 | ## External 2 19 | 20 | Content 2.1 21 | 22 | 23 | 24 | ## External 3.1 25 | 26 | Content 3.1 27 | 28 | 29 | ## External 3.2 30 | 31 | Content 3.2 32 | -------------------------------------------------------------------------------- /plugin/markdown/markdown.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The reveal.js markdown plugin. Handles parsing of 3 | * markdown inside of presentations as well as loading 4 | * of external markdown documents. 5 | */ 6 | (function( root, factory ) { 7 | if( typeof exports === 'object' ) { 8 | module.exports = factory( require( './marked' ) ); 9 | } 10 | else { 11 | // Browser globals (root is window) 12 | root.RevealMarkdown = factory( root.marked ); 13 | root.RevealMarkdown.initialize(); 14 | } 15 | }( this, function( marked ) { 16 | 17 | if( typeof marked === 'undefined' ) { 18 | throw 'The reveal.js Markdown plugin requires marked to be loaded'; 19 | } 20 | 21 | if( typeof hljs !== 'undefined' ) { 22 | marked.setOptions({ 23 | highlight: function( lang, code ) { 24 | return hljs.highlightAuto( lang, code ).value; 25 | } 26 | }); 27 | } 28 | 29 | var DEFAULT_SLIDE_SEPARATOR = '^\n---\n$', 30 | DEFAULT_NOTES_SEPARATOR = 'note:', 31 | DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$', 32 | DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$'; 33 | 34 | 35 | /** 36 | * Retrieves the markdown contents of a slide section 37 | * element. Normalizes leading tabs/whitespace. 38 | */ 39 | function getMarkdownFromSlide( section ) { 40 | 41 | var template = section.querySelector( 'script' ); 42 | 43 | // strip leading whitespace so it isn't evaluated as code 44 | var text = ( template || section ).textContent; 45 | 46 | var leadingWs = text.match( /^\n?(\s*)/ )[1].length, 47 | leadingTabs = text.match( /^\n?(\t*)/ )[1].length; 48 | 49 | if( leadingTabs > 0 ) { 50 | text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' ); 51 | } 52 | else if( leadingWs > 1 ) { 53 | text = text.replace( new RegExp('\\n? {' + leadingWs + '}','g'), '\n' ); 54 | } 55 | 56 | return text; 57 | 58 | } 59 | 60 | /** 61 | * Given a markdown slide section element, this will 62 | * return all arguments that aren't related to markdown 63 | * parsing. Used to forward any other user-defined arguments 64 | * to the output markdown slide. 65 | */ 66 | function getForwardedAttributes( section ) { 67 | 68 | var attributes = section.attributes; 69 | var result = []; 70 | 71 | for( var i = 0, len = attributes.length; i < len; i++ ) { 72 | var name = attributes[i].name, 73 | value = attributes[i].value; 74 | 75 | // disregard attributes that are used for markdown loading/parsing 76 | if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue; 77 | 78 | if( value ) { 79 | result.push( name + '=' + value ); 80 | } 81 | else { 82 | result.push( name ); 83 | } 84 | } 85 | 86 | return result.join( ' ' ); 87 | 88 | } 89 | 90 | /** 91 | * Inspects the given options and fills out default 92 | * values for what's not defined. 93 | */ 94 | function getSlidifyOptions( options ) { 95 | 96 | options = options || {}; 97 | options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR; 98 | options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR; 99 | options.attributes = options.attributes || ''; 100 | 101 | return options; 102 | 103 | } 104 | 105 | /** 106 | * Helper function for constructing a markdown slide. 107 | */ 108 | function createMarkdownSlide( content, options ) { 109 | 110 | options = getSlidifyOptions( options ); 111 | 112 | var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) ); 113 | 114 | if( notesMatch.length === 2 ) { 115 | content = notesMatch[0] + ''; 116 | } 117 | 118 | return ''; 119 | 120 | } 121 | 122 | /** 123 | * Parses a data string into multiple slides based 124 | * on the passed in separator arguments. 125 | */ 126 | function slidify( markdown, options ) { 127 | 128 | options = getSlidifyOptions( options ); 129 | 130 | var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ), 131 | horizontalSeparatorRegex = new RegExp( options.separator ); 132 | 133 | var matches, 134 | lastIndex = 0, 135 | isHorizontal, 136 | wasHorizontal = true, 137 | content, 138 | sectionStack = []; 139 | 140 | // iterate until all blocks between separators are stacked up 141 | while( matches = separatorRegex.exec( markdown ) ) { 142 | notes = null; 143 | 144 | // determine direction (horizontal by default) 145 | isHorizontal = horizontalSeparatorRegex.test( matches[0] ); 146 | 147 | if( !isHorizontal && wasHorizontal ) { 148 | // create vertical stack 149 | sectionStack.push( [] ); 150 | } 151 | 152 | // pluck slide content from markdown input 153 | content = markdown.substring( lastIndex, matches.index ); 154 | 155 | if( isHorizontal && wasHorizontal ) { 156 | // add to horizontal stack 157 | sectionStack.push( content ); 158 | } 159 | else { 160 | // add to vertical stack 161 | sectionStack[sectionStack.length-1].push( content ); 162 | } 163 | 164 | lastIndex = separatorRegex.lastIndex; 165 | wasHorizontal = isHorizontal; 166 | } 167 | 168 | // add the remaining slide 169 | ( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) ); 170 | 171 | var markdownSections = ''; 172 | 173 | // flatten the hierarchical stack, and insert
tags 174 | for( var i = 0, len = sectionStack.length; i < len; i++ ) { 175 | // vertical 176 | if( sectionStack[i] instanceof Array ) { 177 | markdownSections += '
'; 178 | 179 | sectionStack[i].forEach( function( child ) { 180 | markdownSections += '
' + createMarkdownSlide( child, options ) + '
'; 181 | } ); 182 | 183 | markdownSections += '
'; 184 | } 185 | else { 186 | markdownSections += '
' + createMarkdownSlide( sectionStack[i], options ) + '
'; 187 | } 188 | } 189 | 190 | return markdownSections; 191 | 192 | } 193 | 194 | /** 195 | * Parses any current data-markdown slides, splits 196 | * multi-slide markdown into separate sections and 197 | * handles loading of external markdown. 198 | */ 199 | function processSlides() { 200 | 201 | var sections = document.querySelectorAll( '[data-markdown]'), 202 | section; 203 | 204 | for( var i = 0, len = sections.length; i < len; i++ ) { 205 | 206 | section = sections[i]; 207 | 208 | if( section.getAttribute( 'data-markdown' ).length ) { 209 | 210 | var xhr = new XMLHttpRequest(), 211 | url = section.getAttribute( 'data-markdown' ); 212 | 213 | datacharset = section.getAttribute( 'data-charset' ); 214 | 215 | // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes 216 | if( datacharset != null && datacharset != '' ) { 217 | xhr.overrideMimeType( 'text/html; charset=' + datacharset ); 218 | } 219 | 220 | xhr.onreadystatechange = function() { 221 | if( xhr.readyState === 4 ) { 222 | if ( xhr.status >= 200 && xhr.status < 300 ) { 223 | 224 | section.outerHTML = slidify( xhr.responseText, { 225 | separator: section.getAttribute( 'data-separator' ), 226 | verticalSeparator: section.getAttribute( 'data-vertical' ), 227 | notesSeparator: section.getAttribute( 'data-notes' ), 228 | attributes: getForwardedAttributes( section ) 229 | }); 230 | 231 | } 232 | else { 233 | 234 | section.outerHTML = '
' + 235 | 'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' + 236 | 'Check your browser\'s JavaScript console for more details.' + 237 | '

Remember that you need to serve the presentation HTML from a HTTP server.

' + 238 | '
'; 239 | 240 | } 241 | } 242 | }; 243 | 244 | xhr.open( 'GET', url, false ); 245 | 246 | try { 247 | xhr.send(); 248 | } 249 | catch ( e ) { 250 | alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e ); 251 | } 252 | 253 | } 254 | else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-vertical' ) || section.getAttribute( 'data-notes' ) ) { 255 | 256 | section.outerHTML = slidify( getMarkdownFromSlide( section ), { 257 | separator: section.getAttribute( 'data-separator' ), 258 | verticalSeparator: section.getAttribute( 'data-vertical' ), 259 | notesSeparator: section.getAttribute( 'data-notes' ), 260 | attributes: getForwardedAttributes( section ) 261 | }); 262 | 263 | } 264 | else { 265 | section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) ); 266 | } 267 | } 268 | 269 | } 270 | 271 | /** 272 | * Check if a node value has the attributes pattern. 273 | * If yes, extract it and add that value as one or several attributes 274 | * the the terget element. 275 | * 276 | * You need Cache Killer on Chrome to see the effect on any FOM transformation 277 | * directly on refresh (F5) 278 | * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277 279 | */ 280 | function addAttributeInElement( node, elementTarget, separator ) { 281 | 282 | var mardownClassesInElementsRegex = new RegExp( separator, 'mg' ); 283 | var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' ); 284 | var nodeValue = node.nodeValue; 285 | if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) { 286 | 287 | var classes = matches[1]; 288 | nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex ); 289 | node.nodeValue = nodeValue; 290 | while( matchesClass = mardownClassRegex.exec( classes ) ) { 291 | elementTarget.setAttribute( matchesClass[1], matchesClass[2] ); 292 | } 293 | return true; 294 | } 295 | return false; 296 | } 297 | 298 | /** 299 | * Add attributes to the parent element of a text node, 300 | * or the element of an attribute node. 301 | */ 302 | function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) { 303 | 304 | if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) { 305 | previousParentElement = element; 306 | for( var i = 0; i < element.childNodes.length; i++ ) { 307 | childElement = element.childNodes[i]; 308 | if ( i > 0 ) { 309 | j = i - 1; 310 | while ( j >= 0 ) { 311 | aPreviousChildElement = element.childNodes[j]; 312 | if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) { 313 | previousParentElement = aPreviousChildElement; 314 | break; 315 | } 316 | j = j - 1; 317 | } 318 | } 319 | parentSection = section; 320 | if( childElement.nodeName == "section" ) { 321 | parentSection = childElement ; 322 | previousParentElement = childElement ; 323 | } 324 | if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) { 325 | addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes ); 326 | } 327 | } 328 | } 329 | 330 | if ( element.nodeType == Node.COMMENT_NODE ) { 331 | if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) { 332 | addAttributeInElement( element, section, separatorSectionAttributes ); 333 | } 334 | } 335 | } 336 | 337 | /** 338 | * Converts any current data-markdown slides in the 339 | * DOM to HTML. 340 | */ 341 | function convertSlides() { 342 | 343 | var sections = document.querySelectorAll( '[data-markdown]'); 344 | 345 | for( var i = 0, len = sections.length; i < len; i++ ) { 346 | 347 | var section = sections[i]; 348 | 349 | // Only parse the same slide once 350 | if( !section.getAttribute( 'data-markdown-parsed' ) ) { 351 | 352 | section.setAttribute( 'data-markdown-parsed', true ) 353 | 354 | var notes = section.querySelector( 'aside.notes' ); 355 | var markdown = getMarkdownFromSlide( section ); 356 | 357 | section.innerHTML = marked( markdown ); 358 | addAttributes( section, section, null, section.getAttribute( 'data-element-attributes' ) || 359 | section.parentNode.getAttribute( 'data-element-attributes' ) || 360 | DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR, 361 | section.getAttribute( 'data-attributes' ) || 362 | section.parentNode.getAttribute( 'data-attributes' ) || 363 | DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR); 364 | 365 | // If there were notes, we need to re-add them after 366 | // having overwritten the section's HTML 367 | if( notes ) { 368 | section.appendChild( notes ); 369 | } 370 | 371 | } 372 | 373 | } 374 | 375 | } 376 | 377 | // API 378 | return { 379 | 380 | initialize: function() { 381 | processSlides(); 382 | convertSlides(); 383 | }, 384 | 385 | // TODO: Do these belong in the API? 386 | processSlides: processSlides, 387 | convertSlides: convertSlides, 388 | slidify: slidify 389 | 390 | }; 391 | 392 | })); 393 | -------------------------------------------------------------------------------- /plugin/markdown/marked.js: -------------------------------------------------------------------------------- 1 | /** 2 | * marked - a markdown parser 3 | * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed) 4 | * https://github.com/chjj/marked 5 | */ 6 | 7 | (function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){3,} *\n*/,blockquote:/^( *>[^\n]+(\n[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,def:/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, 8 | text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr",/\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b";block.html=replace(block.html)("comment",/\x3c!--[\s\S]*?--\x3e/)("closed", 9 | /<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,paragraph:/^/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1", 10 | "\\2")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm)if(this.options.tables)this.rules=block.tables;else this.rules=block.gfm}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)}; 11 | Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1)this.tokens.push({type:"space"})}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm, 12 | "");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g, 13 | "").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i ?/gm,"");this.token(cap,top);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length); 15 | bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i1&&b.length>1)){src=cap.slice(i+ 16 | 1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item[item.length-1]==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:cap[1]==="pre"||cap[1]==="script",text:cap[0]});continue}if(top&& 17 | (cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^\x3c!--[\s\S]*?--\x3e|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, 20 | code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, 21 | em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal; 22 | if(!this.links)throw new Error("Tokens array requires a `links` property.");if(this.options.gfm)if(this.options.breaks)this.rules=inline.breaks;else this.rules=inline.gfm;else if(this.options.pedantic)this.rules=inline.pedantic}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length); 23 | out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1][6]===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=''+text+"";continue}if(cap=this.rules.url.exec(src)){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=''+text+"";continue}if(cap=this.rules.tag.exec(src)){src=src.substring(cap[0].length); 24 | out+=this.options.sanitize?escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);out+=this.outputLink(cap,{href:cap[2],title:cap[3]});continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0][0];src=cap[0].substring(1)+src;continue}out+=this.outputLink(cap,link);continue}if(cap=this.rules.strong.exec(src)){src= 25 | src.substring(cap[0].length);out+=""+this.output(cap[2]||cap[1])+"";continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=""+this.output(cap[2]||cap[1])+"";continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=""+escape(cap[2],true)+"";continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+="
";continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=""+ 26 | this.output(cap[1])+"";continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=escape(cap[0]);continue}if(src)throw new Error("Infinite loop on byte: "+src.charCodeAt(0));}return out};InlineLexer.prototype.outputLink=function(cap,link){if(cap[0][0]!=="!")return'"+this.output(cap[1])+"";else return''+escape(cap[1])+'"};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/--/g,"\u2014").replace(/'([^']*)'/g,"\u2018$1\u2019").replace(/"([^"]*)"/g,"\u201c$1\u201d").replace(/\.{3}/g,"\u2026")};InlineLexer.prototype.mangle=function(text){var out="",l=text.length,i=0,ch;for(;i0.5)ch="x"+ch.toString(16);out+="&#"+ch+";"}return out};function Parser(options){this.tokens=[];this.token=null; 28 | this.options=options||marked.defaults}Parser.parse=function(src,options){var parser=new Parser(options);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options);this.tokens=src.reverse();var out="";while(this.next())out+=this.tok();return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text; 29 | while(this.peek().type==="text")body+="\n"+this.next().text;return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case "space":return"";case "hr":return"
\n";case "heading":return""+this.inline.output(this.token.text)+"\n";case "code":if(this.options.highlight){var code=this.options.highlight(this.token.text,this.token.lang);if(code!=null&&code!==this.token.text){this.token.escaped=true;this.token.text=code}}if(!this.token.escaped)this.token.text= 30 | escape(this.token.text,true);return"
"+this.token.text+"
\n";case "table":var body="",heading,i,row,cell,j;body+="\n\n";for(i=0;i'+heading+"\n":""+heading+"\n"}body+="\n\n";body+="\n";for(i=0;i'+cell+"\n":""+cell+"\n"}body+="\n"}body+="\n";return"\n"+body+"
\n";case "blockquote_start":var body="";while(this.next().type!=="blockquote_end")body+=this.tok();return"
\n"+body+"
\n";case "list_start":var type=this.token.ordered?"ol":"ul",body="";while(this.next().type!=="list_end")body+= 32 | this.tok();return"<"+type+">\n"+body+"\n";case "list_item_start":var body="";while(this.next().type!=="list_item_end")body+=this.token.type==="text"?this.parseText():this.tok();return"
  • "+body+"
  • \n";case "loose_item_start":var body="";while(this.next().type!=="list_item_end")body+=this.tok();return"
  • "+body+"
  • \n";case "html":return!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;case "paragraph":return"

    "+this.inline.output(this.token.text)+ 33 | "

    \n";case "text":return"

    "+this.parseText()+"

    \n"}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i= 34 | 1,target,key;for(;iAn error occured:

    "+escape(e.message+"",true)+"
    ";throw e;}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:""};marked.Parser=Parser;marked.parser=Parser.parse;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output; 37 | marked.parse=marked;if(typeof exports==="object")module.exports=marked;else if(typeof define==="function"&&define.amd)define(function(){return marked});else this.marked=marked}).call(function(){return this||(typeof window!=="undefined"?window:global)}()); 38 | -------------------------------------------------------------------------------- /plugin/math/math.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin which enables rendering of math equations inside 3 | * of reveal.js slides. Essentially a thin wrapper for MathJax. 4 | * 5 | * @author Hakim El Hattab 6 | */ 7 | var RevealMath = window.RevealMath || (function(){ 8 | 9 | var options = Reveal.getConfig().math || {}; 10 | options.mathjax = options.mathjax || 'http://cdn.mathjax.org/mathjax/latest/MathJax.js'; 11 | options.config = options.config || 'TeX-AMS_HTML-full'; 12 | 13 | loadScript( options.mathjax + '?config=' + options.config, function() { 14 | 15 | MathJax.Hub.Config({ 16 | messageStyle: 'none', 17 | tex2jax: { inlineMath: [['$','$'],['\\(','\\)']] }, 18 | skipStartupTypeset: true 19 | }); 20 | 21 | // Typeset followed by an immediate reveal.js layout since 22 | // the typesetting process could affect slide height 23 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] ); 24 | MathJax.Hub.Queue( Reveal.layout ); 25 | 26 | // Reprocess equations in slides when they turn visible 27 | Reveal.addEventListener( 'slidechanged', function( event ) { 28 | 29 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] ); 30 | 31 | } ); 32 | 33 | } ); 34 | 35 | function loadScript( url, callback ) { 36 | 37 | var head = document.querySelector( 'head' ); 38 | var script = document.createElement( 'script' ); 39 | script.type = 'text/javascript'; 40 | script.src = url; 41 | 42 | // Wrapper for callback to make sure it only fires once 43 | var finish = function() { 44 | if( typeof callback === 'function' ) { 45 | callback.call(); 46 | callback = null; 47 | } 48 | } 49 | 50 | script.onload = finish; 51 | 52 | // IE 53 | script.onreadystatechange = function() { 54 | if ( this.readyState === 'loaded' ) { 55 | finish(); 56 | } 57 | } 58 | 59 | // Normal browsers 60 | head.appendChild( script ); 61 | 62 | } 63 | 64 | })(); 65 | -------------------------------------------------------------------------------- /plugin/multiplex/client.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var multiplex = Reveal.getConfig().multiplex; 3 | var socketId = multiplex.id; 4 | var socket = io.connect(multiplex.url); 5 | 6 | socket.on(multiplex.id, function(data) { 7 | // ignore data from sockets that aren't ours 8 | if (data.socketId !== socketId) { return; } 9 | if( window.location.host === 'localhost:1947' ) return; 10 | 11 | Reveal.slide(data.indexh, data.indexv, data.indexf, 'remote'); 12 | }); 13 | }()); 14 | -------------------------------------------------------------------------------- /plugin/multiplex/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var fs = require('fs'); 3 | var io = require('socket.io'); 4 | var crypto = require('crypto'); 5 | 6 | var app = express.createServer(); 7 | var staticDir = express.static; 8 | 9 | io = io.listen(app); 10 | 11 | var opts = { 12 | port: 1948, 13 | baseDir : __dirname + '/../../' 14 | }; 15 | 16 | io.sockets.on('connection', function(socket) { 17 | socket.on('slidechanged', function(slideData) { 18 | if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return; 19 | if (createHash(slideData.secret) === slideData.socketId) { 20 | slideData.secret = null; 21 | socket.broadcast.emit(slideData.socketId, slideData); 22 | }; 23 | }); 24 | }); 25 | 26 | app.configure(function() { 27 | [ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) { 28 | app.use('/' + dir, staticDir(opts.baseDir + dir)); 29 | }); 30 | }); 31 | 32 | app.get("/", function(req, res) { 33 | res.writeHead(200, {'Content-Type': 'text/html'}); 34 | fs.createReadStream(opts.baseDir + '/index.html').pipe(res); 35 | }); 36 | 37 | app.get("/token", function(req,res) { 38 | var ts = new Date().getTime(); 39 | var rand = Math.floor(Math.random()*9999999); 40 | var secret = ts.toString() + rand.toString(); 41 | res.send({secret: secret, socketId: createHash(secret)}); 42 | }); 43 | 44 | var createHash = function(secret) { 45 | var cipher = crypto.createCipher('blowfish', secret); 46 | return(cipher.final('hex')); 47 | }; 48 | 49 | // Actually listen 50 | app.listen(opts.port || null); 51 | 52 | var brown = '\033[33m', 53 | green = '\033[32m', 54 | reset = '\033[0m'; 55 | 56 | console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset ); -------------------------------------------------------------------------------- /plugin/multiplex/master.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // Don't emit events from inside of notes windows 3 | if ( window.location.search.match( /receiver/gi ) ) { return; } 4 | 5 | var multiplex = Reveal.getConfig().multiplex; 6 | 7 | var socket = io.connect(multiplex.url); 8 | 9 | var notify = function( slideElement, indexh, indexv, origin ) { 10 | if( typeof origin === 'undefined' && origin !== 'remote' ) { 11 | var nextindexh; 12 | var nextindexv; 13 | 14 | var fragmentindex = Reveal.getIndices().f; 15 | if (typeof fragmentindex == 'undefined') { 16 | fragmentindex = 0; 17 | } 18 | 19 | if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') { 20 | nextindexh = indexh; 21 | nextindexv = indexv + 1; 22 | } else { 23 | nextindexh = indexh + 1; 24 | nextindexv = 0; 25 | } 26 | 27 | var slideData = { 28 | indexh : indexh, 29 | indexv : indexv, 30 | indexf : fragmentindex, 31 | nextindexh : nextindexh, 32 | nextindexv : nextindexv, 33 | secret: multiplex.secret, 34 | socketId : multiplex.id 35 | }; 36 | 37 | socket.emit('slidechanged', slideData); 38 | } 39 | } 40 | 41 | Reveal.addEventListener( 'slidechanged', function( event ) { 42 | notify( event.currentSlide, event.indexh, event.indexv, event.origin ); 43 | } ); 44 | 45 | var fragmentNotify = function( event ) { 46 | notify( Reveal.getCurrentSlide(), Reveal.getIndices().h, Reveal.getIndices().v, event.origin ); 47 | }; 48 | 49 | Reveal.addEventListener( 'fragmentshown', fragmentNotify ); 50 | Reveal.addEventListener( 'fragmenthidden', fragmentNotify ); 51 | }()); -------------------------------------------------------------------------------- /plugin/notes-server/client.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // don't emit events from inside the previews themselves 3 | if ( window.location.search.match( /receiver/gi ) ) { return; } 4 | 5 | var socket = io.connect(window.location.origin); 6 | var socketId = Math.random().toString().slice(2); 7 | 8 | console.log('View slide notes at ' + window.location.origin + '/notes/' + socketId); 9 | window.open(window.location.origin + '/notes/' + socketId, 'notes-' + socketId); 10 | 11 | // Fires when a fragment is shown 12 | Reveal.addEventListener( 'fragmentshown', function( event ) { 13 | var fragmentData = { 14 | fragment : 'next', 15 | socketId : socketId 16 | }; 17 | socket.emit('fragmentchanged', fragmentData); 18 | } ); 19 | 20 | // Fires when a fragment is hidden 21 | Reveal.addEventListener( 'fragmenthidden', function( event ) { 22 | var fragmentData = { 23 | fragment : 'previous', 24 | socketId : socketId 25 | }; 26 | socket.emit('fragmentchanged', fragmentData); 27 | } ); 28 | 29 | // Fires when slide is changed 30 | Reveal.addEventListener( 'slidechanged', function( event ) { 31 | var nextindexh; 32 | var nextindexv; 33 | var slideElement = event.currentSlide; 34 | 35 | if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') { 36 | nextindexh = event.indexh; 37 | nextindexv = event.indexv + 1; 38 | } else { 39 | nextindexh = event.indexh + 1; 40 | nextindexv = 0; 41 | } 42 | 43 | var notes = slideElement.querySelector('aside.notes'); 44 | var slideData = { 45 | notes : notes ? notes.innerHTML : '', 46 | indexh : event.indexh, 47 | indexv : event.indexv, 48 | nextindexh : nextindexh, 49 | nextindexv : nextindexv, 50 | socketId : socketId, 51 | markdown : notes ? typeof notes.getAttribute('data-markdown') === 'string' : false 52 | 53 | }; 54 | 55 | socket.emit('slidechanged', slideData); 56 | } ); 57 | }()); 58 | -------------------------------------------------------------------------------- /plugin/notes-server/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var fs = require('fs'); 3 | var io = require('socket.io'); 4 | var _ = require('underscore'); 5 | var Mustache = require('mustache'); 6 | 7 | var app = express.createServer(); 8 | var staticDir = express.static; 9 | 10 | io = io.listen(app); 11 | 12 | var opts = { 13 | port : 1947, 14 | baseDir : __dirname + '/../../' 15 | }; 16 | 17 | io.sockets.on('connection', function(socket) { 18 | socket.on('slidechanged', function(slideData) { 19 | socket.broadcast.emit('slidedata', slideData); 20 | }); 21 | socket.on('fragmentchanged', function(fragmentData) { 22 | socket.broadcast.emit('fragmentdata', fragmentData); 23 | }); 24 | }); 25 | 26 | app.configure(function() { 27 | [ 'css', 'js', 'images', 'plugin', 'lib' ].forEach(function(dir) { 28 | app.use('/' + dir, staticDir(opts.baseDir + dir)); 29 | }); 30 | }); 31 | 32 | app.get("/", function(req, res) { 33 | res.writeHead(200, {'Content-Type': 'text/html'}); 34 | fs.createReadStream(opts.baseDir + '/index.html').pipe(res); 35 | }); 36 | 37 | app.get("/notes/:socketId", function(req, res) { 38 | 39 | fs.readFile(opts.baseDir + 'plugin/notes-server/notes.html', function(err, data) { 40 | res.send(Mustache.to_html(data.toString(), { 41 | socketId : req.params.socketId 42 | })); 43 | }); 44 | // fs.createReadStream(opts.baseDir + 'notes-server/notes.html').pipe(res); 45 | }); 46 | 47 | // Actually listen 48 | app.listen(opts.port || null); 49 | 50 | var brown = '\033[33m', 51 | green = '\033[32m', 52 | reset = '\033[0m'; 53 | 54 | var slidesLocation = "http://localhost" + ( opts.port ? ( ':' + opts.port ) : '' ); 55 | 56 | console.log( brown + "reveal.js - Speaker Notes" + reset ); 57 | console.log( "1. Open the slides at " + green + slidesLocation + reset ); 58 | console.log( "2. Click on the link your JS console to go to the notes page" ); 59 | console.log( "3. Advance through your slides and your notes will advance automatically" ); 60 | -------------------------------------------------------------------------------- /plugin/notes-server/notes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | reveal.js - Slide Notes 9 | 10 | 90 | 91 | 92 | 93 | 94 |
    95 | 96 |
    97 | 98 |
    99 | 100 | UPCOMING: 101 |
    102 |
    103 | 104 | 105 | 106 | 107 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /plugin/notes/notes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | reveal.js - Slide Notes 7 | 8 | 137 | 138 | 139 | 140 | 141 | 148 | 149 |
    150 | 151 |
    152 | 153 |
    154 | 155 | UPCOMING: 156 |
    157 | 158 |
    159 |
    160 |

    Time

    161 | 0:00:00 AM 162 |
    163 |
    164 |

    Elapsed

    165 | 00:00:00 166 |
    167 |
    168 | 169 |
    170 | 171 | 172 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /plugin/notes/notes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles opening of and synchronization with the reveal.js 3 | * notes window. 4 | */ 5 | var RevealNotes = (function() { 6 | 7 | function openNotes() { 8 | var jsFileLocation = document.querySelector('script[src$="notes.js"]').src; // this js file path 9 | jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, ''); // the js folder path 10 | var notesPopup = window.open( jsFileLocation + 'notes.html', 'reveal.js - Notes', 'width=1120,height=850' ); 11 | 12 | // Fires when slide is changed 13 | Reveal.addEventListener( 'slidechanged', post ); 14 | 15 | // Fires when a fragment is shown 16 | Reveal.addEventListener( 'fragmentshown', post ); 17 | 18 | // Fires when a fragment is hidden 19 | Reveal.addEventListener( 'fragmenthidden', post ); 20 | 21 | /** 22 | * Posts the current slide data to the notes window 23 | */ 24 | function post() { 25 | var slideElement = Reveal.getCurrentSlide(), 26 | slideIndices = Reveal.getIndices(), 27 | messageData; 28 | 29 | var notes = slideElement.querySelector( 'aside.notes' ), 30 | nextindexh, 31 | nextindexv; 32 | 33 | if( slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION' ) { 34 | nextindexh = slideIndices.h; 35 | nextindexv = slideIndices.v + 1; 36 | } else { 37 | nextindexh = slideIndices.h + 1; 38 | nextindexv = 0; 39 | } 40 | 41 | messageData = { 42 | notes : notes ? notes.innerHTML : '', 43 | indexh : slideIndices.h, 44 | indexv : slideIndices.v, 45 | indexf : slideIndices.f, 46 | nextindexh : nextindexh, 47 | nextindexv : nextindexv, 48 | markdown : notes ? typeof notes.getAttribute( 'data-markdown' ) === 'string' : false 49 | }; 50 | 51 | notesPopup.postMessage( JSON.stringify( messageData ), '*' ); 52 | } 53 | 54 | // Navigate to the current slide when the notes are loaded 55 | notesPopup.addEventListener( 'load', function( event ) { 56 | post(); 57 | }, false ); 58 | } 59 | 60 | // If the there's a 'notes' query set, open directly 61 | if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) { 62 | openNotes(); 63 | } 64 | 65 | // Open the notes when the 's' key is hit 66 | document.addEventListener( 'keydown', function( event ) { 67 | // Disregard the event if the target is editable or a 68 | // modifier is present 69 | if ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return; 70 | 71 | if( event.keyCode === 83 ) { 72 | event.preventDefault(); 73 | openNotes(); 74 | } 75 | }, false ); 76 | 77 | return { open: openNotes }; 78 | })(); 79 | -------------------------------------------------------------------------------- /plugin/postmessage/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 | 8 | 9 | 10 |
    11 | 12 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /plugin/postmessage/postmessage.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | simple postmessage plugin 4 | 5 | Useful when a reveal slideshow is inside an iframe. 6 | It allows to call reveal methods from outside. 7 | 8 | Example: 9 | var reveal = window.frames[0]; 10 | 11 | // Reveal.prev(); 12 | reveal.postMessage(JSON.stringify({method: 'prev', args: []}), '*'); 13 | // Reveal.next(); 14 | reveal.postMessage(JSON.stringify({method: 'next', args: []}), '*'); 15 | // Reveal.slide(2, 2); 16 | reveal.postMessage(JSON.stringify({method: 'slide', args: [2,2]}), '*'); 17 | 18 | Add to the slideshow: 19 | 20 | dependencies: [ 21 | ... 22 | { src: 'plugin/postmessage/postmessage.js', async: true, condition: function() { return !!document.body.classList; } } 23 | ] 24 | 25 | */ 26 | 27 | (function (){ 28 | 29 | window.addEventListener( "message", function ( event ) { 30 | var data = JSON.parse( event.data ), 31 | method = data.method, 32 | args = data.args; 33 | 34 | if( typeof Reveal[method] === 'function' ) { 35 | Reveal[method].apply( Reveal, data.args ); 36 | } 37 | }, false); 38 | 39 | }()); 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /plugin/print-pdf/print-pdf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * phantomjs script for printing presentations to PDF. 3 | * 4 | * Example: 5 | * phantomjs print-pdf.js "http://lab.hakim.se/reveal-js?print-pdf" reveal-demo.pdf 6 | * 7 | * By Manuel Bieh (https://github.com/manuelbieh) 8 | */ 9 | 10 | // html2pdf.js 11 | var page = new WebPage(); 12 | var system = require( 'system' ); 13 | 14 | page.viewportSize = { 15 | width: 1024, 16 | height: 768 17 | }; 18 | 19 | page.paperSize = { 20 | format: 'letter', 21 | orientation: 'landscape', 22 | margin: { 23 | left: '0', 24 | right: '0', 25 | top: '0', 26 | bottom: '0' 27 | } 28 | }; 29 | 30 | var revealFile = system.args[1] || 'index.html?print-pdf'; 31 | var slideFile = system.args[2] || 'slides.pdf'; 32 | 33 | if( slideFile.match( /\.pdf$/gi ) === null ) { 34 | slideFile += '.pdf'; 35 | } 36 | 37 | console.log( 'Printing PDF...' ); 38 | 39 | page.open( revealFile, function( status ) { 40 | console.log( 'Printed succesfully' ); 41 | page.render( slideFile ); 42 | phantom.exit(); 43 | } ); 44 | 45 | -------------------------------------------------------------------------------- /plugin/remotes/remotes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Touch-based remote controller for your presentation courtesy 3 | * of the folks at http://remotes.io 4 | */ 5 | 6 | (function(window){ 7 | 8 | /** 9 | * Detects if we are dealing with a touch enabled device (with some false positives) 10 | * Borrowed from modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touch.js 11 | */ 12 | var hasTouch = (function(){ 13 | return ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; 14 | })(); 15 | 16 | /** 17 | * Detects if notes are enable and the current page is opened inside an /iframe 18 | * this prevents loading Remotes.io several times 19 | */ 20 | var isNotesAndIframe = (function(){ 21 | return window.RevealNotes && !(self == top); 22 | })(); 23 | 24 | if(!hasTouch && !isNotesAndIframe){ 25 | head.ready( 'remotes.ne.min.js', function() { 26 | new Remotes("preview") 27 | .on("swipe-left", function(e){ Reveal.right(); }) 28 | .on("swipe-right", function(e){ Reveal.left(); }) 29 | .on("swipe-up", function(e){ Reveal.down(); }) 30 | .on("swipe-down", function(e){ Reveal.up(); }) 31 | .on("tap", function(e){ Reveal.next(); }) 32 | .on("zoom-out", function(e){ Reveal.toggleOverview(true); }) 33 | .on("zoom-in", function(e){ Reveal.toggleOverview(false); }) 34 | ; 35 | } ); 36 | 37 | head.js('https://hakim-static.s3.amazonaws.com/reveal-js/remotes.ne.min.js'); 38 | } 39 | })(window); -------------------------------------------------------------------------------- /plugin/search/search.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Handles finding a text string anywhere in the slides and showing the next occurrence to the user 3 | * by navigatating to that slide and highlighting it. 4 | * 5 | * By Jon Snyder , February 2013 6 | */ 7 | 8 | var RevealSearch = (function() { 9 | 10 | var matchedSlides; 11 | var currentMatchedIndex; 12 | var searchboxDirty; 13 | var myHilitor; 14 | 15 | // Original JavaScript code by Chirp Internet: www.chirp.com.au 16 | // Please acknowledge use of this code by including this header. 17 | // 2/2013 jon: modified regex to display any match, not restricted to word boundaries. 18 | 19 | function Hilitor(id, tag) 20 | { 21 | 22 | var targetNode = document.getElementById(id) || document.body; 23 | var hiliteTag = tag || "EM"; 24 | var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM|SPAN)$"); 25 | var colors = ["#ff6", "#a0ffff", "#9f9", "#f99", "#f6f"]; 26 | var wordColor = []; 27 | var colorIdx = 0; 28 | var matchRegex = ""; 29 | var matchingSlides = []; 30 | 31 | this.setRegex = function(input) 32 | { 33 | input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|"); 34 | matchRegex = new RegExp("(" + input + ")","i"); 35 | } 36 | 37 | this.getRegex = function() 38 | { 39 | return matchRegex.toString().replace(/^\/\\b\(|\)\\b\/i$/g, "").replace(/\|/g, " "); 40 | } 41 | 42 | // recursively apply word highlighting 43 | this.hiliteWords = function(node) 44 | { 45 | if(node == undefined || !node) return; 46 | if(!matchRegex) return; 47 | if(skipTags.test(node.nodeName)) return; 48 | 49 | if(node.hasChildNodes()) { 50 | for(var i=0; i < node.childNodes.length; i++) 51 | this.hiliteWords(node.childNodes[i]); 52 | } 53 | if(node.nodeType == 3) { // NODE_TEXT 54 | if((nv = node.nodeValue) && (regs = matchRegex.exec(nv))) { 55 | //find the slide's section element and save it in our list of matching slides 56 | var secnode = node.parentNode; 57 | while (secnode.nodeName != 'SECTION') { 58 | secnode = secnode.parentNode; 59 | } 60 | 61 | var slideIndex = Reveal.getIndices(secnode); 62 | var slidelen = matchingSlides.length; 63 | var alreadyAdded = false; 64 | for (var i=0; i < slidelen; i++) { 65 | if ( (matchingSlides[i].h === slideIndex.h) && (matchingSlides[i].v === slideIndex.v) ) { 66 | alreadyAdded = true; 67 | } 68 | } 69 | if (! alreadyAdded) { 70 | matchingSlides.push(slideIndex); 71 | } 72 | 73 | if(!wordColor[regs[0].toLowerCase()]) { 74 | wordColor[regs[0].toLowerCase()] = colors[colorIdx++ % colors.length]; 75 | } 76 | 77 | var match = document.createElement(hiliteTag); 78 | match.appendChild(document.createTextNode(regs[0])); 79 | match.style.backgroundColor = wordColor[regs[0].toLowerCase()]; 80 | match.style.fontStyle = "inherit"; 81 | match.style.color = "#000"; 82 | 83 | var after = node.splitText(regs.index); 84 | after.nodeValue = after.nodeValue.substring(regs[0].length); 85 | node.parentNode.insertBefore(match, after); 86 | } 87 | } 88 | }; 89 | 90 | // remove highlighting 91 | this.remove = function() 92 | { 93 | var arr = document.getElementsByTagName(hiliteTag); 94 | while(arr.length && (el = arr[0])) { 95 | el.parentNode.replaceChild(el.firstChild, el); 96 | } 97 | }; 98 | 99 | // start highlighting at target node 100 | this.apply = function(input) 101 | { 102 | if(input == undefined || !input) return; 103 | this.remove(); 104 | this.setRegex(input); 105 | this.hiliteWords(targetNode); 106 | return matchingSlides; 107 | }; 108 | 109 | } 110 | 111 | function openSearch() { 112 | //ensure the search term input dialog is visible and has focus: 113 | var inputbox = document.getElementById("searchinput"); 114 | inputbox.style.display = "inline"; 115 | inputbox.focus(); 116 | inputbox.select(); 117 | } 118 | 119 | function toggleSearch() { 120 | var inputbox = document.getElementById("searchinput"); 121 | if (inputbox.style.display !== "inline") { 122 | openSearch(); 123 | } 124 | else { 125 | inputbox.style.display = "none"; 126 | myHilitor.remove(); 127 | } 128 | } 129 | 130 | function doSearch() { 131 | //if there's been a change in the search term, perform a new search: 132 | if (searchboxDirty) { 133 | var searchstring = document.getElementById("searchinput").value; 134 | 135 | //find the keyword amongst the slides 136 | myHilitor = new Hilitor("slidecontent"); 137 | matchedSlides = myHilitor.apply(searchstring); 138 | currentMatchedIndex = 0; 139 | } 140 | 141 | //navigate to the next slide that has the keyword, wrapping to the first if necessary 142 | if (matchedSlides.length && (matchedSlides.length <= currentMatchedIndex)) { 143 | currentMatchedIndex = 0; 144 | } 145 | if (matchedSlides.length > currentMatchedIndex) { 146 | Reveal.slide(matchedSlides[currentMatchedIndex].h, matchedSlides[currentMatchedIndex].v); 147 | currentMatchedIndex++; 148 | } 149 | } 150 | 151 | var dom = {}; 152 | dom.wrapper = document.querySelector( '.reveal' ); 153 | 154 | if( !dom.wrapper.querySelector( '.searchbox' ) ) { 155 | var searchElement = document.createElement( 'div' ); 156 | searchElement.id = "searchinputdiv"; 157 | searchElement.classList.add( 'searchdiv' ); 158 | searchElement.style.position = 'absolute'; 159 | searchElement.style.top = '10px'; 160 | searchElement.style.left = '10px'; 161 | //embedded base64 search icon Designed by Sketchdock - http://www.sketchdock.com/: 162 | searchElement.innerHTML = ''; 163 | dom.wrapper.appendChild( searchElement ); 164 | } 165 | 166 | document.getElementById("searchbutton").addEventListener( 'click', function(event) { 167 | doSearch(); 168 | }, false ); 169 | 170 | document.getElementById("searchinput").addEventListener( 'keyup', function( event ) { 171 | switch (event.keyCode) { 172 | case 13: 173 | event.preventDefault(); 174 | doSearch(); 175 | searchboxDirty = false; 176 | break; 177 | default: 178 | searchboxDirty = true; 179 | } 180 | }, false ); 181 | 182 | // Open the search when the 's' key is hit (yes, this conflicts with the notes plugin, disabling for now) 183 | /* 184 | document.addEventListener( 'keydown', function( event ) { 185 | // Disregard the event if the target is editable or a 186 | // modifier is present 187 | if ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return; 188 | 189 | if( event.keyCode === 83 ) { 190 | event.preventDefault(); 191 | openSearch(); 192 | } 193 | }, false ); 194 | */ 195 | return { open: openSearch }; 196 | })(); 197 | -------------------------------------------------------------------------------- /plugin/zoom-js/zoom.js: -------------------------------------------------------------------------------- 1 | // Custom reveal.js integration 2 | (function(){ 3 | var isEnabled = true; 4 | 5 | document.querySelector( '.reveal' ).addEventListener( 'mousedown', function( event ) { 6 | var modifier = ( Reveal.getConfig().zoomKey ? Reveal.getConfig().zoomKey : 'alt' ) + 'Key'; 7 | 8 | if( event[ modifier ] && isEnabled ) { 9 | event.preventDefault(); 10 | zoom.to({ element: event.target, pan: false }); 11 | } 12 | } ); 13 | 14 | Reveal.addEventListener( 'overviewshown', function() { isEnabled = false; } ); 15 | Reveal.addEventListener( 'overviewhidden', function() { isEnabled = true; } ); 16 | })(); 17 | 18 | /*! 19 | * zoom.js 0.2 (modified version for use with reveal.js) 20 | * http://lab.hakim.se/zoom-js 21 | * MIT licensed 22 | * 23 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se 24 | */ 25 | var zoom = (function(){ 26 | 27 | // The current zoom level (scale) 28 | var level = 1; 29 | 30 | // The current mouse position, used for panning 31 | var mouseX = 0, 32 | mouseY = 0; 33 | 34 | // Timeout before pan is activated 35 | var panEngageTimeout = -1, 36 | panUpdateInterval = -1; 37 | 38 | var currentOptions = null; 39 | 40 | // Check for transform support so that we can fallback otherwise 41 | var supportsTransforms = 'WebkitTransform' in document.body.style || 42 | 'MozTransform' in document.body.style || 43 | 'msTransform' in document.body.style || 44 | 'OTransform' in document.body.style || 45 | 'transform' in document.body.style; 46 | 47 | if( supportsTransforms ) { 48 | // The easing that will be applied when we zoom in/out 49 | document.body.style.transition = 'transform 0.8s ease'; 50 | document.body.style.OTransition = '-o-transform 0.8s ease'; 51 | document.body.style.msTransition = '-ms-transform 0.8s ease'; 52 | document.body.style.MozTransition = '-moz-transform 0.8s ease'; 53 | document.body.style.WebkitTransition = '-webkit-transform 0.8s ease'; 54 | } 55 | 56 | // Zoom out if the user hits escape 57 | document.addEventListener( 'keyup', function( event ) { 58 | if( level !== 1 && event.keyCode === 27 ) { 59 | zoom.out(); 60 | } 61 | }, false ); 62 | 63 | // Monitor mouse movement for panning 64 | document.addEventListener( 'mousemove', function( event ) { 65 | if( level !== 1 ) { 66 | mouseX = event.clientX; 67 | mouseY = event.clientY; 68 | } 69 | }, false ); 70 | 71 | /** 72 | * Applies the CSS required to zoom in, prioritizes use of CSS3 73 | * transforms but falls back on zoom for IE. 74 | * 75 | * @param {Number} pageOffsetX 76 | * @param {Number} pageOffsetY 77 | * @param {Number} elementOffsetX 78 | * @param {Number} elementOffsetY 79 | * @param {Number} scale 80 | */ 81 | function magnify( pageOffsetX, pageOffsetY, elementOffsetX, elementOffsetY, scale ) { 82 | 83 | if( supportsTransforms ) { 84 | var origin = pageOffsetX +'px '+ pageOffsetY +'px', 85 | transform = 'translate('+ -elementOffsetX +'px,'+ -elementOffsetY +'px) scale('+ scale +')'; 86 | 87 | document.body.style.transformOrigin = origin; 88 | document.body.style.OTransformOrigin = origin; 89 | document.body.style.msTransformOrigin = origin; 90 | document.body.style.MozTransformOrigin = origin; 91 | document.body.style.WebkitTransformOrigin = origin; 92 | 93 | document.body.style.transform = transform; 94 | document.body.style.OTransform = transform; 95 | document.body.style.msTransform = transform; 96 | document.body.style.MozTransform = transform; 97 | document.body.style.WebkitTransform = transform; 98 | } 99 | else { 100 | // Reset all values 101 | if( scale === 1 ) { 102 | document.body.style.position = ''; 103 | document.body.style.left = ''; 104 | document.body.style.top = ''; 105 | document.body.style.width = ''; 106 | document.body.style.height = ''; 107 | document.body.style.zoom = ''; 108 | } 109 | // Apply scale 110 | else { 111 | document.body.style.position = 'relative'; 112 | document.body.style.left = ( - ( pageOffsetX + elementOffsetX ) / scale ) + 'px'; 113 | document.body.style.top = ( - ( pageOffsetY + elementOffsetY ) / scale ) + 'px'; 114 | document.body.style.width = ( scale * 100 ) + '%'; 115 | document.body.style.height = ( scale * 100 ) + '%'; 116 | document.body.style.zoom = scale; 117 | } 118 | } 119 | 120 | level = scale; 121 | 122 | if( level !== 1 && document.documentElement.classList ) { 123 | document.documentElement.classList.add( 'zoomed' ); 124 | } 125 | else { 126 | document.documentElement.classList.remove( 'zoomed' ); 127 | } 128 | } 129 | 130 | /** 131 | * Pan the document when the mosue cursor approaches the edges 132 | * of the window. 133 | */ 134 | function pan() { 135 | var range = 0.12, 136 | rangeX = window.innerWidth * range, 137 | rangeY = window.innerHeight * range, 138 | scrollOffset = getScrollOffset(); 139 | 140 | // Up 141 | if( mouseY < rangeY ) { 142 | window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) ); 143 | } 144 | // Down 145 | else if( mouseY > window.innerHeight - rangeY ) { 146 | window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) ); 147 | } 148 | 149 | // Left 150 | if( mouseX < rangeX ) { 151 | window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y ); 152 | } 153 | // Right 154 | else if( mouseX > window.innerWidth - rangeX ) { 155 | window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y ); 156 | } 157 | } 158 | 159 | function getScrollOffset() { 160 | return { 161 | x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset, 162 | y: window.scrollY !== undefined ? window.scrollY : window.pageXYffset 163 | } 164 | } 165 | 166 | return { 167 | /** 168 | * Zooms in on either a rectangle or HTML element. 169 | * 170 | * @param {Object} options 171 | * - element: HTML element to zoom in on 172 | * OR 173 | * - x/y: coordinates in non-transformed space to zoom in on 174 | * - width/height: the portion of the screen to zoom in on 175 | * - scale: can be used instead of width/height to explicitly set scale 176 | */ 177 | to: function( options ) { 178 | // Due to an implementation limitation we can't zoom in 179 | // to another element without zooming out first 180 | if( level !== 1 ) { 181 | zoom.out(); 182 | } 183 | else { 184 | options.x = options.x || 0; 185 | options.y = options.y || 0; 186 | 187 | // If an element is set, that takes precedence 188 | if( !!options.element ) { 189 | // Space around the zoomed in element to leave on screen 190 | var padding = 20; 191 | 192 | options.width = options.element.getBoundingClientRect().width + ( padding * 2 ); 193 | options.height = options.element.getBoundingClientRect().height + ( padding * 2 ); 194 | options.x = options.element.getBoundingClientRect().left - padding; 195 | options.y = options.element.getBoundingClientRect().top - padding; 196 | } 197 | 198 | // If width/height values are set, calculate scale from those values 199 | if( options.width !== undefined && options.height !== undefined ) { 200 | options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 ); 201 | } 202 | 203 | if( options.scale > 1 ) { 204 | options.x *= options.scale; 205 | options.y *= options.scale; 206 | 207 | var scrollOffset = getScrollOffset(); 208 | 209 | if( options.element ) { 210 | scrollOffset.x -= ( window.innerWidth - ( options.width * options.scale ) ) / 2; 211 | } 212 | 213 | magnify( scrollOffset.x, scrollOffset.y, options.x, options.y, options.scale ); 214 | 215 | if( options.pan !== false ) { 216 | 217 | // Wait with engaging panning as it may conflict with the 218 | // zoom transition 219 | panEngageTimeout = setTimeout( function() { 220 | panUpdateInterval = setInterval( pan, 1000 / 60 ); 221 | }, 800 ); 222 | 223 | } 224 | } 225 | 226 | currentOptions = options; 227 | } 228 | }, 229 | 230 | /** 231 | * Resets the document zoom state to its default. 232 | */ 233 | out: function() { 234 | clearTimeout( panEngageTimeout ); 235 | clearInterval( panUpdateInterval ); 236 | 237 | var scrollOffset = getScrollOffset(); 238 | 239 | if( currentOptions && currentOptions.element ) { 240 | scrollOffset.x -= ( window.innerWidth - ( currentOptions.width * currentOptions.scale ) ) / 2; 241 | } 242 | 243 | magnify( scrollOffset.x, scrollOffset.y, 0, 0, 1 ); 244 | 245 | level = 1; 246 | }, 247 | 248 | // Alias 249 | magnify: function( options ) { this.to( options ) }, 250 | reset: function() { this.out() }, 251 | 252 | zoomLevel: function() { 253 | return level; 254 | } 255 | } 256 | 257 | })(); 258 | 259 | -------------------------------------------------------------------------------- /test/examples/assets/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/test/examples/assets/image1.png -------------------------------------------------------------------------------- /test/examples/assets/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/firebase-intro-presentation/c7ffb50207bcfedf9b29ed5c2db63ca9e5aac1b9/test/examples/assets/image2.png -------------------------------------------------------------------------------- /test/examples/barebones.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Barebones 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 |
    17 | 18 |
    19 |

    Barebones Presentation

    20 |

    This example contains the bare minimum includes and markup required to run a reveal.js presentation.

    21 |
    22 | 23 |
    24 |

    No Theme

    25 |

    There's no theme included, so it will fall back on browser defaults.

    26 |
    27 | 28 |
    29 | 30 |
    31 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /test/examples/embedded-media.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Embedded Media 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    18 | 19 |
    20 | 21 |
    22 |

    Embedded Media Test

    23 |
    24 | 25 |
    26 | 27 |
    28 | 29 |
    30 |

    Empty Slide

    31 |
    32 | 33 |
    34 | 35 |
    36 | 37 | 38 | 39 | 40 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /test/examples/math.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Math Plugin 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    18 | 19 |
    20 | 21 |
    22 |

    reveal.js Math Plugin

    23 |

    A thin wrapper for MathJax

    24 |
    25 | 26 |
    27 |

    The Lorenz Equations

    28 | 29 | \[\begin{aligned} 30 | \dot{x} & = \sigma(y-x) \\ 31 | \dot{y} & = \rho x - y - xz \\ 32 | \dot{z} & = -\beta z + xy 33 | \end{aligned} \] 34 |
    35 | 36 |
    37 |

    The Cauchy-Schwarz Inequality

    38 | 39 | 42 |
    43 | 44 |
    45 |

    A Cross Product Formula

    46 | 47 | \[\mathbf{V}_1 \times \mathbf{V}_2 = \begin{vmatrix} 48 | \mathbf{i} & \mathbf{j} & \mathbf{k} \\ 49 | \frac{\partial X}{\partial u} & \frac{\partial Y}{\partial u} & 0 \\ 50 | \frac{\partial X}{\partial v} & \frac{\partial Y}{\partial v} & 0 51 | \end{vmatrix} \] 52 |
    53 | 54 |
    55 |

    The probability of getting \(k\) heads when flipping \(n\) coins is

    56 | 57 | \[P(E) = {n \choose k} p^k (1-p)^{ n-k} \] 58 |
    59 | 60 |
    61 |

    An Identity of Ramanujan

    62 | 63 | \[ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 64 | 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} 65 | {1+\frac{e^{-8\pi}} {1+\ldots} } } } \] 66 |
    67 | 68 |
    69 |

    A Rogers-Ramanujan Identity

    70 | 71 | \[ 1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots = 72 | \prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})}\] 73 |
    74 | 75 |
    76 |

    Maxwell’s Equations

    77 | 78 | \[ \begin{aligned} 79 | \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ 80 | \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ 81 | \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned} 82 | \] 83 |
    84 | 85 |
    86 |
    87 |

    The Lorenz Equations

    88 | 89 |
    90 | \[\begin{aligned} 91 | \dot{x} & = \sigma(y-x) \\ 92 | \dot{y} & = \rho x - y - xz \\ 93 | \dot{z} & = -\beta z + xy 94 | \end{aligned} \] 95 |
    96 |
    97 | 98 |
    99 |

    The Cauchy-Schwarz Inequality

    100 | 101 |
    102 | \[ \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) \] 103 |
    104 |
    105 | 106 |
    107 |

    A Cross Product Formula

    108 | 109 |
    110 | \[\mathbf{V}_1 \times \mathbf{V}_2 = \begin{vmatrix} 111 | \mathbf{i} & \mathbf{j} & \mathbf{k} \\ 112 | \frac{\partial X}{\partial u} & \frac{\partial Y}{\partial u} & 0 \\ 113 | \frac{\partial X}{\partial v} & \frac{\partial Y}{\partial v} & 0 114 | \end{vmatrix} \] 115 |
    116 |
    117 | 118 |
    119 |

    The probability of getting \(k\) heads when flipping \(n\) coins is

    120 | 121 |
    122 | \[P(E) = {n \choose k} p^k (1-p)^{ n-k} \] 123 |
    124 |
    125 | 126 |
    127 |

    An Identity of Ramanujan

    128 | 129 |
    130 | \[ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 131 | 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} 132 | {1+\frac{e^{-8\pi}} {1+\ldots} } } } \] 133 |
    134 |
    135 | 136 |
    137 |

    A Rogers-Ramanujan Identity

    138 | 139 |
    140 | \[ 1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots = 141 | \prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})}\] 142 |
    143 |
    144 | 145 |
    146 |

    Maxwell’s Equations

    147 | 148 |
    149 | \[ \begin{aligned} 150 | \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ 151 | \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ 152 | \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned} 153 | \] 154 |
    155 |
    156 |
    157 | 158 |
    159 | 160 |
    161 | 162 | 163 | 164 | 165 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /test/examples/slide-backgrounds.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Slide Backgrounds 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    18 | 19 |
    20 | 21 |
    22 |

    data-background: #00ffff

    23 |
    24 | 25 |
    26 |

    data-background: #bb00bb

    27 |
    28 | 29 |
    30 |
    31 |

    data-background: #ff0000

    32 |
    33 |
    34 |

    data-background: rgba(0, 0, 0, 0.2)

    35 |
    36 |
    37 |

    data-background: salmon

    38 |
    39 |
    40 | 41 |
    42 |
    43 |

    Background applied to stack

    44 |
    45 |
    46 |

    Background applied to stack

    47 |
    48 |
    49 |

    Background applied to slide inside of stack

    50 |
    51 |
    52 | 53 |
    54 |

    Background image

    55 |
    56 | 57 |
    58 |
    59 |

    Background image

    60 |
    61 |
    62 |

    Background image

    63 |
    64 |
    65 | 66 |
    67 |

    Background image

    68 |
    data-background-size="100px" data-background-repeat="repeat" data-background-color="#111"
    69 |
    70 | 71 |
    72 |

    Same background twice (1/2)

    73 |
    74 |
    75 |

    Same background twice (2/2)

    76 |
    77 | 78 |
    79 |
    80 |

    Same background twice vertical (1/2)

    81 |
    82 |
    83 |

    Same background twice vertical (2/2)

    84 |
    85 |
    86 | 87 |
    88 |

    Same background from horizontal to vertical (1/3)

    89 |
    90 |
    91 |
    92 |

    Same background from horizontal to vertical (2/3)

    93 |
    94 |
    95 |

    Same background from horizontal to vertical (3/3)

    96 |
    97 |
    98 | 99 |
    100 | 101 |
    102 | 103 | 104 | 105 | 106 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /test/qunit-1.12.0.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.12.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://qunitjs.com 5 | * 6 | * Copyright 2012 jQuery Foundation and other contributors 7 | * Released under the MIT license. 8 | * http://jquery.org/license 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | overflow: hidden; 71 | } 72 | 73 | #qunit-userAgent { 74 | padding: 0.5em 0 0.5em 2.5em; 75 | background-color: #2b81af; 76 | color: #fff; 77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 78 | } 79 | 80 | #qunit-modulefilter-container { 81 | float: right; 82 | } 83 | 84 | /** Tests: Pass/Fail */ 85 | 86 | #qunit-tests { 87 | list-style-position: inside; 88 | } 89 | 90 | #qunit-tests li { 91 | padding: 0.4em 0.5em 0.4em 2.5em; 92 | border-bottom: 1px solid #fff; 93 | list-style-position: inside; 94 | } 95 | 96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 97 | display: none; 98 | } 99 | 100 | #qunit-tests li strong { 101 | cursor: pointer; 102 | } 103 | 104 | #qunit-tests li a { 105 | padding: 0.5em; 106 | color: #c2ccd1; 107 | text-decoration: none; 108 | } 109 | #qunit-tests li a:hover, 110 | #qunit-tests li a:focus { 111 | color: #000; 112 | } 113 | 114 | #qunit-tests li .runtime { 115 | float: right; 116 | font-size: smaller; 117 | } 118 | 119 | .qunit-assert-list { 120 | margin-top: 0.5em; 121 | padding: 0.5em; 122 | 123 | background-color: #fff; 124 | 125 | border-radius: 5px; 126 | -moz-border-radius: 5px; 127 | -webkit-border-radius: 5px; 128 | } 129 | 130 | .qunit-collapsed { 131 | display: none; 132 | } 133 | 134 | #qunit-tests table { 135 | border-collapse: collapse; 136 | margin-top: .2em; 137 | } 138 | 139 | #qunit-tests th { 140 | text-align: right; 141 | vertical-align: top; 142 | padding: 0 .5em 0 0; 143 | } 144 | 145 | #qunit-tests td { 146 | vertical-align: top; 147 | } 148 | 149 | #qunit-tests pre { 150 | margin: 0; 151 | white-space: pre-wrap; 152 | word-wrap: break-word; 153 | } 154 | 155 | #qunit-tests del { 156 | background-color: #e0f2be; 157 | color: #374e0c; 158 | text-decoration: none; 159 | } 160 | 161 | #qunit-tests ins { 162 | background-color: #ffcaca; 163 | color: #500; 164 | text-decoration: none; 165 | } 166 | 167 | /*** Test Counts */ 168 | 169 | #qunit-tests b.counts { color: black; } 170 | #qunit-tests b.passed { color: #5E740B; } 171 | #qunit-tests b.failed { color: #710909; } 172 | 173 | #qunit-tests li li { 174 | padding: 5px; 175 | background-color: #fff; 176 | border-bottom: none; 177 | list-style-position: inside; 178 | } 179 | 180 | /*** Passing Styles */ 181 | 182 | #qunit-tests li li.pass { 183 | color: #3c510c; 184 | background-color: #fff; 185 | border-left: 10px solid #C6E746; 186 | } 187 | 188 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 189 | #qunit-tests .pass .test-name { color: #366097; } 190 | 191 | #qunit-tests .pass .test-actual, 192 | #qunit-tests .pass .test-expected { color: #999999; } 193 | 194 | #qunit-banner.qunit-pass { background-color: #C6E746; } 195 | 196 | /*** Failing Styles */ 197 | 198 | #qunit-tests li li.fail { 199 | color: #710909; 200 | background-color: #fff; 201 | border-left: 10px solid #EE5757; 202 | white-space: pre; 203 | } 204 | 205 | #qunit-tests > li:last-child { 206 | border-radius: 0 0 5px 5px; 207 | -moz-border-radius: 0 0 5px 5px; 208 | -webkit-border-bottom-right-radius: 5px; 209 | -webkit-border-bottom-left-radius: 5px; 210 | } 211 | 212 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 213 | #qunit-tests .fail .test-name, 214 | #qunit-tests .fail .module-name { color: #000000; } 215 | 216 | #qunit-tests .fail .test-actual { color: #EE5757; } 217 | #qunit-tests .fail .test-expected { color: green; } 218 | 219 | #qunit-banner.qunit-fail { background-color: #EE5757; } 220 | 221 | 222 | /** Result */ 223 | 224 | #qunit-testresult { 225 | padding: 0.5em 0.5em 0.5em 2.5em; 226 | 227 | color: #2b81af; 228 | background-color: #D2E0E6; 229 | 230 | border-bottom: 1px solid white; 231 | } 232 | #qunit-testresult .module-name { 233 | font-weight: bold; 234 | } 235 | 236 | /** Fixture */ 237 | 238 | #qunit-fixture { 239 | position: absolute; 240 | top: -10000px; 241 | left: -10000px; 242 | width: 1000px; 243 | height: 1000px; 244 | } -------------------------------------------------------------------------------- /test/test-markdown-element-attributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Test Markdown Element Attributes 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 |
    17 | 18 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /test/test-markdown-element-attributes.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reveal.addEventListener( 'ready', function() { 4 | 5 | QUnit.module( 'Markdown' ); 6 | 7 | test( 'Vertical separator', function() { 8 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section' ).length, 4, 'found four slides' ); 9 | }); 10 | 11 | 12 | test( 'Attributes on element header in vertical slides', function() { 13 | strictEqual( document.querySelectorAll( '.reveal .slides section>section h2.fragment.fade-out' ).length, 1, 'found one vertical slide with class fragment.fade-out on header' ); 14 | strictEqual( document.querySelectorAll( '.reveal .slides section>section h2.fragment.shrink' ).length, 1, 'found one vertical slide with class fragment.shrink on header' ); 15 | }); 16 | 17 | test( 'Attributes on element paragraphs in vertical slides', function() { 18 | strictEqual( document.querySelectorAll( '.reveal .slides section>section p.fragment.grow' ).length, 2, 'found a vertical slide with two paragraphs with class fragment.grow' ); 19 | }); 20 | 21 | test( 'Attributes on element list items in vertical slides', function() { 22 | strictEqual( document.querySelectorAll( '.reveal .slides section>section li.fragment.roll-in' ).length, 3, 'found a vertical slide with three list items with class fragment.roll-in' ); 23 | }); 24 | 25 | test( 'Attributes on element paragraphs in horizontal slides', function() { 26 | strictEqual( document.querySelectorAll( '.reveal .slides section p.fragment.highlight-red' ).length, 4, 'found a horizontal slide with four paragraphs with class fragment.grow' ); 27 | }); 28 | test( 'Attributes on element list items in horizontal slides', function() { 29 | strictEqual( document.querySelectorAll( '.reveal .slides section li.fragment.highlight-green' ).length, 5, 'found a horizontal slide with five list items with class fragment.roll-in' ); 30 | }); 31 | test( 'Attributes on element list items in horizontal slides', function() { 32 | strictEqual( document.querySelectorAll( '.reveal .slides section img.reveal.stretch' ).length, 1, 'found a horizontal slide with stretched image, class img.reveal.stretch' ); 33 | }); 34 | 35 | test( 'Attributes on elements in vertical slides with default element attribute separator', function() { 36 | strictEqual( document.querySelectorAll( '.reveal .slides section h2.fragment.highlight-red' ).length, 2, 'found two h2 titles with fragment highlight-red in vertical slides with default element attribute separator' ); 37 | }); 38 | 39 | test( 'Attributes on elements in single slides with default element attribute separator', function() { 40 | strictEqual( document.querySelectorAll( '.reveal .slides section p.fragment.highlight-blue' ).length, 3, 'found three elements with fragment highlight-blue in single slide with default element attribute separator' ); 41 | }); 42 | 43 | } ); 44 | 45 | Reveal.initialize(); 46 | 47 | -------------------------------------------------------------------------------- /test/test-markdown-slide-attributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Test Markdown Attributes 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 |
    17 | 18 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /test/test-markdown-slide-attributes.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reveal.addEventListener( 'ready', function() { 4 | 5 | QUnit.module( 'Markdown' ); 6 | 7 | test( 'Vertical separator', function() { 8 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section' ).length, 6, 'found six vertical slides' ); 9 | }); 10 | 11 | test( 'Id on slide', function() { 12 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section#slide2' ).length, 1, 'found one slide with id slide2' ); 13 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section a[href="#/slide2"]' ).length, 1, 'found one slide with a link to slide2' ); 14 | }); 15 | 16 | test( 'data-background attributes', function() { 17 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-background="#A0C66B"]' ).length, 1, 'found one vertical slide with data-background="#A0C66B"' ); 18 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-background="#ff0000"]' ).length, 1, 'found one vertical slide with data-background="#ff0000"' ); 19 | strictEqual( document.querySelectorAll( '.reveal .slides>section[data-background="#C6916B"]' ).length, 1, 'found one slide with data-background="#C6916B"' ); 20 | }); 21 | 22 | test( 'data-transition attributes', function() { 23 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-transition="zoom"]' ).length, 1, 'found one vertical slide with data-transition="zoom"' ); 24 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-transition="fade"]' ).length, 1, 'found one vertical slide with data-transition="fade"' ); 25 | strictEqual( document.querySelectorAll( '.reveal .slides section [data-transition="zoom"]' ).length, 1, 'found one slide with data-transition="zoom"' ); 26 | }); 27 | 28 | test( 'data-background attributes with default separator', function() { 29 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-background="#A7C66B"]' ).length, 1, 'found one vertical slide with data-background="#A0C66B"' ); 30 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-background="#f70000"]' ).length, 1, 'found one vertical slide with data-background="#ff0000"' ); 31 | strictEqual( document.querySelectorAll( '.reveal .slides>section[data-background="#C7916B"]' ).length, 1, 'found one slide with data-background="#C6916B"' ); 32 | }); 33 | 34 | test( 'data-transition attributes with default separator', function() { 35 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-transition="concave"]' ).length, 1, 'found one vertical slide with data-transition="zoom"' ); 36 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section[data-transition="page"]' ).length, 1, 'found one vertical slide with data-transition="fade"' ); 37 | strictEqual( document.querySelectorAll( '.reveal .slides section [data-transition="concave"]' ).length, 1, 'found one slide with data-transition="zoom"' ); 38 | }); 39 | 40 | test( 'data-transition attributes with inline content', function() { 41 | strictEqual( document.querySelectorAll( '.reveal .slides>section[data-background="#ff0000"]' ).length, 3, 'found three horizontal slides with data-background="#ff0000"' ); 42 | }); 43 | 44 | } ); 45 | 46 | Reveal.initialize(); 47 | 48 | -------------------------------------------------------------------------------- /test/test-markdown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Test Markdown 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 |
    17 | 18 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /test/test-markdown.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reveal.addEventListener( 'ready', function() { 4 | 5 | QUnit.module( 'Markdown' ); 6 | 7 | test( 'Vertical separator', function() { 8 | strictEqual( document.querySelectorAll( '.reveal .slides>section>section' ).length, 2, 'found two slides' ); 9 | }); 10 | 11 | 12 | } ); 13 | 14 | Reveal.initialize(); 15 | 16 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reveal.js - Tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 |
    17 | 18 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | // These tests expect the DOM to contain a presentation 3 | // with the following slide structure: 4 | // 5 | // 1 6 | // 2 - Three sub-slides 7 | // 3 - Three fragment elements 8 | // 3 - Two fragments with same data-fragment-index 9 | // 4 10 | 11 | 12 | Reveal.addEventListener( 'ready', function() { 13 | 14 | // --------------------------------------------------------------- 15 | // DOM TESTS 16 | 17 | QUnit.module( 'DOM' ); 18 | 19 | test( 'Initial slides classes', function() { 20 | var horizontalSlides = document.querySelectorAll( '.reveal .slides>section' ) 21 | 22 | strictEqual( document.querySelectorAll( '.reveal .slides section.past' ).length, 0, 'no .past slides' ); 23 | strictEqual( document.querySelectorAll( '.reveal .slides section.present' ).length, 1, 'one .present slide' ); 24 | strictEqual( document.querySelectorAll( '.reveal .slides>section.future' ).length, horizontalSlides.length - 1, 'remaining horizontal slides are .future' ); 25 | 26 | strictEqual( document.querySelectorAll( '.reveal .slides section.stack' ).length, 2, 'two .stacks' ); 27 | 28 | ok( document.querySelectorAll( '.reveal .slides section.stack' )[0].querySelectorAll( '.future' ).length > 0, 'vertical slides are given .future' ); 29 | }); 30 | 31 | // --------------------------------------------------------------- 32 | // API TESTS 33 | 34 | QUnit.module( 'API' ); 35 | 36 | test( 'Reveal.isReady', function() { 37 | strictEqual( Reveal.isReady(), true, 'returns true' ); 38 | }); 39 | 40 | test( 'Reveal.isOverview', function() { 41 | strictEqual( Reveal.isOverview(), false, 'false by default' ); 42 | 43 | Reveal.toggleOverview(); 44 | strictEqual( Reveal.isOverview(), true, 'true after toggling on' ); 45 | 46 | Reveal.toggleOverview(); 47 | strictEqual( Reveal.isOverview(), false, 'false after toggling off' ); 48 | }); 49 | 50 | test( 'Reveal.isPaused', function() { 51 | strictEqual( Reveal.isPaused(), false, 'false by default' ); 52 | 53 | Reveal.togglePause(); 54 | strictEqual( Reveal.isPaused(), true, 'true after pausing' ); 55 | 56 | Reveal.togglePause(); 57 | strictEqual( Reveal.isPaused(), false, 'false after resuming' ); 58 | }); 59 | 60 | test( 'Reveal.isFirstSlide', function() { 61 | Reveal.slide( 0, 0 ); 62 | strictEqual( Reveal.isFirstSlide(), true, 'true after Reveal.slide( 0, 0 )' ); 63 | 64 | Reveal.slide( 1, 0 ); 65 | strictEqual( Reveal.isFirstSlide(), false, 'false after Reveal.slide( 1, 0 )' ); 66 | 67 | Reveal.slide( 0, 0 ); 68 | strictEqual( Reveal.isFirstSlide(), true, 'true after Reveal.slide( 0, 0 )' ); 69 | }); 70 | 71 | test( 'Reveal.isLastSlide', function() { 72 | Reveal.slide( 0, 0 ); 73 | strictEqual( Reveal.isLastSlide(), false, 'false after Reveal.slide( 0, 0 )' ); 74 | 75 | var lastSlideIndex = document.querySelectorAll( '.reveal .slides>section' ).length - 1; 76 | 77 | Reveal.slide( lastSlideIndex, 0 ); 78 | strictEqual( Reveal.isLastSlide(), true, 'true after Reveal.slide( ', 0+ lastSlideIndex +' )' ); 79 | 80 | Reveal.slide( 0, 0 ); 81 | strictEqual( Reveal.isLastSlide(), false, 'false after Reveal.slide( 0, 0 )' ); 82 | }); 83 | 84 | test( 'Reveal.getIndices', function() { 85 | var indices = Reveal.getIndices(); 86 | 87 | ok( typeof indices.hasOwnProperty( 'h' ), 'h exists' ); 88 | ok( typeof indices.hasOwnProperty( 'v' ), 'v exists' ); 89 | ok( typeof indices.hasOwnProperty( 'f' ), 'f exists' ); 90 | 91 | Reveal.slide( 1, 0 ); 92 | ok( Reveal.getIndices().h === 1 && Reveal.getIndices().v === 0, 'h 1, v 0' ); 93 | 94 | Reveal.slide( 1, 2 ); 95 | ok( Reveal.getIndices().h === 1 && Reveal.getIndices().v === 2, 'h 1, v 2' ); 96 | 97 | Reveal.slide( 0, 0 ); 98 | }); 99 | 100 | test( 'Reveal.getSlide', function() { 101 | var firstSlide = document.querySelector( '.reveal .slides>section:first-child' ); 102 | 103 | equal( Reveal.getSlide( 0 ), firstSlide, 'gets correct first slide' ); 104 | 105 | strictEqual( Reveal.getSlide( 100 ), undefined, 'returns undefined when slide can\'t be found' ); 106 | }); 107 | 108 | test( 'Reveal.getPreviousSlide/getCurrentSlide', function() { 109 | Reveal.slide( 0, 0 ); 110 | Reveal.slide( 1, 0 ); 111 | 112 | var firstSlide = document.querySelector( '.reveal .slides>section:first-child' ); 113 | var secondSlide = document.querySelector( '.reveal .slides>section:nth-child(2)>section' ); 114 | 115 | equal( Reveal.getPreviousSlide(), firstSlide, 'previous is slide #0' ); 116 | equal( Reveal.getCurrentSlide(), secondSlide, 'current is slide #1' ); 117 | }); 118 | 119 | test( 'Reveal.getScale', function() { 120 | ok( typeof Reveal.getScale() === 'number', 'has scale' ); 121 | }); 122 | 123 | test( 'Reveal.getConfig', function() { 124 | ok( typeof Reveal.getConfig() === 'object', 'has config' ); 125 | }); 126 | 127 | test( 'Reveal.configure', function() { 128 | strictEqual( Reveal.getConfig().loop, false, '"loop" is false to start with' ); 129 | 130 | Reveal.configure({ loop: true }); 131 | strictEqual( Reveal.getConfig().loop, true, '"loop" has changed to true' ); 132 | 133 | Reveal.configure({ loop: false, customTestValue: 1 }); 134 | strictEqual( Reveal.getConfig().customTestValue, 1, 'supports custom values' ); 135 | }); 136 | 137 | test( 'Reveal.availableRoutes', function() { 138 | Reveal.slide( 0, 0 ); 139 | deepEqual( Reveal.availableRoutes(), { left: false, up: false, down: false, right: true }, 'correct for first slide' ); 140 | 141 | Reveal.slide( 1, 0 ); 142 | deepEqual( Reveal.availableRoutes(), { left: true, up: false, down: true, right: true }, 'correct for vertical slide' ); 143 | }); 144 | 145 | test( 'Reveal.next', function() { 146 | Reveal.slide( 0, 0 ); 147 | 148 | // Step through vertical child slides 149 | Reveal.next(); 150 | deepEqual( Reveal.getIndices(), { h: 1, v: 0, f: undefined } ); 151 | 152 | Reveal.next(); 153 | deepEqual( Reveal.getIndices(), { h: 1, v: 1, f: undefined } ); 154 | 155 | Reveal.next(); 156 | deepEqual( Reveal.getIndices(), { h: 1, v: 2, f: undefined } ); 157 | 158 | // Step through fragments 159 | Reveal.next(); 160 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: -1 } ); 161 | 162 | Reveal.next(); 163 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 } ); 164 | 165 | Reveal.next(); 166 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 } ); 167 | 168 | Reveal.next(); 169 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 } ); 170 | }); 171 | 172 | test( 'Reveal.next at end', function() { 173 | Reveal.slide( 3 ); 174 | 175 | // We're at the end, this should have no effect 176 | Reveal.next(); 177 | deepEqual( Reveal.getIndices(), { h: 3, v: 0, f: undefined } ); 178 | 179 | Reveal.next(); 180 | deepEqual( Reveal.getIndices(), { h: 3, v: 0, f: undefined } ); 181 | }); 182 | 183 | 184 | // --------------------------------------------------------------- 185 | // FRAGMENT TESTS 186 | 187 | QUnit.module( 'Fragments' ); 188 | 189 | test( 'Sliding to fragments', function() { 190 | Reveal.slide( 2, 0, -1 ); 191 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: -1 }, 'Reveal.slide( 2, 0, -1 )' ); 192 | 193 | Reveal.slide( 2, 0, 0 ); 194 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'Reveal.slide( 2, 0, 0 )' ); 195 | 196 | Reveal.slide( 2, 0, 2 ); 197 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'Reveal.slide( 2, 0, 2 )' ); 198 | 199 | Reveal.slide( 2, 0, 1 ); 200 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'Reveal.slide( 2, 0, 1 )' ); 201 | }); 202 | 203 | test( 'Hiding all fragments', function() { 204 | var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); 205 | 206 | Reveal.slide( 2, 0, 0 ); 207 | strictEqual( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 1, 'one fragment visible when index is 0' ); 208 | 209 | Reveal.slide( 2, 0, -1 ); 210 | strictEqual( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 0, 'no fragments visible when index is -1' ); 211 | }); 212 | 213 | test( 'Current fragment', function() { 214 | var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); 215 | 216 | Reveal.slide( 2, 0 ); 217 | strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment at index -1' ); 218 | 219 | Reveal.slide( 2, 0, 0 ); 220 | strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 1, 'one current fragment at index 0' ); 221 | 222 | Reveal.slide( 1, 0, 0 ); 223 | strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment when navigating to previous slide' ); 224 | 225 | Reveal.slide( 3, 0, 0 ); 226 | strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment when navigating to next slide' ); 227 | }); 228 | 229 | test( 'Stepping through fragments', function() { 230 | Reveal.slide( 2, 0, -1 ); 231 | 232 | // forwards: 233 | 234 | Reveal.next(); 235 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'next() goes to next fragment' ); 236 | 237 | Reveal.right(); 238 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'right() goes to next fragment' ); 239 | 240 | Reveal.down(); 241 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'down() goes to next fragment' ); 242 | 243 | Reveal.down(); // moves to f #3 244 | 245 | // backwards: 246 | 247 | Reveal.prev(); 248 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'prev() goes to prev fragment' ); 249 | 250 | Reveal.left(); 251 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'left() goes to prev fragment' ); 252 | 253 | Reveal.up(); 254 | deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'up() goes to prev fragment' ); 255 | }); 256 | 257 | test( 'Stepping past fragments', function() { 258 | var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); 259 | 260 | Reveal.slide( 0, 0, 0 ); 261 | equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 0, 'no fragments visible when on previous slide' ); 262 | 263 | Reveal.slide( 3, 0, 0 ); 264 | equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 3, 'all fragments visible when on future slide' ); 265 | }); 266 | 267 | test( 'Fragment indices', function() { 268 | var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(2)' ); 269 | 270 | Reveal.slide( 3, 0, 0 ); 271 | equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 2, 'both fragments of same index are shown' ); 272 | }); 273 | 274 | test( 'Index generation', function() { 275 | var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); 276 | 277 | // These have no indices defined to start with 278 | equal( fragmentSlide.querySelectorAll( '.fragment' )[0].getAttribute( 'data-fragment-index' ), '0' ); 279 | equal( fragmentSlide.querySelectorAll( '.fragment' )[1].getAttribute( 'data-fragment-index' ), '1' ); 280 | equal( fragmentSlide.querySelectorAll( '.fragment' )[2].getAttribute( 'data-fragment-index' ), '2' ); 281 | }); 282 | 283 | test( 'Index normalization', function() { 284 | var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(3)' ); 285 | 286 | // These start out as 1-4-4 and should normalize to 0-1-1 287 | equal( fragmentSlide.querySelectorAll( '.fragment' )[0].getAttribute( 'data-fragment-index' ), '0' ); 288 | equal( fragmentSlide.querySelectorAll( '.fragment' )[1].getAttribute( 'data-fragment-index' ), '1' ); 289 | equal( fragmentSlide.querySelectorAll( '.fragment' )[2].getAttribute( 'data-fragment-index' ), '1' ); 290 | }); 291 | 292 | asyncTest( 'fragmentshown event', function() { 293 | expect( 2 ); 294 | 295 | var _onEvent = function( event ) { 296 | ok( true, 'event fired' ); 297 | } 298 | 299 | Reveal.addEventListener( 'fragmentshown', _onEvent ); 300 | 301 | Reveal.slide( 2, 0 ); 302 | Reveal.slide( 2, 0 ); // should do nothing 303 | Reveal.slide( 2, 0, 0 ); // should do nothing 304 | Reveal.next(); 305 | Reveal.next(); 306 | Reveal.prev(); // shouldn't fire fragmentshown 307 | 308 | start(); 309 | 310 | Reveal.removeEventListener( 'fragmentshown', _onEvent ); 311 | }); 312 | 313 | asyncTest( 'fragmenthidden event', function() { 314 | expect( 2 ); 315 | 316 | var _onEvent = function( event ) { 317 | ok( true, 'event fired' ); 318 | } 319 | 320 | Reveal.addEventListener( 'fragmenthidden', _onEvent ); 321 | 322 | Reveal.slide( 2, 0, 2 ); 323 | Reveal.slide( 2, 0, 2 ); // should do nothing 324 | Reveal.prev(); 325 | Reveal.prev(); 326 | Reveal.next(); // shouldn't fire fragmenthidden 327 | 328 | start(); 329 | 330 | Reveal.removeEventListener( 'fragmenthidden', _onEvent ); 331 | }); 332 | 333 | 334 | // --------------------------------------------------------------- 335 | // CONFIGURATION VALUES 336 | 337 | QUnit.module( 'Configuration' ); 338 | 339 | test( 'Controls', function() { 340 | var controlsElement = document.querySelector( '.reveal>.controls' ); 341 | 342 | Reveal.configure({ controls: false }); 343 | equal( controlsElement.style.display, 'none', 'controls are hidden' ); 344 | 345 | Reveal.configure({ controls: true }); 346 | equal( controlsElement.style.display, 'block', 'controls are visible' ); 347 | }); 348 | 349 | test( 'Progress', function() { 350 | var progressElement = document.querySelector( '.reveal>.progress' ); 351 | 352 | Reveal.configure({ progress: false }); 353 | equal( progressElement.style.display, 'none', 'progress are hidden' ); 354 | 355 | Reveal.configure({ progress: true }); 356 | equal( progressElement.style.display, 'block', 'progress are visible' ); 357 | }); 358 | 359 | test( 'Loop', function() { 360 | Reveal.configure({ loop: true }); 361 | 362 | Reveal.slide( 0, 0 ); 363 | 364 | Reveal.left(); 365 | notEqual( Reveal.getIndices().h, 0, 'looped from start to end' ); 366 | 367 | Reveal.right(); 368 | equal( Reveal.getIndices().h, 0, 'looped from end to start' ); 369 | 370 | Reveal.configure({ loop: false }); 371 | }); 372 | 373 | 374 | // --------------------------------------------------------------- 375 | // EVENT TESTS 376 | 377 | QUnit.module( 'Events' ); 378 | 379 | asyncTest( 'slidechanged', function() { 380 | expect( 3 ); 381 | 382 | var _onEvent = function( event ) { 383 | ok( true, 'event fired' ); 384 | } 385 | 386 | Reveal.addEventListener( 'slidechanged', _onEvent ); 387 | 388 | Reveal.slide( 1, 0 ); // should trigger 389 | Reveal.slide( 1, 0 ); // should do nothing 390 | Reveal.next(); // should trigger 391 | Reveal.slide( 3, 0 ); // should trigger 392 | Reveal.next(); // should do nothing 393 | 394 | start(); 395 | 396 | Reveal.removeEventListener( 'slidechanged', _onEvent ); 397 | 398 | }); 399 | 400 | asyncTest( 'paused', function() { 401 | expect( 1 ); 402 | 403 | var _onEvent = function( event ) { 404 | ok( true, 'event fired' ); 405 | } 406 | 407 | Reveal.addEventListener( 'paused', _onEvent ); 408 | 409 | Reveal.togglePause(); 410 | Reveal.togglePause(); 411 | 412 | start(); 413 | 414 | Reveal.removeEventListener( 'paused', _onEvent ); 415 | }); 416 | 417 | asyncTest( 'resumed', function() { 418 | expect( 1 ); 419 | 420 | var _onEvent = function( event ) { 421 | ok( true, 'event fired' ); 422 | } 423 | 424 | Reveal.addEventListener( 'resumed', _onEvent ); 425 | 426 | Reveal.togglePause(); 427 | Reveal.togglePause(); 428 | 429 | start(); 430 | 431 | Reveal.removeEventListener( 'resumed', _onEvent ); 432 | }); 433 | 434 | 435 | } ); 436 | 437 | Reveal.initialize(); 438 | 439 | --------------------------------------------------------------------------------