diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..6f6ea28 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,191 @@ +/* global module:false */ +module.exports = function(grunt) { + var port = grunt.option("port") || 8000; + var root = grunt.option("root") || "."; + + if (!Array.isArray(root)) root = [root]; + + // Project configuration + grunt.initConfig({ + pkg: grunt.file.readJSON("package.json"), + meta: { + banner: + "/*!\n" + + ' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' + + " * http://revealjs.com\n" + + " * MIT licensed\n" + + " *\n" + + " * Copyright (C) 2018 Hakim El Hattab, http://hakim.se\n" + + " */" + }, + + qunit: { + files: ["test/*.html"] + }, + + uglify: { + options: { + banner: "<%= meta.banner %>\n", + ie8: true + }, + build: { + src: "js/reveal.js", + dest: "js/reveal.min.js" + } + }, + + sass: { + core: { + src: "css/reveal.scss", + dest: "css/reveal.css" + }, + themes: { + expand: true, + cwd: "css/theme/source", + src: ["*.sass", "*.scss"], + dest: "css/theme", + ext: ".css" + } + }, + + autoprefixer: { + core: { + src: "css/reveal.css" + } + }, + + cssmin: { + options: { + compatibility: "ie9" + }, + compress: { + src: "css/reveal.css", + dest: "css/reveal.min.css" + } + }, + + jshint: { + options: { + curly: false, + eqeqeq: true, + immed: true, + esnext: true, + latedef: "nofunc", + newcap: true, + noarg: true, + sub: true, + undef: true, + eqnull: true, + browser: true, + expr: true, + loopfunc: true, + globals: { + head: false, + module: false, + console: false, + unescape: false, + define: false, + exports: false + } + }, + files: ["Gruntfile.js", "js/reveal.js"] + }, + + connect: { + server: { + options: { + port: port, + base: root, + livereload: true, + open: true, + useAvailablePort: true + } + } + }, + + zip: { + bundle: { + src: [ + "index.html", + "css/**", + "js/**", + "lib/**", + "images/**", + "plugin/**", + "**.md" + ], + dest: "reveal-js-presentation.zip" + } + }, + + watch: { + js: { + files: ["Gruntfile.js", "js/reveal.js"], + tasks: "js" + }, + theme: { + files: [ + "css/theme/source/*.sass", + "css/theme/source/*.scss", + "css/theme/template/*.sass", + "css/theme/template/*.scss" + ], + tasks: "css-themes" + }, + css: { + files: ["css/reveal.scss"], + tasks: "css-core" + }, + html: { + files: root.map(path => path + "/*.html") + }, + markdown: { + files: root.map(path => path + "/*.md") + }, + options: { + livereload: true + } + }, + + retire: { + js: ["js/reveal.js", "lib/js/*.js", "plugin/**/*.js"], + node: ["."] + } + }); + + // Dependencies + grunt.loadNpmTasks("grunt-contrib-connect"); + grunt.loadNpmTasks("grunt-contrib-cssmin"); + grunt.loadNpmTasks("grunt-contrib-jshint"); + grunt.loadNpmTasks("grunt-contrib-qunit"); + grunt.loadNpmTasks("grunt-contrib-uglify"); + grunt.loadNpmTasks("grunt-contrib-watch"); + grunt.loadNpmTasks("grunt-autoprefixer"); + grunt.loadNpmTasks("grunt-retire"); + grunt.loadNpmTasks("grunt-sass"); + grunt.loadNpmTasks("grunt-zip"); + + // Default task + grunt.registerTask("default", ["css", "js"]); + + // JS task + grunt.registerTask("js", ["jshint", "uglify", "qunit"]); + + // Theme CSS + grunt.registerTask("css-themes", ["sass:themes"]); + + // Core framework CSS + grunt.registerTask("css-core", ["sass:core", "autoprefixer", "cssmin"]); + + // All CSS + grunt.registerTask("css", ["sass", "autoprefixer", "cssmin"]); + + // Package presentation to archive + grunt.registerTask("package", ["default", "zip"]); + + // Serve presentation locally + grunt.registerTask("serve", ["connect", "watch"]); + + // Run tests + grunt.registerTask("test", ["jshint", "qunit"]); +}; diff --git a/css/print/paper.css b/css/print/paper.css index 7c7257a..27d19dd 100644 --- a/css/print/paper.css +++ b/css/print/paper.css @@ -38,7 +38,8 @@ .share-reveal, .state-background, .reveal .progress, - .reveal .backgrounds { + .reveal .backgrounds, + .reveal .slide-number { display: none !important; } @@ -141,7 +142,7 @@ .reveal .slides section { visibility: visible !important; position: static !important; - width: 100% !important; + width: auto !important; height: auto !important; display: block !important; overflow: visible !important; @@ -199,4 +200,4 @@ font-size: 0.8em; } -} \ No newline at end of file +} diff --git a/css/print/pdf.css b/css/print/pdf.css index 2eb4cf2..752d955 100644 --- a/css/print/pdf.css +++ b/css/print/pdf.css @@ -1,15 +1,9 @@ -/* Default Print Stylesheet Template - by Rob Glazebrook of CSSnewbie.com - Last Updated: June 4, 2008 - - Feel free (nay, compelled) to edit, append, and - manipulate this file as you see fit. */ - - -/* SECTION 1: Set default width, margin, float, and - background. This prevents elements from extending - beyond the edge of the printed page, and prevents - unnecessary background images from printing */ +/** + * This stylesheet is used to print reveal.js + * presentations to PDF. + * + * https://github.com/hakimel/reveal.js#pdf-export + */ * { -webkit-print-color-adjust: exact; @@ -29,12 +23,10 @@ html { overflow: visible; } -/* SECTION 2: Remove any elements not needed in print. - This would include navigation, ads, sidebars, etc. */ +/* Remove any elements not needed in print. */ .nestedarrow, .reveal .controls, .reveal .progress, -.reveal .slide-number, .reveal .playback, .reveal.overview, .fork-reveal, @@ -43,16 +35,7 @@ html { display: none !important; } -/* SECTION 3: Set body font face, size, and color. - Consider using a serif font for readability. */ -body, p, td, li, div { - -} - -/* SECTION 4: Set heading font face, sizes, and color. - Differentiate your headings from your body text. - Perhaps use a large sans-serif for distinction. */ -h1,h2,h3,h4,h5,h6 { +h1, h2, h3, h4, h5, h6 { text-shadow: 0 0 0 #000 !important; } @@ -61,8 +44,6 @@ h1,h2,h3,h4,h5,h6 { font-family: Courier, 'Courier New', monospace !important; } - -/* SECTION 5: more reveal.js specific additions by @skypanther */ ul, ol, div, p { visibility: visible; position: static; @@ -79,8 +60,9 @@ ul, ol, div, p { } .reveal .slides { position: static; - width: 100%; - height: auto; + width: 100% !important; + height: auto !important; + zoom: 1 !important; left: auto; top: auto; @@ -90,23 +72,22 @@ ul, ol, div, p { overflow: visible; display: block; - -webkit-perspective: none; - -moz-perspective: none; - -ms-perspective: none; - perspective: none; + perspective: none; + perspective-origin: 50% 50%; +} - -webkit-perspective-origin: 50% 50%; /* there isn't a none/auto value but 50-50 is the default */ - -moz-perspective-origin: 50% 50%; - -ms-perspective-origin: 50% 50%; - perspective-origin: 50% 50%; +.reveal .slides .pdf-page { + position: relative; + overflow: hidden; + z-index: 1; + + page-break-after: always; } -.reveal .slides section { - page-break-after: always !important; +.reveal .slides section { visibility: visible !important; - position: relative !important; display: block !important; - position: relative !important; + position: absolute !important; margin: 0 !important; padding: 0 !important; @@ -115,43 +96,69 @@ ul, ol, div, p { opacity: 1 !important; - -webkit-transform-style: flat !important; - -moz-transform-style: flat !important; - -ms-transform-style: flat !important; - transform-style: flat !important; - - -webkit-transform: none !important; - -moz-transform: none !important; - -ms-transform: none !important; - transform: none !important; + transform-style: flat !important; + transform: none !important; } + .reveal section.stack { + position: relative !important; margin: 0 !important; padding: 0 !important; page-break-after: avoid !important; height: auto !important; min-height: auto !important; } + .reveal img { box-shadow: none; } + .reveal .roll { overflow: visible; line-height: 1em; } /* Slide backgrounds are placed inside of their slide when exporting to PDF */ -.reveal section .slide-background { +.reveal .slide-background { display: block !important; position: absolute; top: 0; left: 0; width: 100%; - z-index: -1; + height: 100%; + z-index: auto !important; } -/* All elements should be above the slide-background */ -.reveal section>* { + +/* Display slide speaker notes when 'showNotes' is enabled */ +.reveal.show-notes { + max-width: none; + max-height: none; +} +.reveal .speaker-notes-pdf { + display: block; + width: 100%; + height: auto; + max-height: none; + top: auto; + right: auto; + bottom: auto; + left: auto; + z-index: 100; +} + +/* Layout option which makes notes appear on a separate page */ +.reveal .speaker-notes-pdf[data-layout="separate-page"] { position: relative; - z-index: 1; + color: inherit; + background-color: transparent; + padding: 20px; + page-break-after: always; + border: 0; } +/* Display slide numbers when 'slideNumber' is enabled */ +.reveal .slide-number-pdf { + display: block; + position: absolute; + font-size: 14px; +} diff --git a/css/reveal.css b/css/reveal.css index 258e975..eda311e 100644 --- a/css/reveal.css +++ b/css/reveal.css @@ -1,14 +1,26 @@ /*! * reveal.js - * http://lab.hakim.se/reveal-js + * http://revealjs.com * MIT licensed * - * Copyright (C) 2015 Hakim El Hattab, http://hakim.se + * Copyright (C) 2018 Hakim El Hattab, http://hakim.se */ /********************************************* * RESET STYLES *********************************************/ -html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe, .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre, .reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code, .reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp, .reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var, .reveal b, .reveal u, .reveal center, .reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li, .reveal fieldset, .reveal form, .reveal label, .reveal legend, .reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td, .reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, .reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, .reveal time, .reveal mark, .reveal audio, video { +html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe, +.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre, +.reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code, +.reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp, +.reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var, +.reveal b, .reveal u, .reveal center, +.reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li, +.reveal fieldset, .reveal form, .reveal label, .reveal legend, +.reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td, +.reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, +.reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, +.reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, +.reveal time, .reveal mark, .reveal audio, .reveal video { margin: 0; padding: 0; border: 0; @@ -16,13 +28,15 @@ html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal i font: inherit; vertical-align: baseline; } -.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure, .reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section { +.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure, +.reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section { display: block; } /********************************************* * GLOBAL STYLES *********************************************/ -html, body { +html, +body { width: 100%; height: 100%; overflow: hidden; } @@ -39,66 +53,107 @@ body { .reveal .slides section .fragment { opacity: 0; visibility: hidden; - -webkit-transition: all 0.2s ease; - transition: all 0.2s ease; } + transition: all .2s ease; } .reveal .slides section .fragment.visible { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.grow { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.grow.visible { -webkit-transform: scale(1.3); - -ms-transform: scale(1.3); transform: scale(1.3); } .reveal .slides section .fragment.shrink { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.shrink.visible { -webkit-transform: scale(0.7); - -ms-transform: scale(0.7); transform: scale(0.7); } .reveal .slides section .fragment.zoom-in { -webkit-transform: scale(0.1); - -ms-transform: scale(0.1); transform: scale(0.1); } .reveal .slides section .fragment.zoom-in.visible { -webkit-transform: none; - -ms-transform: none; transform: none; } .reveal .slides section .fragment.fade-out { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.fade-out.visible { opacity: 0; visibility: hidden; } .reveal .slides section .fragment.semi-fade-out { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.semi-fade-out.visible { opacity: 0.5; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.strike { - opacity: 1; } + opacity: 1; + visibility: inherit; } .reveal .slides section .fragment.strike.visible { text-decoration: line-through; } +.reveal .slides section .fragment.fade-up { + -webkit-transform: translate(0, 20%); + transform: translate(0, 20%); } + .reveal .slides section .fragment.fade-up.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-down { + -webkit-transform: translate(0, -20%); + transform: translate(0, -20%); } + .reveal .slides section .fragment.fade-down.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-right { + -webkit-transform: translate(-20%, 0); + transform: translate(-20%, 0); } + .reveal .slides section .fragment.fade-right.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-left { + -webkit-transform: translate(20%, 0); + transform: translate(20%, 0); } + .reveal .slides section .fragment.fade-left.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-in-then-out, .reveal .slides section .fragment.current-visible { opacity: 0; visibility: hidden; } + .reveal .slides section .fragment.fade-in-then-out.current-fragment, .reveal .slides section .fragment.current-visible.current-fragment { opacity: 1; - visibility: visible; } + visibility: inherit; } -.reveal .slides section .fragment.highlight-red, .reveal .slides section .fragment.highlight-current-red, .reveal .slides section .fragment.highlight-green, .reveal .slides section .fragment.highlight-current-green, .reveal .slides section .fragment.highlight-blue, .reveal .slides section .fragment.highlight-current-blue { +.reveal .slides section .fragment.fade-in-then-semi-out { + opacity: 0; + visibility: hidden; } + .reveal .slides section .fragment.fade-in-then-semi-out.visible { + opacity: 0.5; + visibility: inherit; } + .reveal .slides section .fragment.fade-in-then-semi-out.current-fragment { + opacity: 1; + visibility: inherit; } + +.reveal .slides section .fragment.highlight-red, +.reveal .slides section .fragment.highlight-current-red, +.reveal .slides section .fragment.highlight-green, +.reveal .slides section .fragment.highlight-current-green, +.reveal .slides section .fragment.highlight-blue, +.reveal .slides section .fragment.highlight-current-blue { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.highlight-red.visible { color: #ff2c2d; } @@ -140,116 +195,288 @@ body { .reveal pre.stretch code { height: 100%; max-height: 100%; - -moz-box-sizing: border-box; - box-sizing: border-box; } + box-sizing: border-box; } /********************************************* * CONTROLS *********************************************/ +@-webkit-keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateX(0); + transform: translateX(0); } + 20% { + -webkit-transform: translateX(10px); + transform: translateX(10px); } + 30% { + -webkit-transform: translateX(-5px); + transform: translateX(-5px); } } +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateX(0); + transform: translateX(0); } + 20% { + -webkit-transform: translateX(10px); + transform: translateX(10px); } + 30% { + -webkit-transform: translateX(-5px); + transform: translateX(-5px); } } + +@-webkit-keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateY(0); + transform: translateY(0); } + 20% { + -webkit-transform: translateY(10px); + transform: translateY(10px); } + 30% { + -webkit-transform: translateY(-5px); + transform: translateY(-5px); } } + +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateY(0); + transform: translateY(0); } + 20% { + -webkit-transform: translateY(10px); + transform: translateY(10px); } + 30% { + -webkit-transform: translateY(-5px); + transform: translateY(-5px); } } + .reveal .controls { display: none; - position: fixed; - width: 110px; - height: 110px; - z-index: 30; - right: 10px; - bottom: 10px; - -webkit-user-select: none; } - -.reveal .controls div { position: absolute; - opacity: 0.05; - width: 0; - height: 0; - border: 12px solid transparent; - -webkit-transform: scale(0.9999); - -ms-transform: scale(0.9999); - transform: scale(0.9999); - -webkit-transition: all 0.2s ease; - transition: all 0.2s ease; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - -.reveal .controls div.enabled { - opacity: 0.7; - cursor: pointer; } - -.reveal .controls div.enabled:active { - margin-top: 1px; } - -.reveal .controls div.navigate-left { - top: 42px; - border-right-width: 22px; - border-right-color: #000; } + top: auto; + bottom: 12px; + right: 12px; + left: auto; + z-index: 1; + color: #000; + pointer-events: none; + font-size: 10px; } + .reveal .controls button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + -webkit-transform: scale(0.9999); + transform: scale(0.9999); + transition: color 0.2s ease, opacity 0.2s ease, -webkit-transform 0.2s ease; + transition: color 0.2s ease, opacity 0.2s ease, transform 0.2s ease; + z-index: 2; + pointer-events: auto; + font-size: inherit; + visibility: hidden; + opacity: 0; + -webkit-appearance: none; + -webkit-tap-highlight-color: transparent; } + .reveal .controls .controls-arrow:before, + .reveal .controls .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 2.6em; + height: 0.5em; + border-radius: 0.25em; + background-color: currentColor; + transition: all 0.15s ease, background-color 0.8s ease; + -webkit-transform-origin: 0.2em 50%; + transform-origin: 0.2em 50%; + will-change: transform; } + .reveal .controls .controls-arrow { + position: relative; + width: 3.6em; + height: 3.6em; } + .reveal .controls .controls-arrow:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(45deg); } + .reveal .controls .controls-arrow:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); } + .reveal .controls .controls-arrow:hover:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(40deg); + transform: translateX(0.5em) translateY(1.55em) rotate(40deg); } + .reveal .controls .controls-arrow:hover:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); } + .reveal .controls .controls-arrow:active:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(36deg); + transform: translateX(0.5em) translateY(1.55em) rotate(36deg); } + .reveal .controls .controls-arrow:active:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); } + .reveal .controls .navigate-left { + right: 6.4em; + bottom: 3.2em; + -webkit-transform: translateX(-10px); + transform: translateX(-10px); } + .reveal .controls .navigate-right { + right: 0; + bottom: 3.2em; + -webkit-transform: translateX(10px); + transform: translateX(10px); } + .reveal .controls .navigate-right .controls-arrow { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); } + .reveal .controls .navigate-right.highlight { + -webkit-animation: bounce-right 2s 50 both ease-out; + animation: bounce-right 2s 50 both ease-out; } + .reveal .controls .navigate-up { + right: 3.2em; + bottom: 6.4em; + -webkit-transform: translateY(-10px); + transform: translateY(-10px); } + .reveal .controls .navigate-up .controls-arrow { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + .reveal .controls .navigate-down { + right: 3.2em; + bottom: 0; + -webkit-transform: translateY(10px); + transform: translateY(10px); } + .reveal .controls .navigate-down .controls-arrow { + -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); } + .reveal .controls .navigate-down.highlight { + -webkit-animation: bounce-down 2s 50 both ease-out; + animation: bounce-down 2s 50 both ease-out; } + .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled, + .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; } + .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled:hover, + .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled:hover { + opacity: 1; } + .reveal .controls[data-controls-back-arrows="hidden"] .navigate-left.enabled, + .reveal .controls[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; } + .reveal .controls .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + -webkit-transform: none; + transform: none; } + .reveal .controls .enabled.fragmented { + opacity: 0.5; } + .reveal .controls .enabled:hover, + .reveal .controls .enabled.fragmented:hover { + opacity: 1; } -.reveal .controls div.navigate-left.fragmented { - opacity: 0.3; } +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: 1.4em; + right: 5.5em; } -.reveal .controls div.navigate-right { - left: 74px; - top: 42px; - border-left-width: 22px; - border-left-color: #000; } +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: 1.4em; + right: 0.5em; } -.reveal .controls div.navigate-right.fragmented { - opacity: 0.3; } +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: 1.4em; + bottom: 5em; } -.reveal .controls div.navigate-up { - left: 42px; - border-bottom-width: 22px; - border-bottom-color: #000; } +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: 1.4em; + bottom: 0.5em; } -.reveal .controls div.navigate-up.fragmented { - opacity: 0.3; } +.reveal.has-dark-background .controls { + color: #fff; } -.reveal .controls div.navigate-down { - left: 42px; - top: 74px; - border-top-width: 22px; - border-top-color: #000; } +.reveal.has-light-background .controls { + color: #000; } -.reveal .controls div.navigate-down.fragmented { - opacity: 0.3; } +.reveal.no-hover .controls .controls-arrow:hover:before, +.reveal.no-hover .controls .controls-arrow:active:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(45deg); } + +.reveal.no-hover .controls .controls-arrow:hover:after, +.reveal.no-hover .controls .controls-arrow:active:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); } + +@media screen and (min-width: 500px) { + .reveal .controls[data-controls-layout="edges"] { + top: 0; + right: 0; + bottom: 0; + left: 0; } + .reveal .controls[data-controls-layout="edges"] .navigate-left, + .reveal .controls[data-controls-layout="edges"] .navigate-right, + .reveal .controls[data-controls-layout="edges"] .navigate-up, + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: auto; + right: auto; } + .reveal .controls[data-controls-layout="edges"] .navigate-left { + top: 50%; + left: 8px; + margin-top: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-right { + top: 50%; + right: 8px; + margin-top: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-up { + top: 8px; + left: 50%; + margin-left: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: 8px; + left: 50%; + margin-left: -1.8em; } } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - position: fixed; + position: absolute; display: none; height: 3px; width: 100%; bottom: 0; left: 0; z-index: 10; - background-color: rgba(0, 0, 0, 0.2); } + background-color: rgba(0, 0, 0, 0.2); + color: #fff; } .reveal .progress:after { content: ''; display: block; position: absolute; - height: 20px; + height: 10px; width: 100%; - top: -20px; } + top: -10px; } .reveal .progress span { display: block; height: 100%; width: 0px; - background-color: #000; - -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); - transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + background-color: currentColor; + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* * SLIDE NUMBER *********************************************/ .reveal .slide-number { - position: fixed; + position: absolute; display: block; - right: 15px; - bottom: 15px; - opacity: 0.5; + right: 8px; + bottom: 8px; z-index: 31; - font-size: 12px; } + font-family: Helvetica, sans-serif; + font-size: 12px; + line-height: 1; + color: #fff; + background-color: rgba(0, 0, 0, 0.4); + padding: 5px; } + +.reveal .slide-number a { + color: currentColor; } + +.reveal .slide-number-delimiter { + margin: 0 3px; } /********************************************* * SLIDES @@ -262,6 +489,10 @@ body { -ms-touch-action: none; touch-action: none; } +@media only screen and (orientation: landscape) { + .reveal.ua-iphone { + position: fixed; } } + .reveal .slides { position: absolute; width: 100%; @@ -271,6 +502,7 @@ body { bottom: 0; left: 0; margin: auto; + pointer-events: none; overflow: visible; z-index: 1; text-align: center; @@ -282,56 +514,70 @@ body { .reveal .slides > section { -ms-perspective: 600px; } -.reveal .slides > section, .reveal .slides > section > section { +.reveal .slides > section, +.reveal .slides > section > section { display: none; position: absolute; width: 100%; padding: 20px 0px; + pointer-events: auto; z-index: 10; - -webkit-transform-style: preserve-3d; - transform-style: preserve-3d; - -webkit-transition: -webkit-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), -webkit-transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); - transition: -ms-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); - transition: transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + -webkit-transform-style: flat; + transform-style: flat; + transition: -webkit-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), -webkit-transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /* Global transition speed settings */ .reveal[data-transition-speed="fast"] .slides section { - -webkit-transition-duration: 400ms; - transition-duration: 400ms; } + transition-duration: 400ms; } .reveal[data-transition-speed="slow"] .slides section { - -webkit-transition-duration: 1200ms; - transition-duration: 1200ms; } + transition-duration: 1200ms; } /* Slide-specific transition speed overrides */ .reveal .slides section[data-transition-speed="fast"] { - -webkit-transition-duration: 400ms; - transition-duration: 400ms; } + transition-duration: 400ms; } .reveal .slides section[data-transition-speed="slow"] { - -webkit-transition-duration: 1200ms; - transition-duration: 1200ms; } + transition-duration: 1200ms; } .reveal .slides > section.stack { padding-top: 0; - padding-bottom: 0; } + padding-bottom: 0; + pointer-events: none; } -.reveal .slides > section.present, .reveal .slides > section > section.present { +.reveal .slides > section.present, +.reveal .slides > section > section.present { display: block; z-index: 11; opacity: 1; } -.reveal.center, .reveal.center .slides, .reveal.center .slides section { +.reveal .slides > section:empty, +.reveal .slides > section > section:empty, +.reveal .slides > section[data-background-interactive], +.reveal .slides > section > section[data-background-interactive] { + pointer-events: none; } + +.reveal.center, +.reveal.center .slides, +.reveal.center .slides section { min-height: 0 !important; } /* Don't allow interaction with invisible slides */ -.reveal .slides > section.future, .reveal .slides > section > section.future, .reveal .slides > section.past, .reveal .slides > section > section.past { +.reveal .slides > section.future, +.reveal .slides > section > section.future, +.reveal .slides > section.past, +.reveal .slides > section > section.past { pointer-events: none; } -.reveal.overview .slides > section, .reveal.overview .slides > section > section { +.reveal.overview .slides > section, +.reveal.overview .slides > section > section { pointer-events: auto; } -.reveal .slides > section.past, .reveal .slides > section.future, .reveal .slides > section > section.past, .reveal .slides > section > section.future { +.reveal .slides > section.past, +.reveal .slides > section.future, +.reveal .slides > section > section.past, +.reveal .slides > section > section.future { opacity: 0; } /********************************************* @@ -345,136 +591,191 @@ body { -webkit-backface-visibility: hidden; backface-visibility: hidden; } -.reveal .slides > section[data-transition=slide].past, .reveal .slides > section[data-transition~=slide-out].past, .reveal.slide .slides > section:not([data-transition]).past { +.reveal .slides > section[data-transition=slide].past, +.reveal .slides > section[data-transition~=slide-out].past, +.reveal.slide .slides > section:not([data-transition]).past { -webkit-transform: translate(-150%, 0); - -ms-transform: translate(-150%, 0); transform: translate(-150%, 0); } -.reveal .slides > section[data-transition=slide].future, .reveal .slides > section[data-transition~=slide-in].future, .reveal.slide .slides > section:not([data-transition]).future { +.reveal .slides > section[data-transition=slide].future, +.reveal .slides > section[data-transition~=slide-in].future, +.reveal.slide .slides > section:not([data-transition]).future { -webkit-transform: translate(150%, 0); - -ms-transform: translate(150%, 0); transform: translate(150%, 0); } -.reveal .slides > section > section[data-transition=slide].past, .reveal .slides > section > section[data-transition~=slide-out].past, .reveal.slide .slides > section > section:not([data-transition]).past { +.reveal .slides > section > section[data-transition=slide].past, +.reveal .slides > section > section[data-transition~=slide-out].past, +.reveal.slide .slides > section > section:not([data-transition]).past { -webkit-transform: translate(0, -150%); - -ms-transform: translate(0, -150%); transform: translate(0, -150%); } -.reveal .slides > section > section[data-transition=slide].future, .reveal .slides > section > section[data-transition~=slide-in].future, .reveal.slide .slides > section > section:not([data-transition]).future { +.reveal .slides > section > section[data-transition=slide].future, +.reveal .slides > section > section[data-transition~=slide-in].future, +.reveal.slide .slides > section > section:not([data-transition]).future { -webkit-transform: translate(0, 150%); - -ms-transform: translate(0, 150%); transform: translate(0, 150%); } .reveal.linear section { -webkit-backface-visibility: hidden; backface-visibility: hidden; } -.reveal .slides > section[data-transition=linear].past, .reveal .slides > section[data-transition~=linear-out].past, .reveal.linear .slides > section:not([data-transition]).past { +.reveal .slides > section[data-transition=linear].past, +.reveal .slides > section[data-transition~=linear-out].past, +.reveal.linear .slides > section:not([data-transition]).past { -webkit-transform: translate(-150%, 0); - -ms-transform: translate(-150%, 0); transform: translate(-150%, 0); } -.reveal .slides > section[data-transition=linear].future, .reveal .slides > section[data-transition~=linear-in].future, .reveal.linear .slides > section:not([data-transition]).future { +.reveal .slides > section[data-transition=linear].future, +.reveal .slides > section[data-transition~=linear-in].future, +.reveal.linear .slides > section:not([data-transition]).future { -webkit-transform: translate(150%, 0); - -ms-transform: translate(150%, 0); transform: translate(150%, 0); } -.reveal .slides > section > section[data-transition=linear].past, .reveal .slides > section > section[data-transition~=linear-out].past, .reveal.linear .slides > section > section:not([data-transition]).past { +.reveal .slides > section > section[data-transition=linear].past, +.reveal .slides > section > section[data-transition~=linear-out].past, +.reveal.linear .slides > section > section:not([data-transition]).past { -webkit-transform: translate(0, -150%); - -ms-transform: translate(0, -150%); transform: translate(0, -150%); } -.reveal .slides > section > section[data-transition=linear].future, .reveal .slides > section > section[data-transition~=linear-in].future, .reveal.linear .slides > section > section:not([data-transition]).future { +.reveal .slides > section > section[data-transition=linear].future, +.reveal .slides > section > section[data-transition~=linear-in].future, +.reveal.linear .slides > section > section:not([data-transition]).future { -webkit-transform: translate(0, 150%); - -ms-transform: translate(0, 150%); transform: translate(0, 150%); } /********************************************* * CONVEX TRANSITION * Aliased 'default' for backwards compatibility *********************************************/ -.reveal .slides > section[data-transition=default].past, .reveal .slides > section[data-transition~=default-out].past, .reveal.default .slides > section:not([data-transition]).past { +.reveal .slides section[data-transition=default].stack, +.reveal.default .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=default].past, +.reveal .slides > section[data-transition~=default-out].past, +.reveal.default .slides > section:not([data-transition]).past { -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } -.reveal .slides > section[data-transition=default].future, .reveal .slides > section[data-transition~=default-in].future, .reveal.default .slides > section:not([data-transition]).future { +.reveal .slides > section[data-transition=default].future, +.reveal .slides > section[data-transition~=default-in].future, +.reveal.default .slides > section:not([data-transition]).future { -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } -.reveal .slides > section > section[data-transition=default].past, .reveal .slides > section > section[data-transition~=default-out].past, .reveal.default .slides > section > section:not([data-transition]).past { +.reveal .slides > section > section[data-transition=default].past, +.reveal .slides > section > section[data-transition~=default-out].past, +.reveal.default .slides > section > section:not([data-transition]).past { -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); } -.reveal .slides > section > section[data-transition=default].future, .reveal .slides > section > section[data-transition~=default-in].future, .reveal.default .slides > section > section:not([data-transition]).future { +.reveal .slides > section > section[data-transition=default].future, +.reveal .slides > section > section[data-transition~=default-in].future, +.reveal.default .slides > section > section:not([data-transition]).future { -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); } -.reveal .slides > section[data-transition=convex].past, .reveal .slides > section[data-transition~=convex-out].past, .reveal.convex .slides > section:not([data-transition]).past { +.reveal .slides section[data-transition=convex].stack, +.reveal.convex .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=convex].past, +.reveal .slides > section[data-transition~=convex-out].past, +.reveal.convex .slides > section:not([data-transition]).past { -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } -.reveal .slides > section[data-transition=convex].future, .reveal .slides > section[data-transition~=convex-in].future, .reveal.convex .slides > section:not([data-transition]).future { +.reveal .slides > section[data-transition=convex].future, +.reveal .slides > section[data-transition~=convex-in].future, +.reveal.convex .slides > section:not([data-transition]).future { -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } -.reveal .slides > section > section[data-transition=convex].past, .reveal .slides > section > section[data-transition~=convex-out].past, .reveal.convex .slides > section > section:not([data-transition]).past { +.reveal .slides > section > section[data-transition=convex].past, +.reveal .slides > section > section[data-transition~=convex-out].past, +.reveal.convex .slides > section > section:not([data-transition]).past { -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); } -.reveal .slides > section > section[data-transition=convex].future, .reveal .slides > section > section[data-transition~=convex-in].future, .reveal.convex .slides > section > section:not([data-transition]).future { +.reveal .slides > section > section[data-transition=convex].future, +.reveal .slides > section > section[data-transition~=convex-in].future, +.reveal.convex .slides > section > section:not([data-transition]).future { -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); } /********************************************* * CONCAVE TRANSITION *********************************************/ -.reveal .slides > section[data-transition=concave].past, .reveal .slides > section[data-transition~=concave-out].past, .reveal.concave .slides > section:not([data-transition]).past { +.reveal .slides section[data-transition=concave].stack, +.reveal.concave .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=concave].past, +.reveal .slides > section[data-transition~=concave-out].past, +.reveal.concave .slides > section:not([data-transition]).past { -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); } -.reveal .slides > section[data-transition=concave].future, .reveal .slides > section[data-transition~=concave-in].future, .reveal.concave .slides > section:not([data-transition]).future { +.reveal .slides > section[data-transition=concave].future, +.reveal .slides > section[data-transition~=concave-in].future, +.reveal.concave .slides > section:not([data-transition]).future { -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); } -.reveal .slides > section > section[data-transition=concave].past, .reveal .slides > section > section[data-transition~=concave-out].past, .reveal.concave .slides > section > section:not([data-transition]).past { +.reveal .slides > section > section[data-transition=concave].past, +.reveal .slides > section > section[data-transition~=concave-out].past, +.reveal.concave .slides > section > section:not([data-transition]).past { -webkit-transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); } -.reveal .slides > section > section[data-transition=concave].future, .reveal .slides > section > section[data-transition~=concave-in].future, .reveal.concave .slides > section > section:not([data-transition]).future { +.reveal .slides > section > section[data-transition=concave].future, +.reveal .slides > section > section[data-transition~=concave-in].future, +.reveal.concave .slides > section > section:not([data-transition]).future { -webkit-transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); } /********************************************* * ZOOM TRANSITION *********************************************/ -.reveal .slides > section[data-transition=zoom], .reveal.zoom .slides > section:not([data-transition]) { - -webkit-transition-timing-function: ease; - transition-timing-function: ease; } +.reveal .slides section[data-transition=zoom], +.reveal.zoom .slides section:not([data-transition]) { + transition-timing-function: ease; } -.reveal .slides > section[data-transition=zoom].past, .reveal .slides > section[data-transition~=zoom-out].past, .reveal.zoom .slides > section:not([data-transition]).past { +.reveal .slides > section[data-transition=zoom].past, +.reveal .slides > section[data-transition~=zoom-out].past, +.reveal.zoom .slides > section:not([data-transition]).past { visibility: hidden; -webkit-transform: scale(16); - -ms-transform: scale(16); transform: scale(16); } -.reveal .slides > section[data-transition=zoom].future, .reveal .slides > section[data-transition~=zoom-in].future, .reveal.zoom .slides > section:not([data-transition]).future { +.reveal .slides > section[data-transition=zoom].future, +.reveal .slides > section[data-transition~=zoom-in].future, +.reveal.zoom .slides > section:not([data-transition]).future { visibility: hidden; -webkit-transform: scale(0.2); - -ms-transform: scale(0.2); transform: scale(0.2); } -.reveal .slides > section > section[data-transition=zoom].past, .reveal .slides > section > section[data-transition~=zoom-out].past, .reveal.zoom .slides > section > section:not([data-transition]).past { +.reveal .slides > section > section[data-transition=zoom].past, +.reveal .slides > section > section[data-transition~=zoom-out].past, +.reveal.zoom .slides > section > section:not([data-transition]).past { -webkit-transform: translate(0, -150%); - -ms-transform: translate(0, -150%); transform: translate(0, -150%); } -.reveal .slides > section > section[data-transition=zoom].future, .reveal .slides > section > section[data-transition~=zoom-in].future, .reveal.zoom .slides > section > section:not([data-transition]).future { +.reveal .slides > section > section[data-transition=zoom].future, +.reveal .slides > section > section[data-transition~=zoom-in].future, +.reveal.zoom .slides > section > section:not([data-transition]).future { -webkit-transform: translate(0, 150%); - -ms-transform: translate(0, 150%); transform: translate(0, 150%); } /********************************************* * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. *********************************************/ .reveal.cube .slides { -webkit-perspective: 1300px; @@ -485,8 +786,9 @@ body { min-height: 700px; -webkit-backface-visibility: hidden; backface-visibility: hidden; - -moz-box-sizing: border-box; - box-sizing: border-box; } + box-sizing: border-box; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } .reveal.center.cube .slides section { min-height: 0; } @@ -525,34 +827,34 @@ body { .reveal.cube .slides > section.past { -webkit-transform-origin: 100% 0%; - -ms-transform-origin: 100% 0%; transform-origin: 100% 0%; -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg); transform: translate3d(-100%, 0, 0) rotateY(-90deg); } .reveal.cube .slides > section.future { -webkit-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; transform-origin: 0% 0%; -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg); transform: translate3d(100%, 0, 0) rotateY(90deg); } .reveal.cube .slides > section > section.past { -webkit-transform-origin: 0% 100%; - -ms-transform-origin: 0% 100%; transform-origin: 0% 100%; -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg); transform: translate3d(0, -100%, 0) rotateX(90deg); } .reveal.cube .slides > section > section.future { -webkit-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; transform-origin: 0% 0%; -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg); transform: translate3d(0, 100%, 0) rotateX(-90deg); } /********************************************* * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. *********************************************/ .reveal.page .slides { -webkit-perspective-origin: 0% 50%; @@ -563,8 +865,9 @@ body { .reveal.page .slides section { padding: 30px; min-height: 700px; - -moz-box-sizing: border-box; - box-sizing: border-box; } + box-sizing: border-box; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } .reveal.page .slides section.past { z-index: 12; } @@ -601,28 +904,24 @@ body { .reveal.page .slides > section.past { -webkit-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; transform-origin: 0% 0%; -webkit-transform: translate3d(-40%, 0, 0) rotateY(-80deg); transform: translate3d(-40%, 0, 0) rotateY(-80deg); } .reveal.page .slides > section.future { -webkit-transform-origin: 100% 0%; - -ms-transform-origin: 100% 0%; transform-origin: 100% 0%; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .reveal.page .slides > section > section.past { -webkit-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; transform-origin: 0% 0%; -webkit-transform: translate3d(0, -40%, 0) rotateX(80deg); transform: translate3d(0, -40%, 0) rotateX(80deg); } .reveal.page .slides > section > section.future { -webkit-transform-origin: 0% 100%; - -ms-transform-origin: 0% 100%; transform-origin: 0% 100%; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } @@ -630,26 +929,25 @@ body { /********************************************* * FADE TRANSITION *********************************************/ -.reveal .slides section[data-transition=fade], .reveal.fade .slides section:not([data-transition]), .reveal.fade .slides > section > section:not([data-transition]) { +.reveal .slides section[data-transition=fade], +.reveal.fade .slides section:not([data-transition]), +.reveal.fade .slides > section > section:not([data-transition]) { -webkit-transform: none; - -ms-transform: none; transform: none; - -webkit-transition: opacity 0.5s; - transition: opacity 0.5s; } + transition: opacity 0.5s; } -.reveal.fade.overview .slides section, .reveal.fade.overview .slides > section > section { - -webkit-transition: none; - transition: none; } +.reveal.fade.overview .slides section, +.reveal.fade.overview .slides > section > section { + transition: none; } /********************************************* * NO TRANSITION *********************************************/ -.reveal .slides > section[data-transition=none], .reveal.none .slides > section:not([data-transition]) { +.reveal .slides section[data-transition=none], +.reveal.none .slides section:not([data-transition]) { -webkit-transform: none; - -ms-transform: none; transform: none; - -webkit-transition: none; - transition: none; } + transition: none; } /********************************************* * PAUSED MODE @@ -664,8 +962,22 @@ body { visibility: hidden; opacity: 0; z-index: 100; - -webkit-transition: all 1s ease; - transition: all 1s ease; } + transition: all 1s ease; } + +.reveal .pause-overlay .resume-button { + position: absolute; + bottom: 20px; + right: 20px; + color: #ccc; + border-radius: 2px; + padding: 6px 14px; + border: 2px solid #ccc; + font-size: 16px; + background: transparent; + cursor: pointer; } + .reveal .pause-overlay .resume-button:hover { + color: #fff; + border-color: #fff; } .reveal.paused .pause-overlay { visibility: visible; @@ -686,7 +998,8 @@ body { margin: 0; text-align: center; } -.no-transforms .reveal .controls, .no-transforms .reveal .progress { +.no-transforms .reveal .controls, +.no-transforms .reveal .progress { display: none !important; } .no-transforms .reveal .slides section { @@ -699,15 +1012,14 @@ body { left: -50%; margin: 70px 0; -webkit-transform: none; - -ms-transform: none; transform: none; } .no-transforms .reveal .slides section section { left: 0; } -.reveal .no-transition, .reveal .no-transition * { - -webkit-transition: none !important; - transition: none !important; } +.reveal .no-transition, +.reveal .no-transition * { + transition: none !important; } /********************************************* * PER-SLIDE BACKGROUNDS @@ -728,19 +1040,25 @@ body { height: 100%; opacity: 0; visibility: hidden; - background-color: rgba(0, 0, 0, 0); + overflow: hidden; + background-color: transparent; + transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +.reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; background-position: 50% 50%; background-repeat: no-repeat; - background-size: cover; - -webkit-transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); - transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + background-size: cover; } .reveal .slide-background.stack { display: block; } .reveal .slide-background.present { opacity: 1; - visibility: visible; } + visibility: visible; + z-index: 2; } .print-pdf .reveal .slide-background { opacity: 1 !important; @@ -754,122 +1072,135 @@ body { max-width: none; max-height: none; top: 0; - left: 0; } + left: 0; + -o-object-fit: cover; + object-fit: cover; } + +.reveal .slide-background[data-background-size="contain"] video { + -o-object-fit: contain; + object-fit: contain; } /* Immediate transition style */ -.reveal[data-background-transition=none] > .backgrounds .slide-background, .reveal > .backgrounds .slide-background[data-background-transition=none] { - -webkit-transition: none; - transition: none; } +.reveal[data-background-transition=none] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=none] { + transition: none; } /* Slide */ -.reveal[data-background-transition=slide] > .backgrounds .slide-background, .reveal > .backgrounds .slide-background[data-background-transition=slide] { +.reveal[data-background-transition=slide] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=slide] { opacity: 1; -webkit-backface-visibility: hidden; backface-visibility: hidden; } -.reveal[data-background-transition=slide] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=slide] { +.reveal[data-background-transition=slide] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=slide] { -webkit-transform: translate(-100%, 0); - -ms-transform: translate(-100%, 0); transform: translate(-100%, 0); } -.reveal[data-background-transition=slide] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=slide] { +.reveal[data-background-transition=slide] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=slide] { -webkit-transform: translate(100%, 0); - -ms-transform: translate(100%, 0); transform: translate(100%, 0); } -.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=slide] { +.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=slide] { -webkit-transform: translate(0, -100%); - -ms-transform: translate(0, -100%); transform: translate(0, -100%); } -.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=slide] { +.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=slide] { -webkit-transform: translate(0, 100%); - -ms-transform: translate(0, 100%); transform: translate(0, 100%); } /* Convex */ -.reveal[data-background-transition=convex] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=convex] { +.reveal[data-background-transition=convex] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=convex] { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } -.reveal[data-background-transition=convex] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=convex] { +.reveal[data-background-transition=convex] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=convex] { opacity: 0; -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } -.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=convex] { +.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=convex] { opacity: 0; -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); } -.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=convex] { +.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=convex] { opacity: 0; -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); } /* Concave */ -.reveal[data-background-transition=concave] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=concave] { +.reveal[data-background-transition=concave] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=concave] { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); } -.reveal[data-background-transition=concave] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=concave] { +.reveal[data-background-transition=concave] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=concave] { opacity: 0; -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); } -.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=concave] { +.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=concave] { opacity: 0; -webkit-transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); } -.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=concave] { +.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=concave] { opacity: 0; -webkit-transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); } /* Zoom */ -.reveal[data-background-transition=zoom] > .backgrounds .slide-background, .reveal > .backgrounds .slide-background[data-background-transition=zoom] { - -webkit-transition-timing-function: ease; - transition-timing-function: ease; } +.reveal[data-background-transition=zoom] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=zoom] { + transition-timing-function: ease; } -.reveal[data-background-transition=zoom] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=zoom] { +.reveal[data-background-transition=zoom] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=zoom] { opacity: 0; visibility: hidden; -webkit-transform: scale(16); - -ms-transform: scale(16); transform: scale(16); } -.reveal[data-background-transition=zoom] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=zoom] { +.reveal[data-background-transition=zoom] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=zoom] { opacity: 0; visibility: hidden; -webkit-transform: scale(0.2); - -ms-transform: scale(0.2); transform: scale(0.2); } -.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=zoom] { +.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=zoom] { opacity: 0; visibility: hidden; -webkit-transform: scale(16); - -ms-transform: scale(16); transform: scale(16); } -.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=zoom] { +.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=zoom] { opacity: 0; visibility: hidden; -webkit-transform: scale(0.2); - -ms-transform: scale(0.2); transform: scale(0.2); } /* Global transition speed settings */ .reveal[data-transition-speed="fast"] > .backgrounds .slide-background { - -webkit-transition-duration: 400ms; - transition-duration: 400ms; } + transition-duration: 400ms; } .reveal[data-transition-speed="slow"] > .backgrounds .slide-background { - -webkit-transition-duration: 1200ms; - transition-duration: 1200ms; } + transition-duration: 1200ms; } /********************************************* * OVERVIEW @@ -879,22 +1210,25 @@ body { perspective-origin: 50% 50%; -webkit-perspective: 700px; perspective: 700px; } + .reveal.overview .slides { + -moz-transform-style: preserve-3d; } .reveal.overview .slides section { - height: 700px; + height: 100%; + top: 0 !important; opacity: 1 !important; overflow: hidden; visibility: visible !important; cursor: pointer; - -moz-box-sizing: border-box; - box-sizing: border-box; } - .reveal.overview .slides section:hover, .reveal.overview .slides section.present { + box-sizing: border-box; } + .reveal.overview .slides section:hover, + .reveal.overview .slides section.present { outline: 10px solid rgba(150, 150, 150, 0.4); outline-offset: 10px; } .reveal.overview .slides section .fragment { opacity: 1; - -webkit-transition: none; - transition: none; } - .reveal.overview .slides section:after, .reveal.overview .slides section:before { + transition: none; } + .reveal.overview .slides section:after, + .reveal.overview .slides section:before { display: none !important; } .reveal.overview .slides > section.stack { padding: 0; @@ -904,36 +1238,43 @@ body { overflow: visible; } .reveal.overview .backgrounds { -webkit-perspective: inherit; - perspective: inherit; } + perspective: inherit; + -moz-transform-style: preserve-3d; } .reveal.overview .backgrounds .slide-background { opacity: 1; visibility: visible; outline: 10px solid rgba(150, 150, 150, 0.1); outline-offset: 10px; } + .reveal.overview .backgrounds .slide-background.stack { + overflow: visible; } -.reveal.overview .slides section, .reveal.overview-deactivating .slides section { - -webkit-transition: none; - transition: none; } - -.reveal.overview .backgrounds .slide-background, .reveal.overview-deactivating .backgrounds .slide-background { - -webkit-transition: none; - transition: none; } +.reveal.overview .slides section, +.reveal.overview-deactivating .slides section { + transition: none; } -.reveal.overview-animated .slides { - -webkit-transition: -webkit-transform 0.4s ease; - transition: transform 0.4s ease; } +.reveal.overview .backgrounds .slide-background, +.reveal.overview-deactivating .backgrounds .slide-background { + transition: none; } /********************************************* * RTL SUPPORT *********************************************/ -.reveal.rtl .slides, .reveal.rtl .slides h1, .reveal.rtl .slides h2, .reveal.rtl .slides h3, .reveal.rtl .slides h4, .reveal.rtl .slides h5, .reveal.rtl .slides h6 { +.reveal.rtl .slides, +.reveal.rtl .slides h1, +.reveal.rtl .slides h2, +.reveal.rtl .slides h3, +.reveal.rtl .slides h4, +.reveal.rtl .slides h5, +.reveal.rtl .slides h6 { direction: rtl; font-family: sans-serif; } -.reveal.rtl pre, .reveal.rtl code { +.reveal.rtl pre, +.reveal.rtl code { direction: ltr; } -.reveal.rtl ol, .reveal.rtl ul { +.reveal.rtl ol, +.reveal.rtl ul { text-align: right; } .reveal.rtl .progress span { @@ -943,17 +1284,14 @@ body { * PARALLAX BACKGROUND *********************************************/ .reveal.has-parallax-background .backgrounds { - -webkit-transition: all 0.8s ease; - transition: all 0.8s ease; } + transition: all 0.8s ease; } /* Global transition speed settings */ .reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds { - -webkit-transition-duration: 400ms; - transition-duration: 400ms; } + transition-duration: 400ms; } .reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds { - -webkit-transition-duration: 1200ms; - transition-duration: 1200ms; } + transition-duration: 1200ms; } /********************************************* * LINK PREVIEW OVERLAY @@ -968,8 +1306,7 @@ body { background: rgba(0, 0, 0, 0.9); opacity: 0; visibility: hidden; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } + transition: all 0.3s ease; } .reveal .overlay.visible { opacity: 1; @@ -987,8 +1324,7 @@ body { background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D); visibility: visible; opacity: 0.6; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } + transition: all 0.3s ease; } .reveal .overlay header { position: absolute; @@ -1003,11 +1339,11 @@ body { display: inline-block; width: 40px; height: 40px; + line-height: 36px; padding: 0 10px; float: right; opacity: 0.6; - -moz-box-sizing: border-box; - box-sizing: border-box; } + box-sizing: border-box; } .reveal .overlay header a:hover { opacity: 1; } @@ -1028,6 +1364,9 @@ body { .reveal .overlay .viewport { position: absolute; + display: -webkit-box; + display: -ms-flexbox; + display: flex; top: 40px; right: 0; bottom: 0; @@ -1041,18 +1380,32 @@ body { border: 0; opacity: 0; visibility: hidden; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } + transition: all 0.3s ease; } .reveal .overlay.overlay-preview.loaded .viewport iframe { opacity: 1; visibility: visible; } +.reveal .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; + text-align: center; + letter-spacing: normal; } + +.reveal .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; } + +.reveal .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; } + .reveal .overlay.overlay-preview.loaded .spinner { opacity: 0; visibility: hidden; -webkit-transform: scale(0.2); - -ms-transform: scale(0.2); transform: scale(0.2); } .reveal .overlay.overlay-help .viewport { @@ -1061,8 +1414,8 @@ body { .reveal .overlay.overlay-help .viewport .viewport-inner { width: 600px; - margin: 0 auto; - padding: 60px; + margin: auto; + padding: 20px 20px 80px 20px; text-align: center; letter-spacing: normal; } @@ -1072,11 +1425,12 @@ body { .reveal .overlay.overlay-help .viewport .viewport-inner table { border: 1px solid #fff; border-collapse: collapse; - font-size: 14px; } + font-size: 16px; } -.reveal .overlay.overlay-help .viewport .viewport-inner table th, .reveal .overlay.overlay-help .viewport .viewport-inner table td { +.reveal .overlay.overlay-help .viewport .viewport-inner table th, +.reveal .overlay.overlay-help .viewport .viewport-inner table td { width: 200px; - padding: 10px; + padding: 14px; border: 1px solid #fff; vertical-align: middle; } @@ -1088,13 +1442,13 @@ body { * PLAYBACK COMPONENT *********************************************/ .reveal .playback { - position: fixed; + position: absolute; left: 15px; - bottom: 15px; + bottom: 20px; z-index: 30; cursor: pointer; - -webkit-transition: all 400ms ease; - transition: all 400ms ease; } + transition: all 400ms ease; + -webkit-tap-highlight-color: transparent; } .reveal.overview .playback { opacity: 0; @@ -1122,10 +1476,8 @@ body { position: relative; padding: 0 2px; pointer-events: none; - -webkit-transition: all 400ms ease; - transition: all 400ms ease; + transition: all 400ms ease; -webkit-transform-origin: 50% 0%; - -ms-transform-origin: 50% 0%; transform-origin: 50% 0%; -webkit-transform-style: preserve-3d; transform-style: preserve-3d; @@ -1147,7 +1499,6 @@ body { -webkit-backface-visibility: hidden; backface-visibility: hidden; -webkit-transform-origin: 50% 0%; - -ms-transform-origin: 50% 0%; transform-origin: 50% 0%; -webkit-transform: translate3d(0px, 110%, 0px) rotateX(-90deg); transform: translate3d(0px, 110%, 0px) rotateX(-90deg); } @@ -1158,14 +1509,79 @@ body { .reveal aside.notes { display: none; } +.reveal .speaker-notes { + display: none; + position: absolute; + width: 25vw; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; + z-index: 1; + font-size: 18px; + line-height: 1.4; + border: 1px solid rgba(0, 0, 0, 0.05); + color: #222; + background-color: #f5f5f5; + overflow: auto; + box-sizing: border-box; + text-align: left; + font-family: Helvetica, sans-serif; + -webkit-overflow-scrolling: touch; } + .reveal .speaker-notes .notes-placeholder { + color: #ccc; + font-style: italic; } + .reveal .speaker-notes:focus { + outline: none; } + .reveal .speaker-notes:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; } + +.reveal.show-notes { + max-width: 75vw; + overflow: visible; } + +.reveal.show-notes .speaker-notes { + display: block; } + +@media screen and (min-width: 1600px) { + .reveal .speaker-notes { + font-size: 20px; } } + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + overflow: visible; } + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: 42.8571428571%; } } + +@media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; } + .reveal.show-notes .speaker-notes { + top: 100%; + height: 66.6666666667%; } + .reveal .speaker-notes { + font-size: 14px; } } + /********************************************* * ZOOM PLUGIN *********************************************/ -.zoomed .reveal *, .zoomed .reveal *:before, .zoomed .reveal *:after { +.zoomed .reveal *, +.zoomed .reveal *:before, +.zoomed .reveal *:after { -webkit-backface-visibility: visible !important; backface-visibility: visible !important; } -.zoomed .reveal .progress, .zoomed .reveal .controls { +.zoomed .reveal .progress, +.zoomed .reveal .controls { opacity: 0; } .zoomed .reveal .roll span { diff --git a/css/reveal.scss b/css/reveal.scss index 3321c98..e6608d4 100644 --- a/css/reveal.scss +++ b/css/reveal.scss @@ -1,9 +1,9 @@ /*! * reveal.js - * http://lab.hakim.se/reveal-js + * http://revealjs.com * MIT licensed * - * Copyright (C) 2015 Hakim El Hattab, http://hakim.se + * Copyright (C) 2018 Hakim El Hattab, http://hakim.se */ @@ -23,7 +23,7 @@ html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal i .reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, .reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, -.reveal time, .reveal mark, .reveal audio, video { +.reveal time, .reveal mark, .reveal audio, .reveal video { margin: 0; padding: 0; border: 0; @@ -69,13 +69,13 @@ body { &.visible { opacity: 1; - visibility: visible; + visibility: inherit; } } .reveal .slides section .fragment.grow { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { transform: scale( 1.3 ); @@ -84,7 +84,7 @@ body { .reveal .slides section .fragment.shrink { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { transform: scale( 0.7 ); @@ -101,7 +101,7 @@ body { .reveal .slides section .fragment.fade-out { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { opacity: 0; @@ -111,29 +111,78 @@ body { .reveal .slides section .fragment.semi-fade-out { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { opacity: 0.5; - visibility: visible; + visibility: inherit; } } .reveal .slides section .fragment.strike { opacity: 1; + visibility: inherit; &.visible { text-decoration: line-through; } } +.reveal .slides section .fragment.fade-up { + transform: translate(0, 20%); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-down { + transform: translate(0, -20%); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-right { + transform: translate(-20%, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-left { + transform: translate(20%, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-in-then-out, .reveal .slides section .fragment.current-visible { opacity: 0; visibility: hidden; &.current-fragment { opacity: 1; - visibility: visible; + visibility: inherit; + } +} + +.reveal .slides section .fragment.fade-in-then-semi-out { + opacity: 0; + visibility: hidden; + + &.visible { + opacity: 0.5; + visibility: inherit; + } + + &.current-fragment { + opacity: 1; + visibility: inherit; } } @@ -144,7 +193,7 @@ body { .reveal .slides section .fragment.highlight-blue, .reveal .slides section .fragment.highlight-current-blue { opacity: 1; - visibility: visible; + visibility: inherit; } .reveal .slides section .fragment.highlight-red.visible { color: #ff2c2d @@ -202,80 +251,271 @@ body { * CONTROLS *********************************************/ -.reveal .controls { - display: none; - position: fixed; - width: 110px; - height: 110px; - z-index: 30; - right: 10px; - bottom: 10px; +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% {transform: translateX(0);} + 20% {transform: translateX(10px);} + 30% {transform: translateX(-5px);} +} - -webkit-user-select: none; +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% {transform: translateY(0);} + 20% {transform: translateY(10px);} + 30% {transform: translateY(-5px);} } -.reveal .controls div { - position: absolute; - opacity: 0.05; - width: 0; - height: 0; - border: 12px solid transparent; - transform: scale(.9999); - transition: all 0.2s ease; +$controlArrowSize: 3.6em; +$controlArrowSpacing: 1.4em; +$controlArrowLength: 2.6em; +$controlArrowThickness: 0.5em; +$controlsArrowAngle: 45deg; +$controlsArrowAngleHover: 40deg; +$controlsArrowAngleActive: 36deg; - -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); -} +@mixin controlsArrowTransform( $angle ) { + &:before { + transform: translateX(($controlArrowSize - $controlArrowLength)/2) translateY(($controlArrowSize - $controlArrowThickness)/2) rotate( $angle ); + } -.reveal .controls div.enabled { - opacity: 0.7; - cursor: pointer; + &:after { + transform: translateX(($controlArrowSize - $controlArrowLength)/2) translateY(($controlArrowSize - $controlArrowThickness)/2) rotate( -$angle ); + } } -.reveal .controls div.enabled:active { - margin-top: 1px; -} +.reveal .controls { + $spacing: 12px; + + display: none; + position: absolute; + top: auto; + bottom: $spacing; + right: $spacing; + left: auto; + z-index: 1; + color: #000; + pointer-events: none; + font-size: 10px; + + button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + transform: scale(.9999); + transition: color 0.2s ease, + opacity 0.2s ease, + transform 0.2s ease; + z-index: 2; // above slides + pointer-events: auto; + font-size: inherit; + + visibility: hidden; + opacity: 0; + + -webkit-appearance: none; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); + } - .reveal .controls div.navigate-left { - top: 42px; + .controls-arrow:before, + .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: $controlArrowLength; + height: $controlArrowThickness; + border-radius: $controlArrowThickness/2; + background-color: currentColor; - border-right-width: 22px; - border-right-color: #000; + transition: all 0.15s ease, background-color 0.8s ease; + transform-origin: floor(($controlArrowThickness/2)*10)/10 50%; + will-change: transform; } - .reveal .controls div.navigate-left.fragmented { - opacity: 0.3; + + .controls-arrow { + position: relative; + width: $controlArrowSize; + height: $controlArrowSize; + + @include controlsArrowTransform( $controlsArrowAngle ); + + &:hover { + @include controlsArrowTransform( $controlsArrowAngleHover ); + } + + &:active { + @include controlsArrowTransform( $controlsArrowAngleActive ); } + } + + .navigate-left { + right: $controlArrowSize + $controlArrowSpacing*2; + bottom: $controlArrowSpacing + $controlArrowSize/2; + transform: translateX( -10px ); + } - .reveal .controls div.navigate-right { - left: 74px; - top: 42px; + .navigate-right { + right: 0; + bottom: $controlArrowSpacing + $controlArrowSize/2; + transform: translateX( 10px ); + + .controls-arrow { + transform: rotate( 180deg ); + } - border-left-width: 22px; - border-left-color: #000; + &.highlight { + animation: bounce-right 2s 50 both ease-out; + } } - .reveal .controls div.navigate-right.fragmented { - opacity: 0.3; + + .navigate-up { + right: $controlArrowSpacing + $controlArrowSize/2; + bottom: $controlArrowSpacing*2 + $controlArrowSize; + transform: translateY( -10px ); + + .controls-arrow { + transform: rotate( 90deg ); } + } + + .navigate-down { + right: $controlArrowSpacing + $controlArrowSize/2; + bottom: 0; + transform: translateY( 10px ); - .reveal .controls div.navigate-up { - left: 42px; + .controls-arrow { + transform: rotate( -90deg ); + } - border-bottom-width: 22px; - border-bottom-color: #000; + &.highlight { + animation: bounce-down 2s 50 both ease-out; + } } - .reveal .controls div.navigate-up.fragmented { - opacity: 0.3; + + // Back arrow style: "faded": + // Deemphasize backwards navigation arrows in favor of drawing + // attention to forwards navigation + &[data-controls-back-arrows="faded"] .navigate-left.enabled, + &[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; + + &:hover { + opacity: 1; } + } + + // Back arrow style: "hidden": + // Never show arrows for backwards navigation + &[data-controls-back-arrows="hidden"] .navigate-left.enabled, + &[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; + } + + // Any control button that can be clicked is "enabled" + .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + transform: none; + } - .reveal .controls div.navigate-down { - left: 42px; - top: 74px; + // Any control button that leads to showing or hiding + // a fragment + .enabled.fragmented { + opacity: 0.5; + } - border-top-width: 22px; - border-top-color: #000; + .enabled:hover, + .enabled.fragmented:hover { + opacity: 1; } - .reveal .controls div.navigate-down.fragmented { - opacity: 0.3; +} + +// Adjust the layout when there are no vertical slides +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: $controlArrowSpacing; + right: 0.5em + $controlArrowSpacing + $controlArrowSize; +} + +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: $controlArrowSpacing; + right: 0.5em; +} + +// Adjust the layout when there are no horizontal slides +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: $controlArrowSpacing; + bottom: $controlArrowSpacing + $controlArrowSize; +} +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: $controlArrowSpacing; + bottom: 0.5em; +} + +// Invert arrows based on background color +.reveal.has-dark-background .controls { + color: #fff; +} +.reveal.has-light-background .controls { + color: #000; +} + +// Disable active states on touch devices +.reveal.no-hover .controls .controls-arrow:hover, +.reveal.no-hover .controls .controls-arrow:active { + @include controlsArrowTransform( $controlsArrowAngle ); +} + +// Edge aligned controls layout +@media screen and (min-width: 500px) { + + $spacing: 8px; + + .reveal .controls[data-controls-layout="edges"] { + & { + top: 0; + right: 0; + bottom: 0; + left: 0; + } + + .navigate-left, + .navigate-right, + .navigate-up, + .navigate-down { + bottom: auto; + right: auto; + } + + .navigate-left { + top: 50%; + left: $spacing; + margin-top: -$controlArrowSize/2; + } + + .navigate-right { + top: 50%; + right: $spacing; + margin-top: -$controlArrowSize/2; + } + + .navigate-up { + top: $spacing; + left: 50%; + margin-left: -$controlArrowSize/2; + } + + .navigate-down { + bottom: $spacing; + left: 50%; + margin-left: -$controlArrowSize/2; } + } + +} /********************************************* @@ -283,7 +523,7 @@ body { *********************************************/ .reveal .progress { - position: fixed; + position: absolute; display: none; height: 3px; width: 100%; @@ -292,21 +532,22 @@ body { z-index: 10; background-color: rgba( 0, 0, 0, 0.2 ); + color: #fff; } .reveal .progress:after { content: ''; display: block; position: absolute; - height: 20px; + height: 10px; width: 100%; - top: -20px; + top: -10px; } .reveal .progress span { display: block; height: 100%; width: 0px; - background-color: #000; + background-color: currentColor; transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); } @@ -315,13 +556,25 @@ body { *********************************************/ .reveal .slide-number { - position: fixed; + position: absolute; display: block; - right: 15px; - bottom: 15px; - opacity: 0.5; + right: 8px; + bottom: 8px; z-index: 31; + font-family: Helvetica, sans-serif; font-size: 12px; + line-height: 1; + color: #fff; + background-color: rgba( 0, 0, 0, 0.4 ); + padding: 5px; +} + +.reveal .slide-number a { + color: currentColor; +} + +.reveal .slide-number-delimiter { + margin: 0 3px; } /********************************************* @@ -336,6 +589,16 @@ body { touch-action: none; } +// Mobile Safari sometimes overlays a header at the top +// of the page when in landscape mode. Using fixed +// positioning ensures that reveal.js reduces its height +// when this header is visible. +@media only screen and (orientation : landscape) { + .reveal.ua-iphone { + position: fixed; + } +} + .reveal .slides { position: absolute; width: 100%; @@ -345,6 +608,7 @@ body { bottom: 0; left: 0; margin: auto; + pointer-events: none; overflow: visible; z-index: 1; @@ -363,9 +627,10 @@ body { position: absolute; width: 100%; padding: 20px 0px; + pointer-events: auto; z-index: 10; - transform-style: preserve-3d; + transform-style: flat; transition: transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), @@ -391,6 +656,7 @@ body { .reveal .slides>section.stack { padding-top: 0; padding-bottom: 0; + pointer-events: none; } .reveal .slides>section.present, @@ -400,6 +666,13 @@ body { opacity: 1; } +.reveal .slides>section:empty, +.reveal .slides>section>section:empty, +.reveal .slides>section[data-background-interactive], +.reveal .slides>section>section[data-background-interactive] { + pointer-events: none; +} + .reveal.center, .reveal.center .slides, .reveal.center .slides section { @@ -432,8 +705,14 @@ body { *********************************************/ @mixin transition-global($style) { - .reveal .slides>section[data-transition=#{$style}], - .reveal.#{$style} .slides>section:not([data-transition]) { + .reveal .slides section[data-transition=#{$style}], + .reveal.#{$style} .slides section:not([data-transition]) { + @content; + } +} +@mixin transition-stack($style) { + .reveal .slides section[data-transition=#{$style}].stack, + .reveal.#{$style} .slides section.stack { @content; } } @@ -496,6 +775,10 @@ body { *********************************************/ @each $stylename in default, convex { + @include transition-stack(#{$stylename}) { + transform-style: preserve-3d; + } + @include transition-horizontal-past(#{$stylename}) { transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } @@ -514,6 +797,10 @@ body { * CONCAVE TRANSITION *********************************************/ +@include transition-stack(concave) { + transform-style: preserve-3d; +} + @include transition-horizontal-past(concave) { transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); } @@ -553,6 +840,10 @@ body { /********************************************* * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. *********************************************/ .reveal.cube .slides { @@ -564,6 +855,7 @@ body { min-height: 700px; backface-visibility: hidden; box-sizing: border-box; + transform-style: preserve-3d; } .reveal.center.cube .slides section { min-height: 0; @@ -624,6 +916,10 @@ body { /********************************************* * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. *********************************************/ .reveal.page .slides { @@ -635,6 +931,7 @@ body { padding: 30px; min-height: 700px; box-sizing: border-box; + transform-style: preserve-3d; } .reveal.page .slides section.past { z-index: 12; @@ -737,6 +1034,25 @@ body { z-index: 100; transition: all 1s ease; } + +.reveal .pause-overlay .resume-button { + position: absolute; + bottom: 20px; + right: 20px; + color: #ccc; + border-radius: 2px; + padding: 6px 14px; + border: 2px solid #ccc; + font-size: 16px; + background: transparent; + cursor: pointer; + + &:hover { + color: #fff; + border-color: #fff; + } +} + .reveal.paused .pause-overlay { visibility: visible; opacity: 1; @@ -807,13 +1123,21 @@ body { height: 100%; opacity: 0; visibility: hidden; + overflow: hidden; background-color: rgba( 0, 0, 0, 0 ); + + transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + + .reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; + background-position: 50% 50%; background-repeat: no-repeat; background-size: cover; - - transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); } .reveal .slide-background.stack { @@ -823,6 +1147,7 @@ body { .reveal .slide-background.present { opacity: 1; visibility: visible; + z-index: 2; } .print-pdf .reveal .slide-background { @@ -839,7 +1164,11 @@ body { max-height: none; top: 0; left: 0; + object-fit: cover; } + .reveal .slide-background[data-background-size="contain"] video { + object-fit: contain; + } /* Immediate transition style */ .reveal[data-background-transition=none]>.backgrounds .slide-background, @@ -969,8 +1298,15 @@ body { perspective-origin: 50% 50%; perspective: 700px; + .slides { + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; + } + .slides section { - height: 700px; + height: 100%; + top: 0 !important; opacity: 1 !important; overflow: hidden; visibility: visible !important; @@ -1000,6 +1336,10 @@ body { .backgrounds { perspective: inherit; + + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; } .backgrounds .slide-background { @@ -1010,6 +1350,10 @@ body { outline: 10px solid rgba(150,150,150,0.1); outline-offset: 10px; } + + .backgrounds .slide-background.stack { + overflow: visible; + } } // Disable transitions transitions while we're activating @@ -1024,10 +1368,6 @@ body { transition: none; } -.reveal.overview-animated .slides { - transition: transform 0.4s ease; -} - /********************************************* * RTL SUPPORT @@ -1125,6 +1465,7 @@ body { display: inline-block; width: 40px; height: 40px; + line-height: 36px; padding: 0 10px; float: right; opacity: 0.6; @@ -1152,6 +1493,7 @@ body { .reveal .overlay .viewport { position: absolute; + display: flex; top: 40px; right: 0; bottom: 0; @@ -1175,6 +1517,23 @@ body { visibility: visible; } + .reveal .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; + text-align: center; + letter-spacing: normal; + } + .reveal .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; + } + .reveal .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; + } + .reveal .overlay.overlay-preview.loaded .spinner { opacity: 0; visibility: hidden; @@ -1188,8 +1547,8 @@ body { .reveal .overlay.overlay-help .viewport .viewport-inner { width: 600px; - margin: 0 auto; - padding: 60px; + margin: auto; + padding: 20px 20px 80px 20px; text-align: center; letter-spacing: normal; } @@ -1201,13 +1560,13 @@ body { .reveal .overlay.overlay-help .viewport .viewport-inner table { border: 1px solid #fff; border-collapse: collapse; - font-size: 14px; + font-size: 16px; } .reveal .overlay.overlay-help .viewport .viewport-inner table th, .reveal .overlay.overlay-help .viewport .viewport-inner table td { width: 200px; - padding: 10px; + padding: 14px; border: 1px solid #fff; vertical-align: middle; } @@ -1224,12 +1583,13 @@ body { *********************************************/ .reveal .playback { - position: fixed; + position: absolute; left: 15px; - bottom: 15px; + bottom: 20px; z-index: 30; cursor: pointer; transition: all 400ms ease; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); } .reveal.overview .playback { @@ -1288,10 +1648,97 @@ body { * SPEAKER NOTES *********************************************/ +// Hide on-page notes .reveal aside.notes { display: none; } +// An interface element that can optionally be used to show the +// speaker notes to all viewers, on top of the presentation +.reveal .speaker-notes { + display: none; + position: absolute; + width: 25vw; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; + z-index: 1; + font-size: 18px; + line-height: 1.4; + border: 1px solid rgba( 0, 0, 0, 0.05 ); + color: #222; + background-color: #f5f5f5; + overflow: auto; + box-sizing: border-box; + text-align: left; + font-family: Helvetica, sans-serif; + -webkit-overflow-scrolling: touch; + + .notes-placeholder { + color: #ccc; + font-style: italic; + } + + &:focus { + outline: none; + } + + &:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; + } +} + + +.reveal.show-notes { + max-width: 75vw; + overflow: visible; +} + +.reveal.show-notes .speaker-notes { + display: block; +} + +@media screen and (min-width: 1600px) { + .reveal .speaker-notes { + font-size: 20px; + } +} + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + overflow: visible; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: (30/0.7)*1%; + } +} + +@media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + height: (40/0.6)*1%; + } + + .reveal .speaker-notes { + font-size: 14px; + } +} + /********************************************* * ZOOM PLUGIN @@ -1315,5 +1762,3 @@ body { .zoomed .reveal .roll span:after { visibility: hidden; } - - diff --git a/css/theme/README.md b/css/theme/README.md index 753e0c0..8ae164b 100644 --- a/css/theme/README.md +++ b/css/theme/README.md @@ -1,10 +1,10 @@ ## Dependencies -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 +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 proceeding: https://github.com/hakimel/reveal.js#full-setup ## Creating a Theme -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). +To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled by Grunt from Sass to CSS (see the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/Gruntfile.js)) when you run `npm run build -- css-themes`. Each theme file does four things in the following order: @@ -19,5 +19,3 @@ This is where you override the default theme. Either by specifying variables (se 4. **Include [/css/theme/template/theme.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/theme.scss)** The template theme file which will generate final CSS output based on the currently defined variables. - -When you are done, run `grunt css-themes` to compile the Sass file to CSS and you are ready to use your new theme. diff --git a/css/theme/beige.css b/css/theme/beige.css index 944dbd8..fb5f137 100644 --- a/css/theme/beige.css +++ b/css/theme/beige.css @@ -1,26 +1,26 @@ -@import url(../../lib/font/league-gothic/league-gothic.css); -@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /** * Beige theme for reveal.js. * * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /********************************************* * GLOBAL STYLES *********************************************/ body { background: #f7f2d3; - background: -moz-radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%); - background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #ffffff), color-stop(100%, #f7f2d3)); - background: -webkit-radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%); - background: -o-radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%); - background: -ms-radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%); - background: radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%); + background: -moz-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, white), color-stop(100%, #f7f2d3)); + background: -webkit-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -o-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -ms-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); background-color: #f7f3de; } .reveal { - font-family: 'Lato', sans-serif; - font-size: 36px; + font-family: "Lato", sans-serif; + font-size: 40px; font-weight: normal; color: #333; } @@ -29,17 +29,28 @@ body { background: rgba(79, 64, 28, 0.99); text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: rgba(79, 64, 28, 0.99); + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #333; - font-family: 'League Gothic', Impact, sans-serif; + font-family: "League Gothic", Impact, sans-serif; font-weight: normal; line-height: 1.2; letter-spacing: normal; @@ -70,17 +81,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -97,7 +113,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -107,9 +126,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -120,7 +136,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -139,16 +156,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -158,25 +174,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -193,18 +215,18 @@ body { .reveal a { color: #8b743d; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #c0a76e; + color: #c0a86e; text-shadow: none; border: none; } .reveal .roll span:after { color: #fff; - background: #564726; } + background: #564826; } /********************************************* * IMAGES @@ -215,10 +237,14 @@ body { border: 4px solid #333; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -228,44 +254,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #8b743d; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #8b743d; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #8b743d; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #8b743d; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #c0a76e; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #c0a76e; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #c0a76e; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #c0a76e; } +.reveal .controls { + color: #8b743d; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #8b743d; } .reveal .progress span { - background: #8b743d; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #8b743d; } +@media print { + .backgrounds { + background-color: #f7f3de; } } diff --git a/css/theme/black.css b/css/theme/black.css index ee2ead8..dec6385 100644 --- a/css/theme/black.css +++ b/css/theme/black.css @@ -1,9 +1,9 @@ -@import url(../../lib/font/source-sans-pro/source-sans-pro.css); /** * Black theme for reveal.js. This is the opposite of the 'white' theme. * - * Copyright (C) 2015 Hakim El Hattab, http://hakim.se + * By Hakim El Hattab, http://hakim.se */ +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 { color: #222; } @@ -15,8 +15,8 @@ body { background-color: #222; } .reveal { - font-family: 'Source Sans Pro', Helvetica, sans-serif; - font-size: 38px; + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 42px; font-weight: normal; color: #fff; } @@ -25,17 +25,28 @@ body { background: #bee4fd; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #fff; - font-family: 'Source Sans Pro', Helvetica, sans-serif; + font-family: "Source Sans Pro", Helvetica, sans-serif; font-weight: 600; line-height: 1.2; letter-spacing: normal; @@ -66,17 +77,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -93,7 +109,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -103,9 +122,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -116,7 +132,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -135,16 +152,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -154,25 +170,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -189,9 +211,9 @@ body { .reveal a { color: #42affa; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #8dcffc; @@ -200,7 +222,7 @@ body { .reveal .roll span:after { color: #fff; - background: #068ee9; } + background: #068de9; } /********************************************* * IMAGES @@ -211,10 +233,14 @@ body { border: 4px solid #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -224,44 +250,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #42affa; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #42affa; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #42affa; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #42affa; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #8dcffc; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #8dcffc; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #8dcffc; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #8dcffc; } +.reveal .controls { + color: #42affa; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #42affa; } .reveal .progress span { - background: #42affa; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #42affa; } +@media print { + .backgrounds { + background-color: #222; } } diff --git a/css/theme/blood.css b/css/theme/blood.css index 952fdf2..15e6c20 100644 --- a/css/theme/blood.css +++ b/css/theme/blood.css @@ -1,4 +1,3 @@ -@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic); /** * Blood theme for reveal.js * Author: Walther http://github.com/Walther @@ -10,6 +9,7 @@ * For other themes, change $codeBackground accordingly. * */ +@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic); /********************************************* * GLOBAL STYLES *********************************************/ @@ -18,8 +18,8 @@ body { background-color: #222; } .reveal { - font-family: Ubuntu, 'sans-serif'; - font-size: 36px; + font-family: Ubuntu, "sans-serif"; + font-size: 40px; font-weight: normal; color: #eee; } @@ -28,17 +28,28 @@ body { background: #a23; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #a23; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #eee; - font-family: Ubuntu, 'sans-serif'; + font-family: Ubuntu, "sans-serif"; font-weight: normal; line-height: 1.2; letter-spacing: normal; @@ -69,17 +80,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -96,7 +112,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -106,9 +125,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -119,7 +135,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -138,16 +155,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -157,25 +173,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -192,18 +214,18 @@ body { .reveal a { color: #a23; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #dd5567; + color: #dd5566; text-shadow: none; border: none; } .reveal .roll span:after { color: #fff; - background: #6a1521; } + background: #6a1520; } /********************************************* * IMAGES @@ -214,10 +236,14 @@ body { border: 4px solid #eee; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -227,53 +253,38 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #a23; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #a23; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #a23; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #a23; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #dd5567; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #dd5567; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #dd5567; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #dd5567; } +.reveal .controls { + color: #a23; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #a23; } .reveal .progress span { - background: #a23; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #a23; } +@media print { + .backgrounds { + background-color: #222; } } .reveal p { font-weight: 300; text-shadow: 1px 1px #222; } -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { font-weight: 700; } .reveal p code { diff --git a/css/theme/league.css b/css/theme/league.css index 41967d2..9dfa2ce 100644 --- a/css/theme/league.css +++ b/css/theme/league.css @@ -1,5 +1,3 @@ -@import url(../../lib/font/league-gothic/league-gothic.css); -@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /** * League theme for reveal.js. * @@ -7,6 +5,8 @@ * * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /********************************************* * GLOBAL STYLES *********************************************/ @@ -21,8 +21,8 @@ body { background-color: #2b2b2b; } .reveal { - font-family: 'Lato', sans-serif; - font-size: 36px; + font-family: "Lato", sans-serif; + font-size: 40px; font-weight: normal; color: #eee; } @@ -31,17 +31,28 @@ body { background: #FF5E99; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #FF5E99; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #eee; - font-family: 'League Gothic', Impact, sans-serif; + font-family: "League Gothic", Impact, sans-serif; font-weight: normal; line-height: 1.2; letter-spacing: normal; @@ -72,17 +83,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -99,7 +115,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -109,9 +128,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -122,7 +138,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -141,16 +158,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -160,25 +176,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -195,18 +217,18 @@ body { .reveal a { color: #13DAEC; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #71ebf4; + color: #71e9f4; text-shadow: none; border: none; } .reveal .roll span:after { color: #fff; - background: #0d9ba5; } + background: #0d99a5; } /********************************************* * IMAGES @@ -217,10 +239,14 @@ body { border: 4px solid #eee; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -230,44 +256,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #13DAEC; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #13DAEC; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #13DAEC; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #13DAEC; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #71ebf4; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #71ebf4; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #71ebf4; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #71ebf4; } +.reveal .controls { + color: #13DAEC; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #13DAEC; } .reveal .progress span { - background: #13DAEC; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #13DAEC; } +@media print { + .backgrounds { + background-color: #2b2b2b; } } diff --git a/css/theme/moon.css b/css/theme/moon.css index ac93638..52b3f67 100644 --- a/css/theme/moon.css +++ b/css/theme/moon.css @@ -1,9 +1,9 @@ -@import url(../../lib/font/league-gothic/league-gothic.css); -@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /** * Solarized Dark theme for reveal.js. * Author: Achim Staebler */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /** * Solarized colors by Ethan Schoonover */ @@ -19,8 +19,8 @@ body { background-color: #002b36; } .reveal { - font-family: 'Lato', sans-serif; - font-size: 36px; + font-family: "Lato", sans-serif; + font-size: 40px; font-weight: normal; color: #93a1a1; } @@ -29,17 +29,28 @@ body { background: #d33682; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #eee8d5; - font-family: 'League Gothic', Impact, sans-serif; + font-family: "League Gothic", Impact, sans-serif; font-weight: normal; line-height: 1.2; letter-spacing: normal; @@ -70,17 +81,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -97,7 +113,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -107,9 +126,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -120,7 +136,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -139,16 +156,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -158,25 +174,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -193,18 +215,18 @@ body { .reveal a { color: #268bd2; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #78bae6; + color: #78b9e6; text-shadow: none; border: none; } .reveal .roll span:after { color: #fff; - background: #1a6291; } + background: #1a6091; } /********************************************* * IMAGES @@ -215,10 +237,14 @@ body { border: 4px solid #93a1a1; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -228,44 +254,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #268bd2; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #268bd2; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #268bd2; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #268bd2; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #78bae6; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #78bae6; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #78bae6; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #78bae6; } +.reveal .controls { + color: #268bd2; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #268bd2; } .reveal .progress span { - background: #268bd2; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #268bd2; } +@media print { + .backgrounds { + background-color: #002b36; } } diff --git a/css/theme/night.css b/css/theme/night.css index 6a5ed31..51a3dd3 100644 --- a/css/theme/night.css +++ b/css/theme/night.css @@ -1,10 +1,10 @@ -@import url(https://fonts.googleapis.com/css?family=Montserrat:700); -@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); /** * Black theme for reveal.js. * * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ +@import url(https://fonts.googleapis.com/css?family=Montserrat:700); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); /********************************************* * GLOBAL STYLES *********************************************/ @@ -13,8 +13,8 @@ body { background-color: #111; } .reveal { - font-family: 'Open Sans', sans-serif; - font-size: 30px; + font-family: "Open Sans", sans-serif; + font-size: 40px; font-weight: normal; color: #eee; } @@ -23,17 +23,28 @@ body { background: #e7ad52; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #e7ad52; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #eee; - font-family: 'Montserrat', Impact, sans-serif; + font-family: "Montserrat", Impact, sans-serif; font-weight: normal; line-height: 1.2; letter-spacing: -0.03em; @@ -64,17 +75,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -91,7 +107,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -101,9 +120,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -114,7 +130,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -133,16 +150,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -152,25 +168,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -187,9 +209,9 @@ body { .reveal a { color: #e7ad52; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #f3d7ac; @@ -198,7 +220,7 @@ body { .reveal .roll span:after { color: #fff; - background: #d0881d; } + background: #d08a1d; } /********************************************* * IMAGES @@ -209,10 +231,14 @@ body { border: 4px solid #eee; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -222,44 +248,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #e7ad52; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #e7ad52; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #e7ad52; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #e7ad52; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #f3d7ac; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #f3d7ac; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #f3d7ac; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #f3d7ac; } +.reveal .controls { + color: #e7ad52; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #e7ad52; } .reveal .progress span { - background: #e7ad52; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #e7ad52; } +@media print { + .backgrounds { + background-color: #111; } } diff --git a/css/theme/serif.css b/css/theme/serif.css index fc83e5d..ea01066 100644 --- a/css/theme/serif.css +++ b/css/theme/serif.css @@ -15,8 +15,8 @@ body { background-color: #F0F1EB; } .reveal { - font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; - font-size: 36px; + font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; + font-size: 40px; font-weight: normal; color: #000; } @@ -25,17 +25,28 @@ body { background: #26351C; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #26351C; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #383D3D; - font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; font-weight: normal; line-height: 1.2; letter-spacing: normal; @@ -66,17 +77,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -93,7 +109,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -103,9 +122,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -116,7 +132,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -135,16 +152,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -154,25 +170,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -189,12 +211,12 @@ body { .reveal a { color: #51483D; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #8b7b69; + color: #8b7c69; text-shadow: none; border: none; } @@ -211,10 +233,14 @@ body { border: 4px solid #000; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -224,44 +250,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #51483D; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #51483D; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #51483D; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #51483D; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #8b7b69; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #8b7b69; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #8b7b69; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #8b7b69; } +.reveal .controls { + color: #51483D; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #51483D; } .reveal .progress span { - background: #51483D; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #51483D; } +@media print { + .backgrounds { + background-color: #F0F1EB; } } diff --git a/css/theme/simple.css b/css/theme/simple.css index ea08a27..8432d84 100644 --- a/css/theme/simple.css +++ b/css/theme/simple.css @@ -1,5 +1,3 @@ -@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); -@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /** * A simple theme for reveal.js presentations, similar * to the default theme. The accent color is darkblue. @@ -7,6 +5,11 @@ * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ +@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { + color: #fff; } + /********************************************* * GLOBAL STYLES *********************************************/ @@ -15,8 +18,8 @@ body { background-color: #fff; } .reveal { - font-family: 'Lato', sans-serif; - font-size: 36px; + font-family: "Lato", sans-serif; + font-size: 40px; font-weight: normal; color: #000; } @@ -25,17 +28,28 @@ body { background: rgba(0, 0, 0, 0.99); text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: rgba(0, 0, 0, 0.99); + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #000; - font-family: 'News Cycle', Impact, sans-serif; + font-family: "News Cycle", Impact, sans-serif; font-weight: normal; line-height: 1.2; letter-spacing: normal; @@ -66,17 +80,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -93,7 +112,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -103,9 +125,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -116,7 +135,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -135,16 +155,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -154,25 +173,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -189,9 +214,9 @@ body { .reveal a { color: #00008B; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #0000f1; @@ -211,10 +236,14 @@ body { border: 4px solid #000; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -224,44 +253,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #00008B; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #00008B; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #00008B; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #00008B; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #0000f1; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #0000f1; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #0000f1; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #0000f1; } +.reveal .controls { + color: #00008B; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #00008B; } .reveal .progress span { - background: #00008B; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #00008B; } +@media print { + .backgrounds { + background-color: #fff; } } diff --git a/css/theme/sky.css b/css/theme/sky.css index 83842c4..6f60a1d 100644 --- a/css/theme/sky.css +++ b/css/theme/sky.css @@ -1,10 +1,10 @@ -@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); -@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); /** * Sky theme for reveal.js. * * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ +@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); .reveal a { line-height: 1.3em; } @@ -22,8 +22,8 @@ body { background-color: #f7fbfc; } .reveal { - font-family: 'Open Sans', sans-serif; - font-size: 36px; + font-family: "Open Sans", sans-serif; + font-size: 40px; font-weight: normal; color: #333; } @@ -32,17 +32,28 @@ body { background: #134674; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #134674; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #333; - font-family: 'Quicksand', sans-serif; + font-family: "Quicksand", sans-serif; font-weight: normal; line-height: 1.2; letter-spacing: -0.08em; @@ -73,17 +84,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -100,7 +116,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -110,9 +129,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -123,7 +139,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -142,16 +159,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -161,25 +177,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -196,18 +218,18 @@ body { .reveal a { color: #3b759e; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #74a8cb; + color: #74a7cb; text-shadow: none; border: none; } .reveal .roll span:after { color: #fff; - background: #264d66; } + background: #264c66; } /********************************************* * IMAGES @@ -218,10 +240,14 @@ body { border: 4px solid #333; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -231,44 +257,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #3b759e; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #3b759e; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #3b759e; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #3b759e; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #74a8cb; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #74a8cb; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #74a8cb; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #74a8cb; } +.reveal .controls { + color: #3b759e; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #3b759e; } .reveal .progress span { - background: #3b759e; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #3b759e; } +@media print { + .backgrounds { + background-color: #f7fbfc; } } diff --git a/css/theme/solarized.css b/css/theme/solarized.css index 649f7a9..fe81f09 100644 --- a/css/theme/solarized.css +++ b/css/theme/solarized.css @@ -1,9 +1,9 @@ -@import url(../../lib/font/league-gothic/league-gothic.css); -@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /** * Solarized Light theme for reveal.js. * Author: Achim Staebler */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); /** * Solarized colors by Ethan Schoonover */ @@ -19,8 +19,8 @@ body { background-color: #fdf6e3; } .reveal { - font-family: 'Lato', sans-serif; - font-size: 36px; + font-family: "Lato", sans-serif; + font-size: 40px; font-weight: normal; color: #657b83; } @@ -29,17 +29,28 @@ body { background: #d33682; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #586e75; - font-family: 'League Gothic', Impact, sans-serif; + font-family: "League Gothic", Impact, sans-serif; font-weight: normal; line-height: 1.2; letter-spacing: normal; @@ -70,17 +81,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -97,7 +113,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -107,9 +126,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -120,7 +136,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -139,16 +156,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -158,25 +174,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -193,18 +215,18 @@ body { .reveal a { color: #268bd2; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #78bae6; + color: #78b9e6; text-shadow: none; border: none; } .reveal .roll span:after { color: #fff; - background: #1a6291; } + background: #1a6091; } /********************************************* * IMAGES @@ -215,10 +237,14 @@ body { border: 4px solid #657b83; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -228,44 +254,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #268bd2; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #268bd2; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #268bd2; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #268bd2; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #78bae6; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #78bae6; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #78bae6; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #78bae6; } +.reveal .controls { + color: #268bd2; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #268bd2; } .reveal .progress span { - background: #268bd2; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #268bd2; } +@media print { + .backgrounds { + background-color: #fdf6e3; } } diff --git a/css/theme/source/black.scss b/css/theme/source/black.scss index 73dfecb..84e8d9a 100644 --- a/css/theme/source/black.scss +++ b/css/theme/source/black.scss @@ -1,7 +1,7 @@ /** * Black theme for reveal.js. This is the opposite of the 'white' theme. * - * Copyright (C) 2015 Hakim El Hattab, http://hakim.se + * By Hakim El Hattab, http://hakim.se */ @@ -21,7 +21,7 @@ $backgroundColor: #222; $mainColor: #fff; $headingColor: #fff; -$mainFontSize: 38px; +$mainFontSize: 42px; $mainFont: 'Source Sans Pro', Helvetica, sans-serif; $headingFont: 'Source Sans Pro', Helvetica, sans-serif; $headingTextShadow: none; diff --git a/css/theme/source/blood.scss b/css/theme/source/blood.scss index d22b53d..4533fc0 100644 --- a/css/theme/source/blood.scss +++ b/css/theme/source/blood.scss @@ -28,7 +28,6 @@ $backgroundColor: $coal; // Main text $mainFont: Ubuntu, 'sans-serif'; -$mainFontSize: 36px; $mainColor: #eee; // Headings diff --git a/css/theme/source/night.scss b/css/theme/source/night.scss index b0cb57f..d49a282 100644 --- a/css/theme/source/night.scss +++ b/css/theme/source/night.scss @@ -27,7 +27,6 @@ $headingTextShadow: none; $headingLetterSpacing: -0.03em; $headingTextTransform: none; $selectionBackgroundColor: #e7ad52; -$mainFontSize: 30px; // Theme template ------------------------------ diff --git a/css/theme/source/simple.scss b/css/theme/source/simple.scss index 84c7d9b..394c9cd 100644 --- a/css/theme/source/simple.scss +++ b/css/theme/source/simple.scss @@ -31,6 +31,11 @@ $linkColor: #00008B; $linkColorHover: lighten( $linkColor, 20% ); $selectionBackgroundColor: rgba(0, 0, 0, 0.99); +section.has-dark-background { + &, h1, h2, h3, h4, h5, h6 { + color: #fff; + } +} // Theme template ------------------------------ diff --git a/css/theme/source/white.scss b/css/theme/source/white.scss index 4c5b647..7f06ffd 100644 --- a/css/theme/source/white.scss +++ b/css/theme/source/white.scss @@ -1,7 +1,7 @@ /** * White theme for reveal.js. This is the opposite of the 'black' theme. * - * Copyright (C) 2015 Hakim El Hattab, http://hakim.se + * By Hakim El Hattab, http://hakim.se */ @@ -21,7 +21,7 @@ $backgroundColor: #fff; $mainColor: #222; $headingColor: #222; -$mainFontSize: 38px; +$mainFontSize: 42px; $mainFont: 'Source Sans Pro', Helvetica, sans-serif; $headingFont: 'Source Sans Pro', Helvetica, sans-serif; $headingTextShadow: none; diff --git a/css/theme/template/settings.scss b/css/theme/template/settings.scss index ffaac23..63c02cf 100644 --- a/css/theme/template/settings.scss +++ b/css/theme/template/settings.scss @@ -6,7 +6,7 @@ $backgroundColor: #2b2b2b; // Primary/body text $mainFont: 'Lato', sans-serif; -$mainFontSize: 36px; +$mainFontSize: 40px; $mainColor: #eee; // Vertical spacing between blocks of text diff --git a/css/theme/template/theme.scss b/css/theme/template/theme.scss index bd89d31..a8f142d 100644 --- a/css/theme/template/theme.scss +++ b/css/theme/template/theme.scss @@ -22,8 +22,14 @@ body { text-shadow: none; } -.reveal .slides>section, -.reveal .slides>section>section { +::-moz-selection { + color: $selectionColor; + background: $selectionBackgroundColor; + text-shadow: none; +} + +.reveal .slides section, +.reveal .slides section>section { line-height: 1.3; font-weight: inherit; } @@ -128,11 +134,6 @@ body { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; -} - .reveal blockquote { display: block; position: relative; @@ -168,8 +169,10 @@ body { box-shadow: 0px 0px 6px rgba(0,0,0,0.3); } + .reveal code { font-family: monospace; + text-transform: none; } .reveal pre code { @@ -178,8 +181,6 @@ body { overflow: auto; max-height: 400px; word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } .reveal table { @@ -209,15 +210,18 @@ body { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { vertical-align: super; + font-size: smaller; } .reveal sub { vertical-align: sub; + font-size: smaller; } .reveal small { @@ -269,6 +273,11 @@ body { box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + .reveal section img.plain { + border: 0; + box-shadow: none; + } + .reveal a img { -webkit-transition: all .15s linear; -moz-transition: all .15s linear; @@ -287,40 +296,8 @@ body { * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, -.reveal .controls div.navigate-left.enabled { - border-right-color: $linkColor; -} - -.reveal .controls div.navigate-right, -.reveal .controls div.navigate-right.enabled { - border-left-color: $linkColor; -} - -.reveal .controls div.navigate-up, -.reveal .controls div.navigate-up.enabled { - border-bottom-color: $linkColor; -} - -.reveal .controls div.navigate-down, -.reveal .controls div.navigate-down.enabled { - border-top-color: $linkColor; -} - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: $linkColorHover; -} - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: $linkColorHover; -} - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: $linkColorHover; -} - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: $linkColorHover; +.reveal .controls { + color: $linkColor; } @@ -330,20 +307,19 @@ body { .reveal .progress { background: rgba(0,0,0,0.2); + color: $linkColor; } .reveal .progress span { - background: $linkColor; - -webkit-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); -moz-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); - transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: $linkColor; + @media print { + .backgrounds { + background-color: $backgroundColor; + } } - - diff --git a/css/theme/white.css b/css/theme/white.css index c77d5ab..27e44a1 100644 --- a/css/theme/white.css +++ b/css/theme/white.css @@ -1,9 +1,9 @@ -@import url(../../lib/font/source-sans-pro/source-sans-pro.css); /** * White theme for reveal.js. This is the opposite of the 'black' theme. * - * Copyright (C) 2015 Hakim El Hattab, http://hakim.se + * By Hakim El Hattab, http://hakim.se */ +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { color: #fff; } @@ -15,8 +15,8 @@ body { background-color: #fff; } .reveal { - font-family: 'Source Sans Pro', Helvetica, sans-serif; - font-size: 38px; + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 42px; font-weight: normal; color: #222; } @@ -25,17 +25,28 @@ body { background: #98bdef; text-shadow: none; } -.reveal .slides > section, .reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #98bdef; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } /********************************************* * HEADERS *********************************************/ -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { margin: 0 0 20px 0; color: #222; - font-family: 'Source Sans Pro', Helvetica, sans-serif; + font-family: "Source Sans Pro", Helvetica, sans-serif; font-weight: 600; line-height: 1.2; letter-spacing: normal; @@ -66,17 +77,22 @@ body { line-height: 1.3; } /* Ensure certain elements are never larger than the slide itself */ -.reveal img, .reveal video, .reveal iframe { +.reveal img, +.reveal video, +.reveal iframe { max-width: 95%; max-height: 95%; } -.reveal strong, .reveal b { +.reveal strong, +.reveal b { font-weight: bold; } .reveal em { font-style: italic; } -.reveal ol, .reveal dl, .reveal ul { +.reveal ol, +.reveal dl, +.reveal ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } @@ -93,7 +109,10 @@ body { .reveal ul ul ul { list-style-type: circle; } -.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul { +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { display: block; margin-left: 40px; } @@ -103,9 +122,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, .reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -116,7 +132,8 @@ body { background: rgba(255, 255, 255, 0.05); box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } -.reveal blockquote p:first-child, .reveal blockquote p:last-child { +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { display: inline-block; } .reveal q { @@ -135,16 +152,15 @@ body { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; - word-wrap: normal; - background: #3F3F3F; - color: #DCDCDC; } + word-wrap: normal; } .reveal table { margin: auto; @@ -154,25 +170,31 @@ body { .reveal table th { font-weight: bold; } -.reveal table th, .reveal table td { +.reveal table th, +.reveal table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -.reveal table th[align="center"], .reveal table td[align="center"] { +.reveal table th[align="center"], +.reveal table td[align="center"] { text-align: center; } -.reveal table th[align="right"], .reveal table td[align="right"] { +.reveal table th[align="right"], +.reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -189,18 +211,18 @@ body { .reveal a { color: #2a76dd; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #6ca2e8; + color: #6ca0e8; text-shadow: none; border: none; } .reveal .roll span:after { color: #fff; - background: #1a54a1; } + background: #1a53a1; } /********************************************* * IMAGES @@ -211,10 +233,14 @@ body { border: 4px solid #222; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } +.reveal section img.plain { + border: 0; + box-shadow: none; } + .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -224,44 +250,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled { - border-right-color: #2a76dd; } - -.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled { - border-left-color: #2a76dd; } - -.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled { - border-bottom-color: #2a76dd; } - -.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled { - border-top-color: #2a76dd; } - -.reveal .controls div.navigate-left.enabled:hover { - border-right-color: #6ca2e8; } - -.reveal .controls div.navigate-right.enabled:hover { - border-left-color: #6ca2e8; } - -.reveal .controls div.navigate-up.enabled:hover { - border-bottom-color: #6ca2e8; } - -.reveal .controls div.navigate-down.enabled:hover { - border-top-color: #6ca2e8; } +.reveal .controls { + color: #2a76dd; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #2a76dd; } .reveal .progress span { - background: #2a76dd; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* - * SLIDE NUMBER + * PRINT BACKGROUND *********************************************/ -.reveal .slide-number { - color: #2a76dd; } +@media print { + .backgrounds { + background-color: #fff; } } diff --git a/images/structure-application-2.PNG b/images/structure-application-2.PNG new file mode 100644 index 0000000..c4f9b9a Binary files /dev/null and b/images/structure-application-2.PNG differ diff --git a/images/structure-application.PNG b/images/structure-application.PNG new file mode 100644 index 0000000..5675f10 Binary files /dev/null and b/images/structure-application.PNG differ diff --git a/index.html b/index.html index 303caa4..fe84bc6 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ JS, ES6, lodash, ... - + @@ -36,6 +36,17 @@ + @@ -43,128 +54,152 @@
-

Javascript

http://get-focus.github.io/formation-js/#/
-
- -

JS !== jQuery

-
-
-

Les bases

-
-## Les variables - -- `const` Variable constante qui ne peut plus être modifiée -- `var` portée globale ou locale a une fonction -- `let` portée du block courant `{}` - -> A retenir on utilise `const` au maximum et `let` dans les autres cas - +

Javascript

http://get-focus.github.io/formation-js/#/
+
+
+

Présentation

-

Les strings

-
-						
-'déjà' < 'demain'              // => false
-'déjà'.localeCompare('demain') // => -1 ('déjà' avant 'demain')
-
-'déjà !'.toLocaleUpperCase()      // => 'DÉJÀ !'
-'ÇA POUTRE'.toLocaleLowerCase()   // => 'ça poutre'
-
-'one,two,three'.split(',')        // => ['one', 'two', 'three']
-'one,,two,three'.split(/\W+/)     // => ['one', 'two']
-
-'hello'.substring(1)      // => 'ello'
-'hello'.slice(1, -2)      // => 'el' -> [1;length-2[
-						
-					
+

Kesako?

+
    +
  • Langage créé en 1995 pour réaliser des petits scripts dans des pages Web dans le but de les dynamiser
  • +
  • Le langage respecte l'ensemble des paradigmes orienté objet, et est basé sur un principe de prototype
  • +
  • Langage nativement supporté par tous les navigateurs Web, ce qui le rend extrêmement populaire
  • +
  • Chaque navigateur a cependant son propre moteur JS
  • +
-

Les dates

-
-						
-new Date                   //  maintenant !
-new Date(y,m,d[,h,m,s,ms]) // Valeur découpée. (un peu lourd...)
-
-date.getFullYear()  // JAMAIS
-date.getMonth()     // NAZE Java-like qui démarre à… zéro (janvier). #ugly
-date.getDay()       // PERDUUUU ! C'est le DOW (Day Of Week), 0 = dim., 6 = s
-date.getDate()      // Le jour du mois.  Super logique.
-date.getHours()
-date.getMinutes()
-date.getSeconds()
-date.getMilliseconds() // "Milliseconds", un seul mot : une seule initiale.
-
-// Heure locale du navigateur/système.  On a les mêmes en getUTCXxx()…
-						
-					
+

De l'utilisation du JS

+
    +
  • Utilisation au sens premier du langage: dynamiser une page HTML avec quelques scripts côté client
  • +
  • Utilisation de Jquery: simplification de fonction complexe (écoute d'évènement, manipulation du DOM ...)
  • +
  • Attention! JS !== JQuery
  • +
  • Avènement des SPA: React, Angular, Backbone, Vue ...
  • +
  • Multiplication des usages +
      +
    • Application desktop (Electron)
    • +
    • Application mobile (Cordova, Phonegap)
    • +
    • Application mobile "native" (React native)
    • +
    • Serveur web (NodeJS)
    • +
    • ...
    • +
    +
  • +
-

Les dates

-
-						
-// "J'ai bien lu la doc"
-date.getTime()  // => ex. 1290852603042
-
-// "#superSmart"
-+date
-
-// Et sinon…
-new Date(1301733937452)
+					

Standard ECMAScript (ECMA-262)

+

Il s'agit des normes Javascript écrites au cours des années.

+ + + + + + +
ES5 (2009)Version la plus connue, supportée par tous les navigateurs
ES 2015 (ES6)Ajoute énormément de modifications au langage
...Evolution annuelle de la norme
ES 2019Version actuelle de la norme
ES nextVersion destinée à devenir la nouvelle norme
+ +
On voit beaucoup de « ES7 » ou « ES8 » qui traînent : en général, ça fait référence à l’« ES next » du moment.
+
+
+

Transpilage

+

Le langage évoluant rapidement (tous les ans depuis 2015), les moteurs JS ne suivent pas toujours. Une étape de transpilage nous permet d'écrire notre code + avec toutes les avancées du langage, et rend cela compréhensible pour les différents moteurs JS

+
    +
  • Babel: Implémente les normes ES2017 et ESnext, les publie sous forme de module pour le développeur.
  • +
  • TypeScript: Implemente tout ou partie des normes ES2017 et ESnext. Ajoute un sucre syntaxique de typage pour le langage, ainsi qu'un jeu de fonctions avancées propre à ce typage.
  • +
+
+
+
+
+

Les bases

+
+
+

Types primitifs

+

Il y a 6 types primitifs en Javascript:

-// Mais aussi (car souci pour partir de composantes UTC) : -Date.UTC(y, m, d, h, mn, s, ms) // => numérique -
- +
    +
  • boolean: résultat d'une assertion logique true ou false
  • +
  • null: ne possède qu'une unique valeur null
  • +
  • undefined: représente la non-affectation d'une variable
  • +
  • number: contient l'ensemble des valeurs numéraires (entier, float, NaN, +Infinity, - Infinity)
  • +
  • string: représente une chaine de caractères à partir d'un ensemble d'élements 16bits non-signés
  • +
  • symbol: type de données uniques et inchangeables (souvent utilisé pour créer des identifiants)
  • +
+

Tout le reste est "Objet" (oui oui même les tableaux...)

+

Un objet avec une "call signature" est une fonction

-
- A retenir : Momentjs (on voit ça plus tard...) -
+

Objets

+

JavaScript est conçu autour d'un paradigme simple, basé sur les objets.

+

Un objet est un ensemble de propriétés. Une propriété est une association entre un nom (aussi appelé clé) et une valeur.

+

La valeur d'une propriété peut être une fonction, auquel cas la propriété peut être appelée « méthode ».

-
-

Les objets

+

Objets

-						
-let obj = { first: 'David', last: 'Lopez', age: 77 };
+							
+/// Création d'un objet
+const obj = { first: 'David', last: 'Lopez', age: 77 };
+/// Utilisation des propriétés
 obj.first  // => 'David'
-obj['age'] // => 42
-
+obj['age'] // => 77
+/// Affectation d'une valeur à une propriété
 obj.first = 'pierr';
-obj['first'] // => 'pierr'
-
+obj.first // => 'pierr'
+/// Attention si votre clé de propriété est un nombre!
 obj[77] = 'roberto';
 obj[77]   // => 'roberto'
 obj['77'] // => 'roberto'
 obj.77    // => SyntaxError
-						
-					
+/// Parce que je fais ce que je veux depuis ES2016 :p +obj['a'+ 12] = 'Gino'; +obj.a12; // => 'Gino' évidemment ;) + +
-

Les objets avec ES6

-
-						
-let defaults = { first: 'John', last: 'Doe', age: 42 };
-let trainer  = { last: 'Smith', age: 35 };
-trainer      = { ...defaults, ...trainer, age: 36 }
-// => { first: 'John', last: 'Smith', age: 36 }
-trainers['a'+ 12] = 'Gino';
-trainers.a12;
-// Gino
-						
-					
+					

Strings

+

Le type String de JavaScript est utilisé pour représenter des données textuelles.

+

C'est un ensemble d'"éléments" de valeurs non signées sur 16 bits (unités de codage UTF-16).

+

Chaque élément occupe une position dans la chaîne de caractères. Le premier élément se trouve à l'indice 0, le suivant à l'indice 1 et ainsi de suite.

+

La longueur d'une chaîne de caractères est le nombre d'éléments qu'elle contient.

+

Une String est immuable: il est impossible de la modifier une fois créée. Il est cependant possible de recréer une string à partir d'une autre string.

-

Les tableaux

-
+						

Strings

+
+								
+var maString = 'Hello World!';
+maString.length // => 12
+maString[0] // => 'H'
+maString[0] = 'X'; // Aucun effet. une string est immuable
+maString[0] // => 'H'
+/// Quelques fonctions
+'déjà' < 'demain' // => false Cette comparaison ne fait pas du tout ce que vous pensez!
+'déjà'.localeCompare('demain') // => -1 'déjà' est alphabétiquement avant 'demain'
+'déjà !'.toLocaleUpperCase()      // => 'DÉJÀ !' Renvoie une nouvelle instance en majuscule
+'ÇA POUTRE'.toLocaleLowerCase()   // => 'ça poutre' Renvoie une nouvelle instance en minuscule
+'one,two,three'.split(',')        // => ['one', 'two', 'three'] transformation en tableau selon un séparateur
+'one,,two,three'.split(/\W+/)     // => ['one', 'two'] transformation en tableau selon une expression régulière
+'hello'.substring(1)      // => 'ello' renvoie une nouvelle instance d'une partie de la chaine
+'hello'.slice(1, -2)      // => 'el' -> [1;length-2[
+								
+							
+
+
+

Les tableaux

+

Un Array est un ensemble ordonné de valeurs auxquelles on peut faire référence avec un nom et un indice.

+

JavaScript ne possède pas de type particulier pour représenter un tableau. En revanche, il est possible d'utiliser l'objet natif Array ainsi que ses méthodes pour manipuler des tableaux

+
+
+

Les tableaux

+
 						
 let names = ['John', 'David', 'Rodrigo'];
 
 names.length
-// => 3. R/W : astuce pour réutiliser un tableau!
+// => 3
 
 names[0]
 // => 'John'
@@ -174,13 +209,13 @@ 

Les tableaux

// => 13 names[9] -// => undefined (comme 10 et 11): c'est appelé "sparse array" -
-
+// => undefined (comme de 3 à 11): c'est appelé "sparse array" + +
+

Les tableaux

-						

Les tableaux

let data = [1, 2, 3]; @@ -207,256 +242,550 @@

Les tableaux

-
-						

Les tableaux en ES6

- - -let arr1 = ['one', 'two'], arr2 = ['three', 'four']; -arr1.push(...arr2) // => 4 -arr1 // => ['one', 'two', 'three', 'four'] -//le map -arr1.map(element => console.log(element)) -// for…in itère sur LES PROPRIÉTÉS ÉNUMÉRABLES de N’IMPORTE QUEL OBJET -var arr = ['hello', 'world', , 'cool']; -for (var k in arr){ console.log(k)}; // => 0, 1, 3 -// for…of itère sur les éléments d'un objet itérable -for (var k of arr){ console.log(k)}; // => hello, world, undefined, cool - - -
+

Dates

+

JavaScript ne possède pas de type primitif pour représenter des dates. Cependant l'objet Date et ses méthodes permettent de manipuler des dates et des heures au sein d'une application.

+

L'objet Date possède de nombreuses méthodes pour définir, modifier, obtenir des dates. Il ne possède pas de propriétés

+

JavaScript gère les dates de façon similaire à Java. (Donc assez mal...)

+

Les dates sont représentées selon les nombres de millisecondes écoulées depuis le 1er janvier 1970 à 00h00:00. + L'objet Date permet de représenter des dates allant de -100 000 000 jours jusqu'à +100 000 000 jours par rapport au 1er janvier 1970 UTC.

-
-						

"Déguiser" un objet en tableau

- -// Toi aussi, déguise-toi en tableau ! - -var fakeArray = { 0: '!', 1: 'ça torche', 2: 'JavaScript', length: 3 }; - -fakeArray.join = [].join; fakeArray.reverse = [].reverse; -fakeArray.reverse().join(' '); -// => 'JavaScript ça torche !' +

Dates

+
+								
+new Date()                  //  maintenant !
+new Date(y,m,d[,h,m,s,ms]) // Valeur découpée. (un peu lourd...)
 
-// Ou alors :
-fakeArray.__proto__ = Array.prototype;
-fakeArray.reverse().join(' ');
-// => 'JavaScript ça torche !'
+date.getYear() // JAMAIS #Bug de l'an 2000 exemple: L'année 1976 renverra 76. Mais l'année 2016 renverra 116. -_-'
+date.getFullYear()	// Mieux mais pas vraiment logique dans le nom...
+date.getMonth()     // NAZE Java-like qui démarre à… zéro (janvier). #ugly
+date.getDay()       // PERDUUUU ! C'est le DOW (Day Of Week), 0 = dim., 6 = sam
+date.getDate()      // Le jour du mois.  Super logique.
+date.getHours()
+date.getMinutes()
+date.getSeconds()
+date.getMilliseconds() // "Milliseconds", un seul mot : une seule initiale.
 
-// Méthodes « génériques » utilisables: hn
-// concat, join, pop, push, reverse, shift,
-// slice, sort, splice, toString, unshift.
-						
-					
+// Heure locale du navigateur/système. On a les mêmes en getUTCXxx()… +
+

+
Vous avez le choix dans l'implémentation de la date...
De manière générale, on préferera utiliser des libraries de dates type moment.js pour gérer des dates.
+
+
+

Operateurs

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
==true si les opérandes sont égaux après conversion en valeurs de mêmes types
!=true si les opérandes sont différents après conversion en valeurs de mêmes types.
===true si les opérandes sont égaux et de même type.
!==true si les opérandes ne sont pas égaux ou pas de même type.
>true si l'opérande gauche est supérieur (strictement) à l'opérande droit.
>=true si l'opérande gauche est supérieur ou égal à l'opérande droit.
<true si l'opérande gauche est inférieur (strictement) à l'opérande droit.
<=true si l'opérande gauche est inférieur ou égal à l'opérande droit.
-

Les boucles

-
-						
-const ARRAY = [1,2,3,4];
-for(let i = 0, _l=ARRAY.length; i < _l; i++ ){console.log(ARRAY[i])}
-
-ARRAY.forEach(function(element, idx){console.log(element, idx)});
-
-ARRAY.map(function(element, idx){console.log(element, idx)});
-						
-					
+

Quelques pièges de comparaisons en JS ...

+
+							
+	== ou === ?!
+
+	42 == '42'        // => true  --
+	null == undefined // => true  --
+	null == 0         // => false --
+	0 == undefined    // => false --
+	0 == false        // => true  --
+	1 == true         // => true  --
+	42 == true        // => false --
+	'0' == false      // => true  --
+	'' == false       // => true  --
+	NaN == NaN        // => false --
+							
+						
+
C'est franchement sale...
+
+
+

Pour éviter les écueils, utiliser ===

+
+							
+	// avec ===, fini de jouer : vérif valeur ET TYPE !
+
+	42 === '42'        // => false
+	null === undefined // => false
+	null === 0         // => false
+	0 === undefined    // => false
+	0 === false        // => false
+	'0' === false      // => false
+	NaN === NaN        // => false -- rien à faire !
+							
+						
+
Toujours utiliser la comparaison stricte === et !==
+
+
+

Opérateurs

+

Les opérateurs javascripts font des conversions implicites.

+

Le javascript ne plantera jamais sur une opération. En revanche, il peut vous renvoyer une valeur native comme Nan, +Infinity etc.

+

+	!42  => false
+	!!42  => true //pratique pour convertir n'importe quelle valeur en booleen
+	+'2'  => 2
+	"lala" + 42 => "lala42"
+	1/0 => Infinity
+	"lala" - 42 => NaN
+
+	{} + [] => 0 // Hum ok...
+	[] + {} => "[object Object]" // parce que why not!
+	{} + {} => "[object Object][object Object]" // Bon ça je veux bien
+	[] + [] => "" // Nan mais les gars...
+	[] - {} => NaN // -_-'
+	+[] => 0 // Ah ah
+	+[] + [] => "0" // Vous l'avez?
+	+[] + +[] => 0 // Bon ok j'arrete :=)
+					
-
-

Les fonctions

+

Opérateurs logiques

+

Ces opérateurs servent à résoudre des expressions logiques en prenant en compte le caractère "Truthy" ou "Falsy" des valeurs de l'expression

+ + + + + + + + + + + + + +
&&ET logique. Vrai si les deux expressions ont des valeur Truthy. S'arrête à la première valeur Falsy
||OU logique. Vrai si une des deux expressions a une valeur Truthy. S'arrête à la première valeur Truthy
!NON logique. Faux si l'expression a une valeur Truthy. Vrai sinon.
+
Attention: les opérateurs logiques &&, || et ! fonctionnent différemment des opérateurs binaire &, | et ~.
Pour plus d'info: La doc MDN
+
+
+

Truthy / Falsy

+

+"true" == true => false // la chaine de caratère "true" même après conversion n'est pas égale à true.
+"true" && true => true // la chaine de caratère "true" pour ce qui est des expression logique!!
+"true"&true => 0 // le & binaire renvoie le bit 0...
+"1" == true => true // bah oui après converstion "1" => 1 => true donc égale à true...
+					
+
Globalement les valeurs suivantes sont Falsy :
  • false
  • undefined
  • null
  • ""
  • 0
  • NaN
. Le reste est Truthy.
+
+
+

Destructuring / Spreading

+

Des opérateurs de décomposition (Spread) et d'affectation par décomposition (Destructuring) ont fait leur apparition pour manipuler plus simplement les objets et les collections.

+
    +
  • La syntaxe de décomposition permet d'étendre un itérable en lieu et place de plusieurs arguments (pour les appels de fonctions) ou de plusieurs éléments (pour les tableaux) ou de paires clés-valeurs (pour les objets).
  • +
  • L'affectation par décomposition est une expression JavaScript qui permet d'extraire des données d'un tableau ou d'un objet grâce à une syntaxe dont la forme ressemble à la structure du tableau ou de l'objet.
  • +
+
+
+

+ Destructuring / Spreading +

-						
-function maFonction() {
-	return 'test';
+							
+/* Arrays */
+// Initialisation d'un array
+const array = [1, 2, 3];
+// On crée un nouvel array à partir de l'ancien
+const array2 = [...array, 4, 5]; // array2 => [1,2,3,4,5]
+function f(a, b, c) {
+		return a + b + c;
 }
-maFonction() // test;
-const a = () => 'test';
-a(); //test
-						
-					
+// On appelle la fonction à partir de l'array +f(...array); // => 1+2+3 +// On récupère des éléments précis d'un array. +const [a, b, c, ...rest] = array2; // a=1; b=2, c=3, rest=[4,5] + +
-
-

Quelques pièges de comparaisons en JS ...

+

+ Destructuring / Spreading +

+
+								
+/* Objets */
+// Initialisation d'un objet.
+const objet = {a: 1, b: 2};
+// On crée un nouvel objet à partir de l'ancien
+const objet2 = {...objet, c: 3}; // objet2 = {a:1, b:2, c:3}
+// On récupère des éléments précis d'un objet.
+const {a, b, ...rest} = objet2; // a=1 b=2 rest={c:3}
+// On peut aussi déstructurer directement dans une fonction :
+function g({a, b, c: c2}) {
+		return a + b + c2;
+}
+g(objet2);
+								
+							
+
+
+

Interpolation de strings

+

Les nouvelles normes ECMAScript ont permis de faire évoluer le langage et de fournir un moyen de manipuler les strings. Il est désormais possible de créer des chaines de caractères multilignes et paramétrables!

-						
-== ou === ?!
-
-42 == '42'        // => true  -- 
-null == undefined // => true  -- 
-null == 0         // => false -- 
-0 == undefined    // => false -- 
-0 == false        // => true  -- 
-1 == true         // => true  -- 
-42 == true        // => false -- 
-'0' == false      // => true  -- 
-'' == false       // => true  -- 
-NaN == NaN        // => false -- 
-						
-					
+ +const person = { nom: "Ingargiola", prenom: "Thibault" }; + +const stringAvant = 'Bonjour je suis ' + person.prenom + ' ' + person.nom; +const stringApres= `Bonjour je suis ${objet.prenom} ${objet.nom}. +Et le javascript c'était pas mieux avant!`; + + +
+
+
+
+

Programmation en JS

-

Pour éviter les écueils, utiliser ===

+

Les fonctions

+

Les fonctions sont un peu la base de toute programmation... Le Javascript n'échappe pas à la règle.

+

En JavaScript, les fonctions sont des objets de première classe. Cela signifie qu'elles peuvent être manipulées et échangées, qu'elles peuvent avoir des propriétés et des méthodes, comme tous les autres objets JavaScript. Les fonctions sont des objets Function

+

Afin de renvoyer une valeur, la fonction doit comporter une instruction return. Une fonction qui ne renvoie pas de valeur retourne undefined.

+
+
+

Déclaration

-						
-// avec ===, fini de jouer : vérif valeur ET TYPE !
-
-42 === '42'        // => false
-null === undefined // => false
-null === 0         // => false
-0 === undefined    // => false
-0 === false        // => false
-'0' === false      // => false
-NaN === NaN        // => false -- rien à faire !
+						
+/// Fonction standard
+function a() {
+	/// this = contexte appelant
+}
+/// Variable fonction anonyme (ne pas utiliser)
+const b = function() {
+	/// this = contexte appelant
+}
+/// Variable fonction nommée (usage assez rare)
+const c = function c() {
+	/// this = contexte appelant
+}
+/// Lambda / "fat arrow" (anonyme)
+const d = () => {
+	/// this = contexte de la déclaration
+}
+/// fonction auto-appelante (usage particulier)
+(function(){
+	/// this = contexte appelant
+})()
 						
 					
-

in et delete

-
-						
+						

Déclaration

+
+							
+/// La syntaxe de a est un raccourci pour celle de b. (usage assez rare)
+const e {
+    a() {
+        /// this = contexte appelant
+    },
+    b: function b() {
+        /// this = contexte appelant
+    }
+}
+/// Dans une classe
+class G {
+    a() {
+        /// this = contexte appelant
+		}
+		/// Variable d'instance
+    b = () => {
+        /// this = instance de la classe
+    }
+}
+							
+						
+
+
+

Variables

+

Il existe trois façons de déclarer des variables en javascript:

+
    +
  • var: On déclare une variable dont la portée est celle de la fonction courante, éventuellement en initialisant sa valeur.
  • +
  • let: On déclare une variable dont la portée est celle du bloc courant, éventuellement en initialisant sa valeur.
  • +
  • const: On déclare une constante nommée, dont la portée est celle du bloc courant, accessible en lecture seule.
  • +
+
var n'est plus vraiment utilisé dans les nouvelles normes. On préfèrera utiliser const dans les 3/4 des cas, et let dans les cas ou la variables est réaffectable.
+
+
+

Boucles

+

Depuis ECMAScript2015, le prototype ARRAY a gagné de nouvelles méthodes d'itération sur les éléments. Nous allons en présenter deux ici:

+ + + + + + + + + + + +
mapimmutableCrée un nouveau tableau avec les résultats de l'appel d'une fonction fournie sur chaque élément du tableau appelant.
foreachmutablePermet d'exécuter une fonction donnée sur chaque élément du tableau. Ne renvoie rien
+
+
+

Boucles

+
+								
+const ARRAY = [1,2,3,4];
 
-let person = { name: 'Joe', langs: ['fr', 'en'] };
-
-'name' in person        // => true
-person.age = 35;
-'age' in person         // => true
-person.age = null;
-'age' in person         // => true
-delete person.age
-'age' in person         // => false
-person.age              // => undefined
-
-0 in person.langs       // => true
-'0' in person.langs     // => true
-person.langs[3] = 'de';
-2 in person.langs       // => false
-3 in person.langs       // => true
-delete person.langs[3]
-person.langs            // => ['fr', 'en', undefined, undefined]
-						
-					
+for(let i = 0, _l=ARRAY.length; i < _l; i++ ){console.log(ARRAY[i], i)} + +ARRAY.forEach(function(element, idx){console.log(element, idx)}); + +ARRAY.map(function(element, idx){console.log(element, idx)}); +
+
-

Falsy / Truthy

+

Scope

+

Les éléments créés en javascript ont une portée. Un Scope.

+

Lorsqu'une variable est déclarée avec var en dehors des fonctions, elle est appelée variable globale car elle est disponible pour tout le code contenu dans le document. Lorsqu'une variable est déclarée dans une fonction, elle est appelée variable locale car elle n'est disponible qu'au sein de cette fonction.

+

Avant la norme ECMAScript2015, il n'existait pas d'autre manière de déclarer des variables. Grâce aux mots clés const et let, il est possible de restreindre la portée d'une variable au bloc en cours (un bloc if par exemple)

-

XML

-
-					
-
-  
-    
-    
-    
-  
-
-					
-				
+

Scope

+
+							
+if (true) {
+	var x = 5;
+}
+console.log(x); // x vaut 5
 
+if (true) {
+	let y = 5;
+}
+console.log(y); // ReferenceError: y is not defined
+							
+						
-

JSON

+

Closure

+

Il est possible d'imbriquer une fonction au sein d'une fonction. La fonction imbriquée (interne) est privée par rapport à la fonction (externe) qui la contient. Cela forme ce qu'on appelle une Closure.

+

Étant donné qu'une fonction imbriquée est une closure, cela signifie que la fonction imbriquée peut « hériter » du scope de la fonction parente. Et donc avoir accès aux variables de cette fonction.

+
+
+

Closure

+

+function externe(x) {
+	function interne(y) {
+			return x + y;
+	}
+	return interne;
+}
+var fn_interne = externe(3); // fn_interne est donc une fonction qui prend en parametre y en renvoie 3 + y
+var resultat = fn_interne(5); // donc 8
+
+var resultat1 = externe(3)(5); // renvoie 8
+						
+
+
+

Closure

-					
-{
-    "menu": {
-        "id": "file",
-        "value": "File",
-        "popup": {
-            "menuitem": [
-                { "value": "New", "onclick": "CreateNewDoc()" },
-                { "value": "Open", "onclick": "OpenDoc()" },
-                { "value": "Close", "onclick": "CloseDoc()" }
-            ]
-        }
-    }
+							
+function publicFx() {
+	let dateAppel = Date.now();
+	return function() { console.log(dateAppel); };
 }
-					
-				
+let privilegedFx1 = publicFx(); +// Attendre un bref instant +let privilegedFx2 = publicFx(); +// privilegedFx(1,2) sont en fait les fonctions internes construites au +// sein de publicFx, qui grâce aux règles de portée "voient" +// dateAppel. Elles sont *closed over* par publicFx, ce qui fait +// que les valeurs de dateAppel au moment où les fonctions ont été +// renvoyéees sont préservées +privilegedFx1(); // => affiche la dateAppel du moment de la création de la fonction privilegedFx1! +privilegedFx2(); // => affiche la dateAppel d'après ! + + +
Les closures sont un bon moyen de rendre privée une partie du code. Elles seront très utiles pour créer des modules. Mais on y reviendra plus tard...
+
+

Contexte

+

Le contexte d'une fonction est représenté par la variable this.

+

La valeur de this sera déterminée à partir de la façon dont une fonction est appelée. Il n'est pas possible de lui affecter une valeur lors de l'exécution et sa valeur peut être différente à chaque fois que la fonction est appelée.

+

Les différentes évolutions du langage ont permis de mieux définir cette variable indépendamment de la façon dont elle est appelée : bind, fat-arrow...

+
+
+

Binding implicite

+

La valeur de this dépend donc de la façon dont il est appelé. De manière générale, lorsque this est utilisé dans une fonction, il aura la valeur de l'appelant.

+
+				
+const monObjet = {
+	firstName: 'Thibault',
+	lastName: 'Ingargiola',
+	fullName() {
+		console.log(this.firstName + ' ' + this.lastName)
+	},
+	strictFullName() {
+		"use strict";
+		console.log(this.firstName + ' ' + this.lastName)
+	}
+};
 
+monObjet.fullName(); // => log Thibault Ingargiola. Dans ce cas this === monObjet
+monObjet.strictFullName(); // => IDEM
 
-				
-

Oublions jQuery

+const newFullName = monObjet.fullname; const newStrictFullName = monObjet.strictFullname; +newFullName(); // => log undefined undefined. this vaut window. +newStrictFullName(); // => Error!!!!!!!! this vaut undefined +
+
+
+
+

Le problème

+

Comment faire si je veux utiliser this mais que je ne maitrise pas l'appelant?

+
+				
+var name = 'Mr X';
+let obj = {
+	name: 'Joe Lopez',
+	greet(whom) {
+	console.log(this);
+		console.log(this.name + ' salue ' + whom);
+	},
+	greetAll(first, second, last) {
+	console.log(this);
+		[first, second, last].forEach(this.greet);
+	}
+};
+obj.greet("les lopezs de France");
+// => 'Joe Lopez salut les lopezs de France !'
+let fx = obj.greet;
+fx("l’atelier") // => '"Mr X salue l’atelier"'
+obj.greetAll('David', 'Joe', 'Guénolé'); // => 'Mr X salue David, Mr X salue Joe, Mr X salue Guénolé'
+/// Le contexte est perdu au travers du foreach
+				
+			
+
+
+

Solution 1: la closure

+

On fixe le contexte et on fait un appel explicite à la fonction.

+
+				
+const obj = {
+	// …
+	greetAll(first, second, last) {
+		const that = this;
+		[first, second, last].forEach(function(name) {
+			that.greet(name);
+		});
+	}
+}
+				
+			
+
+
+

Solution 2: bind

+

La fonction bind permet de déterminer le contexte d'une fonction qu'importe la façon dont elle est appelée.

+
+				
+const obj = {
+	// …
+	greetAll(first, second, last) {
+		[first, second, last].forEach(this.greet.bind(this));
+	}
+}
+				
+			
+
Ici, on crée donc une nouvelle fonction pour les trois appels dont le contexte vaudra this.
+
+
+

Solution 3: la fat arrow

+

Cette notation permet de "binder" automatiquement le contexte englobant.

-					
-//Avant
-const monElement = $('[data-my-selector]');
-
-//Après
-const monElement = document.querySelector('[data-my-selector]');
+					
+	const obj = {
+		// …
+		greetAll(first, second, last) {
+			[first, second, last].forEach(name => this.greet(name));
+		}
+	}
 					
 				
-
+
+

Binding

+

La fat arrow c'est la vie!

+
+					
+['add', 'remove'].forEach((action) => {
+	['change', 'status', 'error'].forEach((elt) => {
+		this[`${action}${capitalizeDefinition}${capitalize(elt)}Listener`] = (cb) => {
+			this[`${action}Listener`](`${def}:${elt}`, cb);
+		}
+	})
+});
+					
+				
-
-
-

Les outils

-
-
-
-
-

Node.js

-
    -
  • JS côté serveur
  • -
  • Apporte plein d'outils sous forme de modules
  • -
  • npm (node package module)
  • -
-
-
-

Node.js

-
    -
  • package.json
  • -
  • node_modules
  • -
  • npm install -g ma-dependance-globale
  • -
  • npm install -g rimraf
  • -
  • npm install --save ma-dependance
  • -
-
-
-

Babel

-
-
-

Webpack

-
+
+

call et apply

+

Les fonctions call et apply permettent de fixer le contexte au travers du premier argument lors de l'appel. La fonction call prendra par la suite une liste d'argument là ou apply utilisera un tableau.

+
+				
+	function ajout(c, d){
+		return this.a + this.b + c + d;
+	}
 
-				
-

Debug

-
+ var o = {a:1, b:3}; + + ajout.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 + + ajout.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 +
+
-
-
-

Prototypes / Constructor

-
-
-

Classes ?

-
    -
  • Avant ES6 pas de classe
  • -
  • JS est un langage prototypale
  • -
  • En JS tout est un objet
  • -
  • Une propriété est identifiée par une paire nom / valeur
  • -
  • Une propriété peut être une fonction
  • -
-
+
+
-

Constructeur

-
    -
  • Fonctions servant à initialiser un nouvel objet. Le nom du constructeur est un peu comme « le nom de la classe »…
  • -
  • Toute fonction peut servir de constructeur : il suffit de l’appeler avec l’opérateur new.
  • -
  • Elle dispose alors d’une variable implicite this, qui représente la nouvelle « instance ».
  • -
  • L’objet créé référence son constructeur : constructor.
  • -
+

Le JS objet

+
On l'avait dit au début, mais le javascript n'a pas de de classe à proprement parler. C'est un langage prototypable!
-

Constructeur

-
-						
+						

Constructeur

+
    +
  • Fonctions servant à initialiser un nouvel objet. Le nom du constructeur est un peu comme « le nom de la classe »…
  • +
  • Toute fonction peut servir de constructeur : il suffit de l’appeler avec l’opérateur new.
  • +
  • Elle dispose alors d’une variable implicite this, qui représente la nouvelle « instance ».
  • +
  • L’objet créé référence son constructeur : constructor.
  • +
+
+
+

Constructeur

+
+							
 function Person(first, last) {
-  this.first = first;
-  this.last = last;
+this.first = first;
+this.last = last;
 }
 
 var joeLopezPerson = new Person('Joe', 'Lopez');
@@ -466,111 +795,133 @@ 

Constructeur

// Si on jouait aux cons ?! function LopezPerson(first, last) { - this.first = first; - this.last = last; - return { first: 'Anne', last: 'Pas Lopez' }; +this.first = first; +this.last = last; +return { first: 'Anne', last: 'Pas Lopez' }; } var oops = new LopezPerson('Henry', 'Lopez'); oops.first // => Anne -
-
-
- -
-

Prototypes

-
    -
  • Tout constructeur a un prototype : un objet qui définit les propriétés (et donc méthodes) partagées par tous les objets que produit ce constructeur.
  • -
  • Le prototype est « vivant » : si on le triture après l’appel au constructeur, ça marche quand même !
  • -
  • Techniquement, y’a plein d’autres trucs dans un prototype (réf. au constructeur, gestion de propriétés…).
  • -
  • On verra, plutôt vous verrez...
  • -
-
+
+ +
-

Prototype

-
-						
+						

Prototypes

+
    +
  • Tout constructeur a un prototype : un objet qui définit les propriétés (et donc méthodes) partagées par tous les objets que produit ce constructeur.
  • +
  • Le prototype est « vivant » : si on le triture après l’appel au constructeur, ça marche quand même !
  • +
  • Techniquement, y’a plein d’autres trucs dans un prototype (réf. au constructeur, gestion de propriétés…).
  • +
  • On verra, plutôt vous verrez...
  • +
+
+
+

Prototype

+
+							
 function Person(first, last) {
-  this.first = first;
-  this.last = last;
+	this.first = first;
+	this.last = last;
 }
 
 // On augmente l'existant…
 Person.prototype.fullName = function fullName() {
-  return this.first + ' ' + this.last;
+	return this.first + ' ' + this.last;
 }
 Person.prototype.greet = function greet() {
-  alert('Salut je m’appelle ' + this.first);
+	alert('Salut je m’appelle ' + this.first);
 }
 
 var john = new Person('John', 'Smith');
 john.fullName() // => 'John Smith'
 john.greet()    // 'Salut je m’appelle John'
-						
-					
-
-
-

Prototype (Don't)

-
-						
+							
+						
+
+
+

Prototype (Don't)

+
+							
 function Person(first, last) {
-  this.first = first;
-  this.last = last;
-  this.fullName = function fullName() {
-    return this.first + ' ' + this.last;
-  }
-  this.greet = function greet() {
-    alert('Salut je m’appelle ' + this.first);
-  }
+	this.first = first;
+	this.last = last;
+	this.fullName = function fullName() {
+		return this.first + ' ' + this.last;
+	}
+	this.greet = function greet() {
+		alert('Salut je m’appelle ' + this.first);
+	}
 }
 var john = new Person('John', 'Smith');
 john.fullName() // => 'John Smith'
 john.greet()    // 'Salut je m’appelle John'
-						
-					
-
Mauvaise pratique: on copie la fonction dans chaque constructeur
-
+
+ +
Mauvaise pratique: on copie la fonction dans chaque constructeur
+
-

Les classes ES6

+
+							

"Déguiser" un objet en tableau

+ +/// Toi aussi, déguise-toi en tableau ! + +var fakeArray = { 0: '!', 1: 'ça torche', 2: 'JavaScript', length: 3 }; + +fakeArray.join = [].join; fakeArray.reverse = [].reverse; +fakeArray.reverse().join(' '); +/// => 'JavaScript ça torche !' + +// Ou alors : +fakeArray.__proto__ = Array.prototype; +fakeArray.reverse().join(' '); +// => 'JavaScript ça torche !' + +// Méthodes « génériques » utilisables: hn +// concat, join, pop, push, reverse, shift, +// slice, sort, splice, toString, unshift. + +
+
+
+

Classe

+

Bon depuis ES2015 on a tout de même un équivalent de classe. Ce n'est que du sucre syntaxique, mais c'est quand même vachement bien!

-						
+							
 class TodoItem extends Component {
-  constructor(props, context) {
-    super(props, context);
-    this.state = {
-      editing: false
-    };
-  }
-
-  handleDoubleClick() {
-    this.setState({ editing: true });
-  }
-  …
+	constructor(props, context) {
+		super(props, context);
+		this.state = {
+			editing: false
+		};
+	}
+
+	handleDoubleClick() {
+		this.setState({ editing: true });
+	}
+	…
 }
-						
-					
-
Attention il s'agit de sucre syntaxique pas d'une classe comme en Java ou .NET. Le JS reste un langage prototypale.
+
+
-

La chaîne d'appel d'une méthode

- obj.prop ou obj['prop'] (c'est équivalent) +

La chaîne d'appel d'une méthode

+

Supposons que l'on fasse appel à l'attribut d'un objet: obj.prop

    -
  1. On part de l’objet indexé (obj)
  2. -
  3. Si on trouve prop dans ses own properties, on s’arrête là
  4. -
  5. Sinon, on passe sur le prototype du niveau supérieur : celui du constructeur de l’objet en cours*
  6. -
  7. On reprend à l’étape 2, sauf si on était déjà sur Object.prototype, auquel cas le lookup est fini, et échoue (undefined).
  8. +
  9. On part de l’objet indexé (obj)
  10. +
  11. Si on trouve prop dans ses own properties, on s’arrête là
  12. +
  13. Sinon, on passe sur le prototype du niveau supérieur : celui du constructeur de l’objet en cours*
  14. +
  15. On reprend à l’étape 2, sauf si on était déjà sur Object.prototype, auquel cas le lookup est fini, et échoue (undefined).
-
*conceptuellement, constructor.prototype ou __proto__
+
*on le trouve sous: constructor.prototype ou __proto__
-
-

La preuve en live...

-
-						
+				
+

La preuve en live...

+
+							
 function Person(first, last) {
-  this.first = first;
-  this.last = last;
+	this.first = first;
+	this.last = last;
 }
 Person.prototype.fullName = function fullName() {
-  return this.first + ' ' + this.last;
+	return this.first + ' ' + this.last;
 };
 const davidLopez = new Person('David', 'Lopez');
 
@@ -579,340 +930,224 @@ 

La preuve en live...

davidLopez.toString() // => '[object Object]', Object.prototype Person.prototype.toString = function personToString() { - return '#Person ' + this.fullName(); + return '#Person ' + this.fullName(); }; davidLopez.toString() // => "#Person David Lopez" -
-
+
+
-
-

Closure

-
-

Closure simple

-
-						
-function publicFx() {
-  let dateAppel = Date.now();
-  return function() { console.log(dateAppel); };
-}
-let privilegedFx1 = publicFx();
-// Attendre un bref instant
-let privilegedFx2 = publicFx();
-
-// privilegedFx(1,2) sont en fait les fonctions internes construites au
-// sein de publicFx, qui grâce aux règles de portée "voient"
-// dateAppel.  Elles sont *closed over* par publicFx, ce qui fait
-// que les valeurs de dateAppel au moment où les fonctions ont été
-// renvoyéees sont préservées
-privilegedFx1(); // => affiche la dateAppel du moment de la création de la fonction privilegedFx1!
-privilegedFx2(); // => affiche la dateAppel d'après !
-						
-					
+
+
+

Structure applicative

+

Une application SPA est composée de plusieurs fichiers javascripts. C'est quand même plus pratique pour coder!

+

On découpe donc une application en modules

+
+
+
+
-

Les modules sont une utilisation des closures

+

Module avant ES6

+

Les modules javascripts permettent d'isoler votre code du reste de l'application, d'organiser votre code, d'éviter les collision de noms etc.


-function yourModule(require, module, exports) {
+/// fichier1.js
+const private = 5;
+const monObjet = {
+	name: "lala",
+	age: 42 + private,
+	awesomeness: true
 
-  let widgets = {};
-  let util = require('util');
-  let Widget = require('widgets/base');
+}
 
-  function CoolWidget(elt) { … }
-  util.inherits(CoolWidget, Widget);
-  // …
+module.exports = monObjet;
 
-  module.exports = Widget;
-}
+/// fichier2.js
+const lala = require('./fichier1.js');
+lala.awesomeness; // true;
+private; // undefined
+lala.age; // 47
 					
-
Dans node on ne voit pas la fonction enrobante...
-
-
-

Le binding

- -
-
-

Le problème

-
-						
-var name = 'Mr X';
-let obj = {
-  name: 'Joe Lopez',
-  greet: function greet(whom) {
-	console.log(this) 
-    console.log(this.name + ' salue ' + whom);
-  },
-  greetAll: function greetAll(first, second, last) {
-	console.log(this)
-    [first, second, last].forEach(this.greet);
-  }
-};
-obj.greet("les lopezs de France");
-// => 'Joe Lopez salut les lopezs de France !'
-let fx = obj.greet;
-fx("l’atelier") // => '"Mr X salue l’atelier"'
-obj.greetAll('David', 'Joe', 'Guénolé'); // => 'Mr X salue David, Mr X salue Joe, Mr X salue undefined'
-						
-					
-
-
-

Comment faire ? La closure?

-
-						
-const obj = {
-  // …
-  greetAll: function greetAll(first, second, last) {
-    var that = this;
-    [first, second, last].forEach(function(name) {
-      that.greet(name);
-    });
-  }
-}
-						
-					
-
-
-

Comment faire ? ES6 style?

-
-						
-const obj = {
-  // …
-  greetAll(first, second, last) {
-    [first, second, last].forEach(name => this.greet(name));
-	//Ultra fat
-  }
-}
-						
-					
-

call et apply

-
-						
-
-
-//let fx = obj.greet;
-fx.call(obj, 'les singes') // Joe Lopez salue les singes
-
-
-let xy = { 0: 'Zero', 1: 'One', length: 2 };
-Array.prototype.join.call(xy, '-') // 'Zero-One'
-
-
-fx.apply(obj, ['']) // => 'Joe salue l’atelier'
-Array.prototype.push.apply(xy, ['Two', 'Three', 'Four']) // => 5
-xy // => { 0: 'Zero', 1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', length: 5 }
-
-
- -
+

Module depuis ES6

+

Il est possible d'exporter plusieurs variables au sein d'un même fichier en les nommant.

+

+	/// fichier1.js
+	export monObjet = {
+		name: "lala",
+		age: 42
+	}
+
+	export monObjet2 = {
+			name: "lola",
+			age: 8
+	}
+
+	/// fichier2.js
+	import {monObjet, monObjet2 as renommage} from "./fichier1.js";
+
+	monObjet.age; // 42
+	renommage.age; // 8
+						
+
+
+

Module depuis ES6

+

Il existe différentes manières d'exporter et d'importer des choses via module. Le mieux est d'aller voir la doc ;)

+

+	/// fichier1.js
+	export default  {
+		name: "lala",
+		age: 42
+	}
+
+	export const lolo = 28
+
+	/// fichier2.js
+	import lala from "./fichier1.js";
+	import * as monModule from './fichier1.js';
+
+	lala.age; // 42
+	monModule.lolo; // 28
+						
+
-
-
-

JS et asynchronisme

+
+
+

Gestion de l'asynchrone

+

Dans une application JS, vous aurez à gérer de l'asynchrone. Ne serait-ce que pour faire des appels à une API. Il existe plusieurs manière de gérer cela.

-

Callback

-
-						
+					

Callbacks

+

 function delayedAlert() {
-  window.setTimeout(slowAlert, 2000);
+	window.setTimeout(slowAlert, 2000);
 }
 
 function slowAlert() {
-  alert("That was really slow!");
+	alert("That was really slow!");
 }
 					
+
Attention le code de la callback est exécuté dans un contexte complètement différent!
-

Les promesses

-

- Une promesse a un état (pending, fullfilled, rejected).
- Elle est asynchrone, et se termine soit par un succès soit par une erreur et renvoie une nouvelle promesse. -

- promise.then(successCb,errorCb).then(otherSuccess, otherCb).catch(errorHandlingFn) - +

Promesses

+

L'objet Promise est utilisé pour réaliser des traitements de façon asynchrone. Une promesse représente une valeur qui peut être disponible maintenant, dans le futur voire jamais.

+

Une promesse a un état (pending, fullfilled, rejected). En fonction de sa résolution, elle appellera la callback associée via l'instruction then, qui renverra elle aussi une promesse.

+
promise.then(successCb,errorCb).then(otherSuccess, otherCb).catch(errorHandlingFn)
-
-

Les promesses

-

-const myPromise = new Promise((resolve, reject) => {
-	ajaxCall({success: resolve, error: reject});
-})
 
-Promise.resolve([1,2,3,4]);
-
-Promise.reject('ma super erreur')
-
-					
- -
-

Les promesses

-

-fetch('/users.json')
-  .then(function(response) {
-    return response.json()
-  }).then(function(json) {
-    console.log('parsed json', json)
-  }).catch(function(ex) {
-    console.log('parsing failed', ex)
-  })					
-
-
-
-
-

ES6 / 2015

-
-
-

Les modules de node

-
-						
-//Dans un fichier
-module.exports = monObjetAExporter;
-
-//Utilisation
-require('./mon_module_locale');
-//Utilisation d'un module npm
-require('mon_module');
-
-
-
-					
-
-
-

Les modules de Papa

-
-						
-import * as types from '../constants/ActionTypes';
-
-export function addTodo(text) {
-  return { type: types.ADD_TODO, text };
-}
-import React, { PropTypes, Component } from 'react';
-import classnames from 'classnames';
-import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';
-import 'http://material.js'
-…
-export default Footer;
-export const ADD_TODO = 'ADD_TODO';
-export const DELETE_TODO = 'DELETE_TODO';
-export const EDIT_TODO = 'EDIT_TODO';
-
-
-
-
-					
+

Promesses

+

+var promise1 = new Promise(function(resolve, reject) {
+	setTimeout(function() {
+		resolve('foo');
+	}, 300);
+});
+
+promise1.then(function(value) {
+	console.log(value);
+	// expected output: "foo"
+});
+					
-

Destruct

-
-						
-const { activeCount } = this.props;
-…
-const { filter: selectedFilter, onShow } = this.props;
-const [, filters] = output.props.children;
-…
-const [,, clear] = output.props.children;
-var { op: a, lhs: { op: b }, rhs: c } = getASTNode();
-Détails
-
-
+						

Promesses

-
-
+

+fetch('/users.json')
+.then(function(response) {
+	return response.json()
+}).then(function(json) {
+	console.log('parsed json', json)
+}).catch(function(ex) {
+	console.log('parsing failed', ex)
+})
+						
+
-

Les strings interpolation et multi lignes

-
-						
-const person = { first: 'Thomas', last: 'Anderson', age: 25, nickname: 'Neo' };
-
-// Interpolation de JS quelconque
-console.log(`${person.first} aka ${person.nickname}`)
-// => 'Thomas aka Neo'
-
-// Multi-ligne !
-const markup = `
  • -${person.first} ${person.last}, age ${person.age} -
  • `; - -
    - - -
    +

    Async/await

    +

    La déclaration async function définit une fonction asynchrone qui renvoie un objet AsyncFunction.

    +

    Une fonction asynchrone est une fonction qui s'exécute de façon asynchrone grâce à la boucle d'évènement en utilisant une promesse (Promise) comme valeur de retour.

    +

    L'opérateur await permet d'attendre la résolution d'une promesse. Il ne peut être utilisé qu'au sein d'une fonction asynchrone.

    +
    Au final, c'est un sucre syntaxique autour d'une promesse
    -
    -

    Valeur par défaut dans une fonction

    -
    -						
    -function add(source, numToAdd = 1){
    -	return source + numToAdd;
    +			
    +

    Async/await

    +
    
    +function resolveAfter2Seconds(x) {
    +	return new Promise(resolve => {
    +		setTimeout(() => {
    +			resolve(x);
    +		}, 2000);
    +	});
     }
    -						
    -
     
    -					
    -
    -
    -

    Les objets

    -
    -						
    -function editTodo(id, text) {
    -  return { type: types.EDIT_TODO, id, text };
    -  //On créé un objet avec pour clef le nom de la variable.
    +async function f1() {
    +	var x = await resolveAfter2Seconds(10);
    +	console.log(x); // 10
     }
    -const FILTERS = {
    -  [maVar1]: 'All',
    -  [maVar2]: 'Active',
    -  [maVar3]: 'Completed'
    -};
    -						
    -
    -
    -					
    -
    +f1(); +
    -
    -
    -

    LODASH / UNDERSCORE

    +
    -
    +
    -

    on ne réinvente pas la roue

    +

    L'éco-système JS

    -

    Les trucs utiles

    +

    lodash

    +

    Lodash est la librairie utilitaire la plus utilisée du monde JS. Si vous voulez manipuler des objets/collections etc. Lodash a certainement la fonction qu'il vous faut.

    • Est ce que ma variable est un nombre / function / tableau lodash/lang
    • Filtrer, trier, reduce.... lodash/{collection/object/array}
    • Plein de fonctions prêtes à l'emploi lodash/function
    • +
    • ...
    -
    -
    -
    -

    MOMENT

    -
    -

    on ne réinvente pas la roue

    - -
    -
    -

    Les trucs utiles

    +

    moment

    +

    Les dates en JS, c'est pas vraiment la joie... Du coup on utilise moment. C'est bien plus pratique!

    • formatter des dates moment().format('L')
    • Manipuler des dates moment(maDate).add('days', 1)
    • Timezone...
    -
    Si tu manipules des dates tu utilises momentjs
    -
    -
    -

    Exercices

    +

    i18n

    +

    i18n vous sera utile pour gérer l'internationalisation de votre application. L'intégralité de vos ressources textuelles passeront par cette librairie pour pouvoir gérer le multilinguisme.

    +
    +
    +

    node.js

    +

    + Node.js vous permet de créer des applicatifs écrits en javascript, et notamment des serveurs. On l'utilisera notamment pour éxécuter l'intégralité de la toolchain d'un projet SPA. +

    +
    +
    +

    babel

    +

    + Babel est le traducteur universelle du javascript. Il permet de transpiler le langage écrit dans une version évoluée d'ECMAScript en une version compréhensible pour n'importe quel navigateur. +

    +
    +
    +

    webpack

    +

    + Webpack est un ordonnanceur javascript utilisé pour builder les projets javascript. Il permet d'ajouter plusieurs modules à un pipeline de build. +

    +

    Au travers de ce pipeline de build, on passe d'un projet complexe avec des modules, des librairies etc. à un livrable composé de fichiers statiques à intégrer dans une page web.

    +
    transpilage --> obfuscation --> compréssion --> postCss --> ...
    +
    +
    +

    npm

    +

    + Npm (Node Package Manager) est le gestionnaire de paquet qui vient avec Node.js. Il vous servira à gérer l'intégralité de vos dépendances javascripts. +

    +
    npm install internet
    @@ -940,17 +1175,5 @@

    - - diff --git a/js/reveal.js b/js/reveal.js index ff5ea53..eaa9d60 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -1,9 +1,9 @@ /*! * reveal.js - * http://lab.hakim.se/reveal-js + * http://revealjs.com * MIT licensed * - * Copyright (C) 2015 Hakim El Hattab, http://hakim.se + * Copyright (C) 2018 Hakim El Hattab, http://hakim.se */ (function( root, factory ) { if( typeof define === 'function' && define.amd ) { @@ -25,10 +25,14 @@ var Reveal; + // The reveal.js version + var VERSION = '3.7.0'; + var SLIDES_SELECTOR = '.slides section', HORIZONTAL_SLIDES_SELECTOR = '.slides>section', VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section', HOME_SLIDE_SELECTOR = '.slides>section:first-of-type', + UA = navigator.userAgent, // Configuration defaults, can be overridden at initialization time config = { @@ -39,21 +43,39 @@ height: 700, // Factor of the display size that should remain empty around the content - margin: 0.1, + margin: 0.04, // Bounds for smallest/largest possible scale to apply to content minScale: 0.2, - maxScale: 1.5, + maxScale: 2.0, - // Display controls in the bottom right corner + // Display presentation control arrows controls: true, + // Help the user learn the controls by providing hints, for example by + // bouncing the down arrow when they first encounter a vertical slide + controlsTutorial: true, + + // Determines where controls appear, "edges" or "bottom-right" + controlsLayout: 'bottom-right', + + // Visibility rule for backwards navigation arrows; "faded", "hidden" + // or "visible" + controlsBackArrows: 'faded', + // Display a presentation progress bar progress: true, // Display the page number of the current slide slideNumber: false, + // Use 1 based indexing for # links to match slide number (default is zero + // based) + hashOneBasedIndex: false, + + // Determine which displays to show the slide number on + showSlideNumber: 'all', + // Push each slide change to the browser history history: false, @@ -66,6 +88,10 @@ // Enable the slide overview mode overview: true, + // Disables the default reveal.js slide layout so that you can use + // custom CSS layout + disableLayout: false, + // Vertical centering of slides center: true, @@ -78,28 +104,54 @@ // Change the presentation direction to be RTL rtl: false, + // Randomizes the order of slides each time the presentation loads + shuffle: false, + // Turns fragments on and off globally fragments: true, + // Flags whether to include the current fragment in the URL, + // so that reloading brings you to the same fragment position + fragmentInURL: false, + // Flags if the presentation is running in an embedded mode, // i.e. contained within a limited portion of the screen embedded: false, - // Flags if we should show a help overlay when the questionmark + // Flags if we should show a help overlay when the question-mark // key is pressed help: true, // Flags if it should be possible to pause the presentation (blackout) pause: true, - // Number of milliseconds between automatically proceeding to the - // next slide, disabled when set to 0, this value can be overwritten - // by using a data-autoslide attribute on your slides + // Flags if speaker notes should be visible to all viewers + showNotes: false, + + // Global override for autolaying embedded media (video/audio/iframe) + // - null: Media will only autoplay if data-autoplay is present + // - true: All media will autoplay, regardless of individual setting + // - false: No media will autoplay, regardless of individual setting + autoPlayMedia: null, + + // Controls automatic progression to the next slide + // - 0: Auto-sliding only happens if the data-autoslide HTML attribute + // is present on the current slide or fragment + // - 1+: All slides will progress automatically at the given interval + // - false: No auto-sliding, even if data-autoslide is present autoSlide: 0, // Stop auto-sliding after user input autoSlideStoppable: true, + // Use this method for navigation when auto-sliding (defaults to navigateNext) + autoSlideMethod: null, + + // Specify the average time in seconds that you think you will spend + // presenting each slide. This is used to show a pacing timer in the + // speaker view + defaultTiming: null, + // Enable slide navigation via mouse wheel mouseWheel: false, @@ -110,6 +162,8 @@ hideAddressBar: true, // Opens links in an iframe preview overlay + // Add `data-preview-link` and `data-preview-link="false"` to customise each link + // individually previewLinks: false, // Exposes the reveal.js API through window.postMessage @@ -118,7 +172,7 @@ // Dispatches all reveal.js events to the parent window through postMessage postMessageEvents: false, - // Focuses body when page changes visiblity to ensure keyboard shortcuts work + // Focuses body when page changes visibility to ensure keyboard shortcuts work focusBodyOnPageVisibilityChange: true, // Transition style @@ -136,24 +190,54 @@ // Parallax background size parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px" + // Parallax background repeat + parallaxBackgroundRepeat: '', // repeat/repeat-x/repeat-y/no-repeat/initial/inherit + + // Parallax background position + parallaxBackgroundPosition: '', // CSS syntax, e.g. "top left" + // Amount of pixels to move the parallax background per slide step parallaxBackgroundHorizontal: null, parallaxBackgroundVertical: null, + // The maximum number of pages a single slide can expand onto when printing + // to PDF, unlimited by default + pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY, + + // Prints each fragment on a separate slide + pdfSeparateFragments: true, + + // Offset used to reduce the height of content within exported PDF pages. + // This exists to account for environment differences based on how you + // print to PDF. CLI printing options, like phantomjs and wkpdf, can end + // on precisely the total height of the document whereas in-browser + // printing has to end one pixel before. + pdfPageHeightOffset: -1, + // Number of slides away from the current that are visible viewDistance: 3, + // The display mode that will be used to show slides + display: 'block', + // Script dependencies to load dependencies: [] }, + // Flags if Reveal.initialize() has been called + initialized = false, + // Flags if reveal.js is loaded (has dispatched the 'ready' event) loaded = false, // Flags if the overview mode is currently active overview = false, + // Holds the dimensions of our overview slides, including margins + overviewSlideWidth = null, + overviewSlideHeight = null, + // The horizontal and vertical index of the currently active slide indexh, indexv, @@ -164,6 +248,10 @@ previousBackground, + // Remember which directions that the user has navigated towards + hasNavigatedRight = false, + hasNavigatedDown = false, + // Slides may hold a data-state attribute which we pick up and apply // as a class to the body. This list contains the combined state of // all current slides. @@ -185,6 +273,9 @@ // Client is a mobile device, see #checkCapabilities() isMobileDevice, + // Client is a desktop Chrome, see #checkCapabilities() + isChrome, + // Throttles mouse wheel navigation lastMouseWheelStep = 0, @@ -226,13 +317,21 @@ 'B , .': 'Pause', 'F': 'Fullscreen', 'ESC, O': 'Slide overview' - }; + }, + + // Holds custom key code mappings + registeredKeyBindings = {}; /** * Starts up the presentation if the client is capable. */ function initialize( options ) { + // Make sure we only initialize once + if( initialized === true ) return; + + initialized = true; + checkCapabilities(); if( !features.transforms2d && !features.transforms3d ) { @@ -289,40 +388,47 @@ */ function checkCapabilities() { - features.transforms3d = 'WebkitPerspective' in document.body.style || - 'MozPerspective' in document.body.style || - 'msPerspective' in document.body.style || - 'OPerspective' in document.body.style || - 'perspective' in document.body.style; + isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ); + isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA ); + + var testElement = document.createElement( 'div' ); - features.transforms2d = 'WebkitTransform' in document.body.style || - 'MozTransform' in document.body.style || - 'msTransform' in document.body.style || - 'OTransform' in document.body.style || - 'transform' in document.body.style; + features.transforms3d = 'WebkitPerspective' in testElement.style || + 'MozPerspective' in testElement.style || + 'msPerspective' in testElement.style || + 'OPerspective' in testElement.style || + 'perspective' in testElement.style; + + features.transforms2d = 'WebkitTransform' in testElement.style || + 'MozTransform' in testElement.style || + 'msTransform' in testElement.style || + 'OTransform' in testElement.style || + 'transform' in testElement.style; features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function'; features.canvas = !!document.createElement( 'canvas' ).getContext; - features.touch = !!( 'ontouchstart' in window ); - // Transitions in the overview are disabled in desktop and - // mobile Safari due to lag - features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( navigator.userAgent ); + // Safari due to lag + features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( UA ); - isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( navigator.userAgent ); + // Flags if we should use zoom instead of transform to scale + // up slides. Zoom produces crisper results but has a lot of + // xbrowser quirks so we only use it in whitelsited browsers. + features.zoom = 'zoom' in testElement.style && !isMobileDevice && + ( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) ); } - /** - * Loads the dependencies of reveal.js. Dependencies are - * defined via the configuration option 'dependencies' - * and will be loaded prior to starting/binding reveal.js. - * Some dependencies may have an 'async' flag, if so they - * will load after reveal.js has been started up. - */ + /** + * Loads the dependencies of reveal.js. Dependencies are + * defined via the configuration option 'dependencies' + * and will be loaded prior to starting/binding reveal.js. + * Some dependencies may have an 'async' flag, if so they + * will load after reveal.js has been started up. + */ function load() { var scripts = [], @@ -340,7 +446,7 @@ } function loadScript( s ) { - head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], function() { + head.ready( s.src.match( /([\w\d_\-]*)\.?js(\?[\w\d.=&]*)?$|[^\\\/]*$/i )[0], function() { // Extension may contain callback functions if( typeof s.callback === 'function' ) { s.callback.apply( this ); @@ -386,14 +492,16 @@ */ function start() { + loaded = true; + // Make sure we've got all the DOM elements we need setupDOM(); // Listen to messages posted to this window setupPostMessage(); - // Prevent iframes from scrolling the slides out of view - setupIframeScrollPrevention(); + // Prevent the slides from being scrolled out of view + setupScrollPrevention(); // Resets all vertical slides so that only the first is visible resetVerticalSlides(); @@ -413,7 +521,7 @@ // Enable transitions now that we're loaded dom.slides.classList.remove( 'no-transition' ); - loaded = true; + dom.wrapper.classList.add( 'ready' ); dispatchEvent( 'ready', { 'indexh': indexh, @@ -448,6 +556,20 @@ // Prevent transitions while we're loading dom.slides.classList.add( 'no-transition' ); + if( isMobileDevice ) { + dom.wrapper.classList.add( 'no-hover' ); + } + else { + dom.wrapper.classList.remove( 'no-hover' ); + } + + if( /iphone/gi.test( UA ) ) { + dom.wrapper.classList.add( 'ua-iphone' ); + } + else { + dom.wrapper.classList.remove( 'ua-iphone' ); + } + // Background element dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null ); @@ -456,21 +578,23 @@ dom.progressbar = dom.progress.querySelector( 'span' ); // Arrow controls - createSingletonNode( dom.wrapper, 'aside', 'controls', - '' + - '' + - '' + - '' ); + dom.controls = createSingletonNode( dom.wrapper, 'aside', 'controls', + '' + + '' + + '' + + '' ); // Slide number dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' ); - // Overlay graphic which is displayed during the paused mode - createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null ); + // Element containing notes that are visible to the audience + dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null ); + dom.speakerNotes.setAttribute( 'data-prevent-swipe', '' ); + dom.speakerNotes.setAttribute( 'tabindex', '0' ); - // Cache references to elements - dom.controls = document.querySelector( '.reveal .controls' ); - dom.theme = document.querySelector( '#theme' ); + // Overlay graphic which is displayed during the paused mode + dom.pauseOverlay = createSingletonNode( dom.wrapper, 'div', 'pause-overlay', '' ); + dom.resumeButton = dom.pauseOverlay.querySelector( '.resume-button' ); dom.wrapper.setAttribute( 'role', 'application' ); @@ -482,6 +606,10 @@ dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) ); dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) ); + // The right and down arrows in the standard reveal.js controls + dom.controlsRightArrow = dom.controls.querySelector( '.navigate-right' ); + dom.controlsDownArrow = dom.controls.querySelector( '.navigate-down' ); + dom.statusDiv = createStatusDiv(); } @@ -489,6 +617,8 @@ * Creates a hidden div with role aria-live to announce the * current slide content. Hide the div off-screen to make it * available only to Assistive Technologies. + * + * @return {HTMLElement} */ function createStatusDiv() { @@ -498,7 +628,7 @@ statusDiv.style.position = 'absolute'; statusDiv.style.height = '1px'; statusDiv.style.width = '1px'; - statusDiv.style.overflow ='hidden'; + statusDiv.style.overflow = 'hidden'; statusDiv.style.clip = 'rect( 1px, 1px, 1px, 1px )'; statusDiv.setAttribute( 'id', 'aria-status-div' ); statusDiv.setAttribute( 'aria-live', 'polite' ); @@ -509,6 +639,38 @@ } + /** + * Converts the given HTML element into a string of text + * that can be announced to a screen reader. Hidden + * elements are excluded. + */ + function getStatusText( node ) { + + var text = ''; + + // Text node + if( node.nodeType === 3 ) { + text += node.textContent; + } + // Element node + else if( node.nodeType === 1 ) { + + var isAriaHidden = node.getAttribute( 'aria-hidden' ); + var isDisplayHidden = window.getComputedStyle( node )['display'] === 'none'; + if( isAriaHidden !== 'true' && !isDisplayHidden ) { + + toArray( node.childNodes ).forEach( function( child ) { + text += getStatusText( child ); + } ); + + } + + } + + return text; + + } + /** * Configures the presentation for printing to a static * PDF. @@ -519,14 +681,14 @@ // Dimensions of the PDF pages var pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ), - pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) ); + pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) ); // Dimensions of slides within the pages var slideWidth = slideSize.width, slideHeight = slideSize.height; // Let the browser know what page size we want to print - injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0;}' ); + injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' ); // Limit the size of certain elements to the dimensions of the slide injectStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' ); @@ -535,6 +697,22 @@ document.body.style.width = pageWidth + 'px'; document.body.style.height = pageHeight + 'px'; + // Make sure stretch elements fit on slide + layoutSlideContents( slideWidth, slideHeight ); + + // Add each slide's index as attributes on itself, we need these + // indices to generate slide numbers below + toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { + hslide.setAttribute( 'data-index-h', h ); + + if( hslide.classList.contains( 'stack' ) ) { + toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) { + vslide.setAttribute( 'data-index-h', h ); + vslide.setAttribute( 'data-index-v', v ); + } ); + } + } ); + // Slide and slide background layout toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { @@ -545,56 +723,150 @@ var left = ( pageWidth - slideWidth ) / 2, top = ( pageHeight - slideHeight ) / 2; - var contentHeight = getAbsoluteHeight( slide ); + var contentHeight = slide.scrollHeight; var numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 ); + // Adhere to configured pages per slide limit + numberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide ); + // Center slides vertically if( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) { top = Math.max( ( pageHeight - contentHeight ) / 2, 0 ); } + // Wrap the slide in a page element and hide its overflow + // so that no page ever flows onto another + var page = document.createElement( 'div' ); + page.className = 'pdf-page'; + page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px'; + slide.parentNode.insertBefore( page, slide ); + page.appendChild( slide ); + // Position the slide inside of the page slide.style.left = left + 'px'; slide.style.top = top + 'px'; slide.style.width = slideWidth + 'px'; - // TODO Backgrounds need to be multiplied when the slide - // stretches over multiple pages - var background = slide.querySelector( '.slide-background' ); - if( background ) { - background.style.width = pageWidth + 'px'; - background.style.height = ( pageHeight * numberOfPages ) + 'px'; - background.style.top = -top + 'px'; - background.style.left = -left + 'px'; + if( slide.slideBackgroundElement ) { + page.insertBefore( slide.slideBackgroundElement, slide ); + } + + // Inject notes if `showNotes` is enabled + if( config.showNotes ) { + + // Are there notes for this slide? + var notes = getSlideNotes( slide ); + if( notes ) { + + var notesSpacing = 8; + var notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline'; + var notesElement = document.createElement( 'div' ); + notesElement.classList.add( 'speaker-notes' ); + notesElement.classList.add( 'speaker-notes-pdf' ); + notesElement.setAttribute( 'data-layout', notesLayout ); + notesElement.innerHTML = notes; + + if( notesLayout === 'separate-page' ) { + page.parentNode.insertBefore( notesElement, page.nextSibling ); + } + else { + notesElement.style.left = notesSpacing + 'px'; + notesElement.style.bottom = notesSpacing + 'px'; + notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px'; + page.appendChild( notesElement ); + } + + } + + } + + // Inject slide numbers if `slideNumbers` are enabled + if( config.slideNumber && /all|print/i.test( config.showSlideNumber ) ) { + var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1, + slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1; + + var numberElement = document.createElement( 'div' ); + numberElement.classList.add( 'slide-number' ); + numberElement.classList.add( 'slide-number-pdf' ); + numberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV ); + page.appendChild( numberElement ); + } + + // Copy page and show fragments one after another + if( config.pdfSeparateFragments ) { + + // Each fragment 'group' is an array containing one or more + // fragments. Multiple fragments that appear at the same time + // are part of the same group. + var fragmentGroups = sortFragments( page.querySelectorAll( '.fragment' ), true ); + + var previousFragmentStep; + var previousPage; + + fragmentGroups.forEach( function( fragments ) { + + // Remove 'current-fragment' from the previous group + if( previousFragmentStep ) { + previousFragmentStep.forEach( function( fragment ) { + fragment.classList.remove( 'current-fragment' ); + } ); + } + + // Show the fragments for the current index + fragments.forEach( function( fragment ) { + fragment.classList.add( 'visible', 'current-fragment' ); + } ); + + // Create a separate page for the current fragment state + var clonedPage = page.cloneNode( true ); + page.parentNode.insertBefore( clonedPage, ( previousPage || page ).nextSibling ); + + previousFragmentStep = fragments; + previousPage = clonedPage; + + } ); + + // Reset the first/original page so that all fragments are hidden + fragmentGroups.forEach( function( fragments ) { + fragments.forEach( function( fragment ) { + fragment.classList.remove( 'visible', 'current-fragment' ); + } ); + } ); + } + // Show all fragments + else { + toArray( page.querySelectorAll( '.fragment:not(.fade-out)' ) ).forEach( function( fragment ) { + fragment.classList.add( 'visible' ); + } ); + } + } } ); - // Show all fragments - toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' .fragment' ) ).forEach( function( fragment ) { - fragment.classList.add( 'visible' ); - } ); + // Notify subscribers that the PDF layout is good to go + dispatchEvent( 'pdf-ready' ); } /** - * This is an unfortunate necessity. Iframes can trigger the - * parent window to scroll, for example by focusing an input. + * This is an unfortunate necessity. Some actions – such as + * an input field being focused in an iframe or using the + * keyboard to expand text selection beyond the bounds of + * a slide – can trigger our content to be pushed out of view. * This scrolling can not be prevented by hiding overflow in - * CSS so we have to resort to repeatedly checking if the - * browser has decided to offset our slides :( + * CSS (we already do) so we have to resort to repeatedly + * checking if the slides have been offset :( */ - function setupIframeScrollPrevention() { + function setupScrollPrevention() { - if( dom.slides.querySelector( 'iframe' ) ) { - setInterval( function() { - if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) { - dom.wrapper.scrollTop = 0; - dom.wrapper.scrollLeft = 0; - } - }, 500 ); - } + setInterval( function() { + if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) { + dom.wrapper.scrollTop = 0; + dom.wrapper.scrollLeft = 0; + } + }, 1000 ); } @@ -602,6 +874,13 @@ * Creates an HTML element and returns a reference to it. * If the element already exists the existing instance will * be returned. + * + * @param {HTMLElement} container + * @param {string} tagname + * @param {string} classname + * @param {string} innerHTML + * + * @return {HTMLElement} */ function createSingletonNode( container, tagname, classname, innerHTML ) { @@ -619,7 +898,7 @@ // If no node was found, create it now var node = document.createElement( tagname ); - node.classList.add( classname ); + node.className = classname; if( typeof innerHTML === 'string' ) { node.innerHTML = innerHTML; } @@ -645,24 +924,12 @@ // Iterate over all horizontal slides toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) { - var backgroundStack; - - if( printMode ) { - backgroundStack = createBackground( slideh, slideh ); - } - else { - backgroundStack = createBackground( slideh, dom.background ); - } + var backgroundStack = createBackground( slideh, dom.background ); // Iterate over all vertical slides toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) { - if( printMode ) { - createBackground( slidev, slidev ); - } - else { - createBackground( slidev, backgroundStack ); - } + createBackground( slidev, backgroundStack ); backgroundStack.classList.add( 'stack' ); @@ -675,6 +942,8 @@ dom.background.style.backgroundImage = 'url("' + config.parallaxBackgroundImage + '")'; dom.background.style.backgroundSize = config.parallaxBackgroundSize; + dom.background.style.backgroundRepeat = config.parallaxBackgroundRepeat; + dom.background.style.backgroundPosition = config.parallaxBackgroundPosition; // Make sure the below properties are set on the element - these properties are // needed for proper transitions to be set on the element via CSS. To remove @@ -700,9 +969,61 @@ * @param {HTMLElement} slide * @param {HTMLElement} container The element that the background * should be appended to + * @return {HTMLElement} New background div */ function createBackground( slide, container ) { + + // Main slide background element + var element = document.createElement( 'div' ); + element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' ); + + // Inner background element that wraps images/videos/iframes + var contentElement = document.createElement( 'div' ); + contentElement.className = 'slide-background-content'; + + element.appendChild( contentElement ); + container.appendChild( element ); + + slide.slideBackgroundElement = element; + slide.slideBackgroundContentElement = contentElement; + + // Syncs the background to reflect all current background settings + syncBackground( slide ); + + return element; + + } + + /** + * Renders all of the visual properties of a slide background + * based on the various background attributes. + * + * @param {HTMLElement} slide + */ + function syncBackground( slide ) { + + var element = slide.slideBackgroundElement, + contentElement = slide.slideBackgroundContentElement; + + // Reset the prior background state in case this is not the + // initial sync + slide.classList.remove( 'has-dark-background' ); + slide.classList.remove( 'has-light-background' ); + + element.removeAttribute( 'data-loaded' ); + element.removeAttribute( 'data-background-hash' ); + element.removeAttribute( 'data-background-size' ); + element.removeAttribute( 'data-background-transition' ); + element.style.backgroundColor = ''; + + contentElement.style.backgroundSize = ''; + contentElement.style.backgroundRepeat = ''; + contentElement.style.backgroundPosition = ''; + contentElement.style.backgroundImage = ''; + contentElement.style.opacity = ''; + contentElement.innerHTML = ''; + var data = { background: slide.getAttribute( 'data-background' ), backgroundSize: slide.getAttribute( 'data-background-size' ), @@ -712,17 +1033,13 @@ backgroundColor: slide.getAttribute( 'data-background-color' ), backgroundRepeat: slide.getAttribute( 'data-background-repeat' ), backgroundPosition: slide.getAttribute( 'data-background-position' ), - backgroundTransition: slide.getAttribute( 'data-background-transition' ) + backgroundTransition: slide.getAttribute( 'data-background-transition' ), + backgroundOpacity: slide.getAttribute( 'data-background-opacity' ) }; - var element = document.createElement( 'div' ); - - // Carry over custom classes from the slide to the background - element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' ); - if( data.background ) { // Auto-wrap image urls in url(...) - if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)$/gi.test( data.background ) ) { + if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test( data.background ) ) { slide.setAttribute( 'data-background-image', data.background ); } else { @@ -742,34 +1059,33 @@ data.backgroundColor + data.backgroundRepeat + data.backgroundPosition + - data.backgroundTransition ); + data.backgroundTransition + + data.backgroundOpacity ); } // Additional and optional background properties - if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize; + if( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize ); if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor; - if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat; - if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition; if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition ); - container.appendChild( element ); - - // If backgrounds are being recreated, clear old classes - slide.classList.remove( 'has-dark-background' ); - slide.classList.remove( 'has-light-background' ); + // Background image options are set on the content wrapper + if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize; + if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat; + if( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition; + if( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity; // If this slide has a background color, add a class that // signals if it is light or dark. If the slide has no background // color, no class will be set - var computedBackgroundColor = window.getComputedStyle( element ).backgroundColor; - if( computedBackgroundColor ) { - var rgb = colorToRgb( computedBackgroundColor ); + var computedBackgroundStyle = window.getComputedStyle( element ); + if( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) { + var rgb = colorToRgb( computedBackgroundStyle.backgroundColor ); // Ignore fully transparent backgrounds. Some browsers return // rgba(0,0,0,0) when reading the computed background color of // an element with no background if( rgb && rgb.a !== 0 ) { - if( colorBrightness( computedBackgroundColor ) < 128 ) { + if( colorBrightness( computedBackgroundStyle.backgroundColor ) < 128 ) { slide.classList.add( 'has-dark-background' ); } else { @@ -778,8 +1094,6 @@ } } - return element; - } /** @@ -815,17 +1129,27 @@ /** * Applies the configuration settings from the config * object. May be called multiple times. + * + * @param {object} options */ function configure( options ) { - var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length; - - dom.wrapper.classList.remove( config.transition ); + var oldTransition = config.transition; // New config options may be passed when this method // is invoked through the API after initialization if( typeof options === 'object' ) extend( config, options ); + // Abort if reveal.js hasn't finished loading, config + // changes will be applied automatically once loading + // finishes + if( loaded === false ) return; + + var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length; + + // Remove the previously configured transition class + dom.wrapper.classList.remove( oldTransition ); + // Force linear transition based on browser capabilities if( features.transforms3d === false ) config.transition = 'linear'; @@ -837,6 +1161,13 @@ dom.controls.style.display = config.controls ? 'block' : 'none'; dom.progress.style.display = config.progress ? 'block' : 'none'; + dom.controls.setAttribute( 'data-controls-layout', config.controlsLayout ); + dom.controls.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows ); + + if( config.shuffle ) { + shuffle(); + } + if( config.rtl ) { dom.wrapper.classList.add( 'rtl' ); } @@ -856,6 +1187,10 @@ resume(); } + if( config.showNotes ) { + dom.speakerNotes.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' ); + } + if( config.mouseWheel ) { document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF document.addEventListener( 'mousewheel', onDocumentMouseScroll, false ); @@ -876,10 +1211,11 @@ // Iframe link previews if( config.previewLinks ) { enablePreviewLinks(); + disablePreviewLinks( '[data-preview-link=false]' ); } else { disablePreviewLinks(); - enablePreviewLinks( '[data-preview-link]' ); + enablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' ); } // Remove existing auto-slide controls @@ -906,6 +1242,19 @@ } ); } + // Slide numbers + var slideNumberDisplay = 'none'; + if( config.slideNumber && !isPrintingPDF() ) { + if( config.showSlideNumber === 'all' ) { + slideNumberDisplay = 'block'; + } + else if( config.showSlideNumber === 'speaker' && isSpeakerNotes() ) { + slideNumberDisplay = 'block'; + } + } + + dom.slideNumber.style.display = slideNumberDisplay; + sync(); } @@ -921,13 +1270,8 @@ window.addEventListener( 'resize', onWindowResize, false ); if( config.touch ) { - dom.wrapper.addEventListener( 'touchstart', onTouchStart, false ); - dom.wrapper.addEventListener( 'touchmove', onTouchMove, false ); - dom.wrapper.addEventListener( 'touchend', onTouchEnd, false ); - - // Support pointer-style touch interaction as well - if( window.navigator.pointerEnabled ) { - // IE 11 uses un-prefixed version of pointer events + if( 'onpointerdown' in window ) { + // Use W3C pointer events dom.wrapper.addEventListener( 'pointerdown', onPointerDown, false ); dom.wrapper.addEventListener( 'pointermove', onPointerMove, false ); dom.wrapper.addEventListener( 'pointerup', onPointerUp, false ); @@ -938,6 +1282,12 @@ dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false ); dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false ); } + else { + // Fall back to touch events + dom.wrapper.addEventListener( 'touchstart', onTouchStart, false ); + dom.wrapper.addEventListener( 'touchmove', onTouchMove, false ); + dom.wrapper.addEventListener( 'touchend', onTouchEnd, false ); + } } if( config.keyboard ) { @@ -949,6 +1299,8 @@ dom.progress.addEventListener( 'click', onProgressClicked, false ); } + dom.resumeButton.addEventListener( 'click', resume, false ); + if( config.focusBodyOnPageVisibilityChange ) { var visibilityChange; @@ -973,7 +1325,7 @@ // Only support touch for Android, fixes double navigations in // stock browser - if( navigator.userAgent.match( /android/gi ) ) { + if( UA.match( /android/gi ) ) { pointerEvents = [ 'touchstart' ]; } @@ -1000,22 +1352,19 @@ window.removeEventListener( 'hashchange', onWindowHashChange, false ); window.removeEventListener( 'resize', onWindowResize, false ); + dom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false ); + dom.wrapper.removeEventListener( 'pointermove', onPointerMove, false ); + dom.wrapper.removeEventListener( 'pointerup', onPointerUp, false ); + + dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false ); + dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false ); + dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false ); + dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false ); dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false ); dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false ); - // IE11 - if( window.navigator.pointerEnabled ) { - dom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false ); - dom.wrapper.removeEventListener( 'pointermove', onPointerMove, false ); - dom.wrapper.removeEventListener( 'pointerup', onPointerUp, false ); - } - // IE10 - else if( window.navigator.msPointerEnabled ) { - dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false ); - dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false ); - dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false ); - } + dom.resumeButton.removeEventListener( 'click', resume, false ); if ( config.progress && dom.progress ) { dom.progress.removeEventListener( 'click', onProgressClicked, false ); @@ -1032,9 +1381,44 @@ } + /** + * Add a custom key binding with optional description to + * be added to the help screen. + */ + function addKeyBinding( binding, callback ) { + + if( typeof binding === 'object' && binding.keyCode ) { + registeredKeyBindings[binding.keyCode] = { + callback: callback, + key: binding.key, + description: binding.description + }; + } + else { + registeredKeyBindings[binding] = { + callback: callback, + key: null, + description: null + }; + } + + } + + /** + * Removes the specified custom key binding. + */ + function removeKeyBinding( keyCode ) { + + delete registeredKeyBindings[keyCode]; + + } + /** * Extend object a with the properties of object b. * If there's a conflict, object b takes precedence. + * + * @param {object} a + * @param {object} b */ function extend( a, b ) { @@ -1042,10 +1426,15 @@ a[ i ] = b[ i ]; } + return a; + } /** * Converts the target object to an array. + * + * @param {object} o + * @return {object[]} */ function toArray( o ) { @@ -1055,6 +1444,9 @@ /** * Utility for deserializing a value. + * + * @param {*} value + * @return {*} */ function deserialize( value ) { @@ -1062,7 +1454,7 @@ if( value === 'null' ) return null; else if( value === 'true' ) return true; else if( value === 'false' ) return false; - else if( value.match( /^\d+$/ ) ) return parseFloat( value ); + else if( value.match( /^-?[\d\.]+$/ ) ) return parseFloat( value ); } return value; @@ -1073,8 +1465,10 @@ * Measures the distance in pixels between point a * and point b. * - * @param {Object} a point with x/y properties - * @param {Object} b point with x/y properties + * @param {object} a point with x/y properties + * @param {object} b point with x/y properties + * + * @return {number} */ function distanceBetween( a, b ) { @@ -1087,6 +1481,9 @@ /** * Applies a CSS transform to the target element. + * + * @param {HTMLElement} element + * @param {string} transform */ function transformElement( element, transform ) { @@ -1101,6 +1498,8 @@ * Applies CSS transforms to the slides container. The container * is transformed from two separate sources: layout and the overview * mode. + * + * @param {object} transforms */ function transformSlides( transforms ) { @@ -1120,6 +1519,8 @@ /** * Injects the given CSS styles into the DOM. + * + * @param {string} value */ function injectStyleSheet( value ) { @@ -1135,14 +1536,56 @@ } + /** + * Find the closest parent that matches the given + * selector. + * + * @param {HTMLElement} target The child element + * @param {String} selector The CSS selector to match + * the parents against + * + * @return {HTMLElement} The matched parent or null + * if no matching parent was found + */ + function closestParent( target, selector ) { + + var parent = target.parentNode; + + while( parent ) { + + // There's some overhead doing this each time, we don't + // want to rewrite the element prototype but should still + // be enough to feature detect once at startup... + var matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector; + + // If we find a match, we're all set + if( matchesMethod && matchesMethod.call( parent, selector ) ) { + return parent; + } + + // Keep searching + parent = parent.parentNode; + + } + + return null; + + } + /** * Converts various color input formats to an {r:0,g:0,b:0} object. * - * @param {String} color The string representation of a color, - * the following formats are supported: - * - #000 - * - #000000 - * - rgb(0,0,0) + * @param {string} color The string representation of a color + * @example + * colorToRgb('#000'); + * @example + * colorToRgb('#000000'); + * @example + * colorToRgb('rgb(0,0,0)'); + * @example + * colorToRgb('rgba(0,0,0)'); + * + * @return {{r: number, g: number, b: number, [a]: number}|null} */ function colorToRgb( color ) { @@ -1192,7 +1635,8 @@ /** * Calculates brightness on a scale of 0-255. * - * @param color See colorStringToRgb for supported formats. + * @param {string} color See colorToRgb for supported formats. + * @see {@link colorToRgb} */ function colorBrightness( color ) { @@ -1206,46 +1650,14 @@ } - /** - * Retrieves the height of the given element by looking - * at the position and height of its immediate children. - */ - function getAbsoluteHeight( element ) { - - var height = 0; - - if( element ) { - var absoluteChildren = 0; - - toArray( element.childNodes ).forEach( function( child ) { - - if( typeof child.offsetTop === 'number' && child.style ) { - // Count # of abs children - if( window.getComputedStyle( child ).position === 'absolute' ) { - absoluteChildren += 1; - } - - height = Math.max( height, child.offsetTop + child.offsetHeight ); - } - - } ); - - // If there are no absolute children, use offsetHeight - if( absoluteChildren === 0 ) { - height = element.offsetHeight; - } - - } - - return height; - - } - /** * Returns the remaining height within the parent of the * target element. * * remaining height = [ configured parent height ] - [ current parent height ] + * + * @param {HTMLElement} element + * @param {number} [height] */ function getRemainingHeight( element, height ) { @@ -1278,6 +1690,15 @@ } + /** + * Check if this instance is being used to print a PDF with fragments. + */ + function isPrintingPDFFragments() { + + return ( /print-pdf-fragments/gi ).test( window.location.search ); + + } + /** * Hides the address bar if we're on a mobile device. */ @@ -1368,6 +1789,8 @@ /** * Bind preview frame links. + * + * @param {string} [selector=a] - selector for anchors */ function enablePreviewLinks( selector ) { @@ -1384,9 +1807,9 @@ /** * Unbind preview frame links. */ - function disablePreviewLinks() { + function disablePreviewLinks( selector ) { - var anchors = toArray( document.querySelectorAll( 'a' ) ); + var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) ); anchors.forEach( function( element ) { if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) { @@ -1398,6 +1821,8 @@ /** * Opens a preview window for the target URL. + * + * @param {string} url - url for preview iframe src */ function showPreview( url ) { @@ -1416,6 +1841,9 @@ '
    ', '
    ', '', + '', + 'Unable to load iframe. This is likely due to the site\'s policy (x-frame-options).', + '', '
    ' ].join(''); @@ -1439,7 +1867,29 @@ } /** - * Opens a overlay window with help material. + * Open or close help overlay window. + * + * @param {Boolean} [override] Flag which overrides the + * toggle logic and forcibly sets the desired state. True means + * help is open, false means it's closed. + */ + function toggleHelp( override ){ + + if( typeof override === 'boolean' ) { + override ? showHelp() : closeOverlay(); + } + else { + if( dom.overlay ) { + closeOverlay(); + } + else { + showHelp(); + } + } + } + + /** + * Opens an overlay window with help material. */ function showHelp() { @@ -1459,6 +1909,13 @@ html += '' + key + '' + keyboardShortcuts[ key ] + ''; } + // Add custom key bindings that have associated descriptions + for( var binding in registeredKeyBindings ) { + if( registeredKeyBindings[binding].key && registeredKeyBindings[binding].description ) { + html += '' + registeredKeyBindings[binding].key + '' + registeredKeyBindings[binding].description + ''; + } + } + html += ''; dom.overlay.innerHTML = [ @@ -1503,71 +1960,80 @@ if( dom.wrapper && !isPrintingPDF() ) { - var size = getComputedSlideSize(); + if( !config.disableLayout ) { - var slidePadding = 20; // TODO Dig this out of DOM + var size = getComputedSlideSize(); - // Layout the contents of the slides - layoutSlideContents( config.width, config.height, slidePadding ); + // Layout the contents of the slides + layoutSlideContents( config.width, config.height ); - dom.slides.style.width = size.width + 'px'; - dom.slides.style.height = size.height + 'px'; + dom.slides.style.width = size.width + 'px'; + dom.slides.style.height = size.height + 'px'; - // Determine scale of content to fit within available space - scale = Math.min( size.presentationWidth / size.width, size.presentationHeight / size.height ); + // Determine scale of content to fit within available space + scale = Math.min( size.presentationWidth / size.width, size.presentationHeight / size.height ); - // Respect max/min scale settings - scale = Math.max( scale, config.minScale ); - scale = Math.min( scale, config.maxScale ); + // Respect max/min scale settings + scale = Math.max( scale, config.minScale ); + scale = Math.min( scale, config.maxScale ); - // Don't apply any scaling styles if scale is 1 - if( scale === 1 ) { - dom.slides.style.zoom = ''; - dom.slides.style.left = ''; - dom.slides.style.top = ''; - dom.slides.style.bottom = ''; - dom.slides.style.right = ''; - transformSlides( { layout: '' } ); - } - else { - // Prefer zooming in desktop Chrome so that content remains crisp - if( !isMobileDevice && /chrome/i.test( navigator.userAgent ) && typeof dom.slides.style.zoom !== 'undefined' ) { - dom.slides.style.zoom = scale; + // Don't apply any scaling styles if scale is 1 + if( scale === 1 ) { + dom.slides.style.zoom = ''; + dom.slides.style.left = ''; + dom.slides.style.top = ''; + dom.slides.style.bottom = ''; + dom.slides.style.right = ''; transformSlides( { layout: '' } ); } - // Apply scale transform as a fallback else { - dom.slides.style.left = '50%'; - dom.slides.style.top = '50%'; - dom.slides.style.bottom = 'auto'; - dom.slides.style.right = 'auto'; - transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } ); + // Prefer zoom for scaling up so that content remains crisp. + // Don't use zoom to scale down since that can lead to shifts + // in text layout/line breaks. + if( scale > 1 && features.zoom ) { + dom.slides.style.zoom = scale; + dom.slides.style.left = ''; + dom.slides.style.top = ''; + dom.slides.style.bottom = ''; + dom.slides.style.right = ''; + transformSlides( { layout: '' } ); + } + // Apply scale transform as a fallback + else { + dom.slides.style.zoom = ''; + dom.slides.style.left = '50%'; + dom.slides.style.top = '50%'; + dom.slides.style.bottom = 'auto'; + dom.slides.style.right = 'auto'; + transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } ); + } } - } - // Select all slides, vertical and horizontal - var slides = toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ); + // Select all slides, vertical and horizontal + var slides = toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ); - for( var i = 0, len = slides.length; i < len; i++ ) { - var slide = slides[ i ]; + for( var i = 0, len = slides.length; i < len; i++ ) { + var slide = slides[ i ]; - // Don't bother updating invisible slides - if( slide.style.display === 'none' ) { - continue; - } + // Don't bother updating invisible slides + if( slide.style.display === 'none' ) { + continue; + } - if( config.center || slide.classList.contains( 'center' ) ) { - // Vertical stacks are not centred since their section - // children will be - if( slide.classList.contains( 'stack' ) ) { - slide.style.top = 0; + if( config.center || slide.classList.contains( 'center' ) ) { + // Vertical stacks are not centred since their section + // children will be + if( slide.classList.contains( 'stack' ) ) { + slide.style.top = 0; + } + else { + slide.style.top = Math.max( ( size.height - slide.scrollHeight ) / 2, 0 ) + 'px'; + } } else { - slide.style.top = Math.max( ( ( size.height - getAbsoluteHeight( slide ) ) / 2 ) - slidePadding, 0 ) + 'px'; + slide.style.top = ''; } - } - else { - slide.style.top = ''; + } } @@ -1575,6 +2041,10 @@ updateProgress(); updateParallax(); + if( isOverview() ) { + updateOverview(); + } + } } @@ -1582,8 +2052,11 @@ /** * Applies layout logic to the contents of all slides in * the presentation. + * + * @param {string|number} width + * @param {string|number} height */ - function layoutSlideContents( width, height, padding ) { + function layoutSlideContents( width, height ) { // Handle sizing of elements with the 'stretch' class toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) { @@ -1615,6 +2088,9 @@ * Calculates the computed pixel size of our slides. These * values are based on the width and height configuration * options. + * + * @param {number} [presentationWidth=dom.wrapper.offsetWidth] + * @param {number} [presentationHeight=dom.wrapper.offsetHeight] */ function getComputedSlideSize( presentationWidth, presentationHeight ) { @@ -1652,7 +2128,7 @@ * from the stack. * * @param {HTMLElement} stack The vertical stack element - * @param {int} v Index to memorize + * @param {string|number} [v=0] Index to memorize */ function setPreviousVerticalIndex( stack, v ) { @@ -1716,6 +2192,17 @@ } } ); + // Calculate slide sizes + var margin = 70; + var slideSize = getComputedSlideSize(); + overviewSlideWidth = slideSize.width + margin; + overviewSlideHeight = slideSize.height + margin; + + // Reverse in RTL mode + if( config.rtl ) { + overviewSlideWidth = -overviewSlideWidth; + } + updateSlidesVisibility(); layoutOverview(); updateOverview(); @@ -1739,19 +2226,10 @@ */ function layoutOverview() { - var margin = 70; - var slideWidth = config.width + margin, - slideHeight = config.height + margin; - - // Reverse in RTL mode - if( config.rtl ) { - slideWidth = -slideWidth; - } - // Layout slides toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { hslide.setAttribute( 'data-index-h', h ); - transformElement( hslide, 'translate3d(' + ( h * slideWidth ) + 'px, 0, 0)' ); + transformElement( hslide, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); if( hslide.classList.contains( 'stack' ) ) { @@ -1759,7 +2237,7 @@ vslide.setAttribute( 'data-index-h', h ); vslide.setAttribute( 'data-index-v', v ); - transformElement( vslide, 'translate3d(0, ' + ( v * slideHeight ) + 'px, 0)' ); + transformElement( vslide, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); } ); } @@ -1767,10 +2245,10 @@ // Layout slide backgrounds toArray( dom.background.childNodes ).forEach( function( hbackground, h ) { - transformElement( hbackground, 'translate3d(' + ( h * slideWidth ) + 'px, 0, 0)' ); + transformElement( hbackground, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); toArray( hbackground.querySelectorAll( '.slide-background' ) ).forEach( function( vbackground, v ) { - transformElement( vbackground, 'translate3d(0, ' + ( v * slideHeight ) + 'px, 0)' ); + transformElement( vbackground, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); } ); } ); @@ -1782,20 +2260,14 @@ */ function updateOverview() { - var margin = 70; - var slideWidth = config.width + margin, - slideHeight = config.height + margin; - - // Reverse in RTL mode - if( config.rtl ) { - slideWidth = -slideWidth; - } + var vmin = Math.min( window.innerWidth, window.innerHeight ); + var scale = Math.max( vmin / 5, 150 ) / vmin; transformSlides( { overview: [ - 'translateX('+ ( -indexh * slideWidth ) +'px)', - 'translateY('+ ( -indexv * slideHeight ) +'px)', - 'translateZ('+ ( window.innerWidth < 400 ? -1000 : -2500 ) +'px)' + 'scale('+ scale +')', + 'translateX('+ ( -indexh * overviewSlideWidth ) +'px)', + 'translateY('+ ( -indexv * overviewSlideHeight ) +'px)' ].join( ' ' ) } ); @@ -1860,7 +2332,7 @@ /** * Toggles the slide overview mode on and off. * - * @param {Boolean} override Optional flag which overrides the + * @param {Boolean} [override] Flag which overrides the * toggle logic and forcibly sets the desired state. True means * overview is open, false means it's closed. */ @@ -1887,12 +2359,48 @@ } + /** + * Return a hash URL that will resolve to the current slide location. + */ + function locationHash() { + + var url = '/'; + + // Attempt to create a named link based on the slide's ID + var id = currentSlide ? currentSlide.getAttribute( 'id' ) : null; + if( id ) { + id = encodeURIComponent( id ); + } + + var indexf; + if( config.fragmentInURL ) { + indexf = getIndices().f; + } + + // If the current slide has an ID, use that as a named link, + // but we don't support named links with a fragment index + if( typeof id === 'string' && id.length && indexf === undefined ) { + url = '/' + id; + } + // Otherwise use the /h/v index + else { + var hashIndexBase = config.hashOneBasedIndex ? 1 : 0; + if( indexh > 0 || indexv > 0 || indexf !== undefined ) url += indexh + hashIndexBase; + if( indexv > 0 || indexf !== undefined ) url += '/' + (indexv + hashIndexBase ); + if( indexf !== undefined ) url += '/' + indexf; + } + + return url; + + } + /** * Checks if the current or specified slide is vertical * (nested within another slide). * - * @param {HTMLElement} slide [optional] The slide to check + * @param {HTMLElement} [slide=currentSlide] The slide to check * orientation of + * @return {Boolean} */ function isVerticalSlide( slide ) { @@ -1911,10 +2419,10 @@ */ function enterFullscreen() { - var element = document.body; + var element = document.documentElement; // Check which implementation is available - var requestMethod = element.requestFullScreen || + var requestMethod = element.requestFullscreen || element.webkitRequestFullscreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || @@ -1977,6 +2485,8 @@ /** * Checks if we are currently in the paused mode. + * + * @return {Boolean} */ function isPaused() { @@ -1987,7 +2497,7 @@ /** * Toggles the auto slide mode on and off. * - * @param {Boolean} override Optional flag which sets the desired state. + * @param {Boolean} [override] Flag which sets the desired state. * True means autoplay starts, false means it stops. */ @@ -2005,6 +2515,8 @@ /** * Checks if the auto slide mode is currently on. + * + * @return {Boolean} */ function isAutoSliding() { @@ -2017,11 +2529,11 @@ * slide which matches the specified horizontal and vertical * indices. * - * @param {int} h Horizontal index of the target slide - * @param {int} v Vertical index of the target slide - * @param {int} f Optional index of a fragment within the + * @param {number} [h=indexh] Horizontal index of the target slide + * @param {number} [v=indexv] Vertical index of the target slide + * @param {number} [f] Index of a fragment within the * target slide to activate - * @param {int} o Optional origin for use in multimaster environments + * @param {number} [o] Origin for use in multimaster environments */ function slide( h, v, f, o ) { @@ -2031,6 +2543,9 @@ // Query all horizontal slides in the deck var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); + // Abort if there are no slides + if( horizontalSlides.length === 0 ) return; + // If no vertical index is specified and the upcoming slide is a // stack, resume at its previous vertical index if( v === undefined && !isOverview() ) { @@ -2103,16 +2618,7 @@ // Dispatch an event if the slide changed var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore ); - if( slideChanged ) { - dispatchEvent( 'slidechanged', { - 'indexh': indexh, - 'indexv': indexv, - 'previousSlide': previousSlide, - 'currentSlide': currentSlide, - 'origin': o - } ); - } - else { + if (!slideChanged) { // Ensure that the previous slide is never the same as the current previousSlide = null; } @@ -2120,7 +2626,7 @@ // Solves an edge case where the previous slide maintains the // 'present' class when navigating between adjacent vertical // stacks - if( previousSlide ) { + if( previousSlide && previousSlide !== currentSlide ) { previousSlide.classList.remove( 'present' ); previousSlide.setAttribute( 'aria-hidden', 'true' ); @@ -2140,6 +2646,16 @@ } } + if( slideChanged ) { + dispatchEvent( 'slidechanged', { + 'indexh': indexh, + 'indexv': indexv, + 'previousSlide': previousSlide, + 'currentSlide': currentSlide, + 'origin': o + } ); + } + // Handle embedded content if( slideChanged || !previousSlide ) { stopEmbeddedContent( previousSlide ); @@ -2147,13 +2663,14 @@ } // Announce the current slide contents, for screen readers - dom.statusDiv.textContent = currentSlide.textContent; + dom.statusDiv.textContent = getStatusText( currentSlide ); updateControls(); updateProgress(); updateBackground(); updateParallax(); updateSlideNumber(); + updateNotes(); // Update the URL hash writeURL(); @@ -2192,12 +2709,21 @@ updateControls(); updateProgress(); - updateBackground( true ); updateSlideNumber(); updateSlidesVisibility(); + updateBackground( true ); + updateNotesVisibility(); + updateNotes(); formatEmbeddedContent(); - startEmbeddedContent( currentSlide ); + + // Start or stop embedded content depending on global config + if( config.autoPlayMedia === false ) { + stopEmbeddedContent( currentSlide, { unloadIframes: false } ); + } + else { + startEmbeddedContent( currentSlide ); + } if( isOverview() ) { layoutOverview(); @@ -2205,6 +2731,41 @@ } + /** + * Updates reveal.js to keep in sync with new slide attributes. For + * example, if you add a new `data-background-image` you can call + * this to have reveal.js render the new background image. + * + * Similar to #sync() but more efficient when you only need to + * refresh a specific slide. + * + * @param {HTMLElement} slide + */ + function syncSlide( slide ) { + + syncBackground( slide ); + syncFragments( slide ); + + updateBackground(); + updateNotes(); + + loadSlide( slide ); + + } + + /** + * Formats the fragments on the given slide so that they have + * valid indices. Call this if fragments are changed in the DOM + * after reveal.js has already initialized. + * + * @param {HTMLElement} slide + */ + function syncFragments( slide ) { + + sortFragments( slide.querySelectorAll( '.fragment' ) ); + + } + /** * Resets all vertical slides so that only the first * is visible. @@ -2252,16 +2813,33 @@ } + /** + * Randomly shuffles all slides in the deck. + */ + function shuffle() { + + var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + slides.forEach( function( slide ) { + + // Insert this slide next to another random slide. This may + // cause the slide to insert before itself but that's fine. + dom.slides.insertBefore( slide, slides[ Math.floor( Math.random() * slides.length ) ] ); + + } ); + + } + /** * Updates one dimension of slides by showing the slide * with the specified index. * - * @param {String} selector A CSS selector that will fetch + * @param {string} selector A CSS selector that will fetch * the group of slides we are working with - * @param {Number} index The index of the slide that should be + * @param {number} index The index of the slide that should be * shown * - * @return {Number} The index of the slide that is now shown, + * @return {number} The index of the slide that is now shown, * might differ from the passed in index if it was out of * bounds. */ @@ -2413,10 +2991,10 @@ // Show the horizontal slide if it's within the view distance if( distanceX < viewDistance ) { - showSlide( horizontalSlide ); + loadSlide( horizontalSlide ); } else { - hideSlide( horizontalSlide ); + unloadSlide( horizontalSlide ); } if( verticalSlidesLength ) { @@ -2428,18 +3006,77 @@ distanceY = x === ( indexh || 0 ) ? Math.abs( ( indexv || 0 ) - y ) : Math.abs( y - oy ); - if( distanceX + distanceY < viewDistance ) { - showSlide( verticalSlide ); - } - else { - hideSlide( verticalSlide ); - } - } + if( distanceX + distanceY < viewDistance ) { + loadSlide( verticalSlide ); + } + else { + unloadSlide( verticalSlide ); + } + } + + } + } + + // Flag if there are ANY vertical slides, anywhere in the deck + if( dom.wrapper.querySelectorAll( '.slides>section>section' ).length ) { + dom.wrapper.classList.add( 'has-vertical-slides' ); + } + else { + dom.wrapper.classList.remove( 'has-vertical-slides' ); + } + + // Flag if there are ANY horizontal slides, anywhere in the deck + if( dom.wrapper.querySelectorAll( '.slides>section' ).length > 1 ) { + dom.wrapper.classList.add( 'has-horizontal-slides' ); + } + else { + dom.wrapper.classList.remove( 'has-horizontal-slides' ); + } + + } + + } + + /** + * Pick up notes from the current slide and display them + * to the viewer. + * + * @see {@link config.showNotes} + */ + function updateNotes() { + + if( config.showNotes && dom.speakerNotes && currentSlide && !isPrintingPDF() ) { + + dom.speakerNotes.innerHTML = getSlideNotes() || 'No notes on this slide.'; + + } + + } + + /** + * Updates the visibility of the speaker notes sidebar that + * is used to share annotated slides. The notes sidebar is + * only visible if showNotes is true and there are notes on + * one or more slides in the deck. + */ + function updateNotesVisibility() { + + if( config.showNotes && hasNotes() ) { + dom.wrapper.classList.add( 'show-notes' ); + } + else { + dom.wrapper.classList.remove( 'show-notes' ); + } + + } - } - } + /** + * Checks if there are speaker notes for ANY slide in the + * presentation. + */ + function hasNotes() { - } + return dom.slides.querySelectorAll( '[data-notes], aside.notes' ).length > 0; } @@ -2457,33 +3094,79 @@ } + /** * Updates the slide number div to reflect the current slide. * - * Slide number format can be defined as a string using the - * following variables: - * h: current slide's horizontal index - * v: current slide's vertical index - * c: current slide index (flattened) - * t: total number of slides (flattened) + * The following slide number formats are available: + * "h.v": horizontal . vertical slide number (default) + * "h/v": horizontal / vertical slide number + * "c": flattened slide number + * "c/t": flattened slide number / total slides */ function updateSlideNumber() { // Update slide number if enabled - if( config.slideNumber && dom.slideNumber) { + if( config.slideNumber && dom.slideNumber ) { - // Default to only showing the current slide number - var format = 'c'; + var value = []; + var format = 'h.v'; - // Check if a custom slide number format is available + // Check if a custom number format is available if( typeof config.slideNumber === 'string' ) { format = config.slideNumber; } - dom.slideNumber.innerHTML = format.replace( /h/g, indexh ) - .replace( /v/g, indexv ) - .replace( /c/g, getSlidePastCount() + 1 ) - .replace( /t/g, getTotalSlides() ); + // If there are ONLY vertical slides in this deck, always use + // a flattened slide number + if( !/c/.test( format ) && dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length === 1 ) { + format = 'c'; + } + + switch( format ) { + case 'c': + value.push( getSlidePastCount() + 1 ); + break; + case 'c/t': + value.push( getSlidePastCount() + 1, '/', getTotalSlides() ); + break; + case 'h/v': + value.push( indexh + 1 ); + if( isVerticalSlide() ) value.push( '/', indexv + 1 ); + break; + default: + value.push( indexh + 1 ); + if( isVerticalSlide() ) value.push( '.', indexv + 1 ); + } + + dom.slideNumber.innerHTML = formatSlideNumber( value[0], value[1], value[2] ); + } + + } + + /** + * Applies HTML formatting to a slide number before it's + * written to the DOM. + * + * @param {number} a Current slide + * @param {string} delimiter Character to separate slide numbers + * @param {(number|*)} b Total slides + * @return {string} HTML string fragment + */ + function formatSlideNumber( a, delimiter, b ) { + + var url = '#' + locationHash(); + if( typeof b === 'number' && !isNaN( b ) ) { + return '
    ' + + ''+ a +'' + + ''+ delimiter +'' + + ''+ b +'' + + ''; + } + else { + return '' + + ''+ a +'' + + ''; } } @@ -2504,34 +3187,57 @@ .concat( dom.controlsNext ).forEach( function( node ) { node.classList.remove( 'enabled' ); node.classList.remove( 'fragmented' ); + + // Set 'disabled' attribute on all directions + node.setAttribute( 'disabled', 'disabled' ); } ); - // Add the 'enabled' class to the available routes - if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); } ); + // Add the 'enabled' class to the available routes; remove 'disabled' attribute to enable buttons + if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Prev/next buttons - if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); } ); + if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Highlight fragment directions if( currentSlide ) { // Always apply fragment decorator to prev/next buttons - if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Apply fragment decorators to directional buttons based on // what slide axis they are in if( isVerticalSlide( currentSlide ) ) { - if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + } + else { + if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + } + + } + + if( config.controlsTutorial ) { + + // Highlight control arrows with an animation to ensure + // that the viewer knows how to navigate + if( !hasNavigatedDown && routes.down ) { + dom.controlsDownArrow.classList.add( 'highlight' ); } else { - if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + dom.controlsDownArrow.classList.remove( 'highlight' ); + + if( !hasNavigatedRight && routes.right && indexv === 0 ) { + dom.controlsRightArrow.classList.add( 'highlight' ); + } + else { + dom.controlsRightArrow.classList.remove( 'highlight' ); + } } } @@ -2542,7 +3248,7 @@ * Updates the background elements to reflect the current * slide. * - * @param {Boolean} includeAll If true, the backgrounds of + * @param {boolean} includeAll If true, the backgrounds of * all vertical slides (not just the present) will be updated. */ function updateBackground( includeAll ) { @@ -2599,30 +3305,30 @@ } ); - // Stop any currently playing video background + // Stop content inside of previous backgrounds if( previousBackground ) { - var previousVideo = previousBackground.querySelector( 'video' ); - if( previousVideo ) previousVideo.pause(); + stopEmbeddedContent( previousBackground ); } + // Start content in the current background if( currentBackground ) { - // Start video playback - var currentVideo = currentBackground.querySelector( 'video' ); - if( currentVideo ) { - currentVideo.currentTime = 0; - currentVideo.play(); - } + startEmbeddedContent( currentBackground ); + + var currentBackgroundContent = currentBackground.querySelector( '.slide-background-content' ); + if( currentBackgroundContent ) { - var backgroundImageURL = currentBackground.style.backgroundImage || ''; + var backgroundImageURL = currentBackgroundContent.style.backgroundImage || ''; + + // Restart GIFs (doesn't work in Firefox) + if( /\.gif/i.test( backgroundImageURL ) ) { + currentBackgroundContent.style.backgroundImage = ''; + window.getComputedStyle( currentBackgroundContent ).opacity; + currentBackgroundContent.style.backgroundImage = backgroundImageURL; + } - // Restart GIFs (doesn't work in Firefox) - if( /\.gif/i.test( backgroundImageURL ) ) { - currentBackground.style.backgroundImage = ''; - window.getComputedStyle( currentBackground ).opacity; - currentBackground.style.backgroundImage = backgroundImageURL; } // Don't transition between identical backgrounds. This @@ -2688,7 +3394,7 @@ horizontalOffsetMultiplier = config.parallaxBackgroundHorizontal; } else { - horizontalOffsetMultiplier = ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ); + horizontalOffsetMultiplier = horizontalSlideCount > 1 ? ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) : 0; } horizontalOffset = horizontalOffsetMultiplier * indexh * -1; @@ -2705,7 +3411,7 @@ verticalOffsetMultiplier = ( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ); } - verticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indexv * 1 : 0; + verticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indexv : 0; dom.background.style.backgroundPosition = horizontalOffset + 'px ' + -verticalOffset + 'px'; @@ -2717,15 +3423,20 @@ * Called when the given slide is within the configured view * distance. Shows the slide element and loads any content * that is set to load lazily (data-src). + * + * @param {HTMLElement} slide Slide to show */ - function showSlide( slide ) { + function loadSlide( slide, options ) { + + options = options || {}; // Show the slide element - slide.style.display = 'block'; + slide.style.display = config.display; // Media elements with data-src attributes toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) { element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); + element.setAttribute( 'data-lazy-loaded', '' ); element.removeAttribute( 'data-src' ); } ); @@ -2736,6 +3447,7 @@ toArray( media.querySelectorAll( 'source[data-src]' ) ).forEach( function( source ) { source.setAttribute( 'src', source.getAttribute( 'data-src' ) ); source.removeAttribute( 'data-src' ); + source.setAttribute( 'data-lazy-loaded', '' ); sources += 1; } ); @@ -2748,11 +3460,12 @@ // Show the corresponding background element - var indices = getIndices( slide ); - var background = getSlideBackground( indices.h, indices.v ); + var background = slide.slideBackgroundElement; if( background ) { background.style.display = 'block'; + var backgroundContent = slide.slideBackgroundContentElement; + // If the background contains media, load it if( background.hasAttribute( 'data-loaded' ) === false ) { background.setAttribute( 'data-loaded', 'true' ); @@ -2760,11 +3473,12 @@ var backgroundImage = slide.getAttribute( 'data-background-image' ), backgroundVideo = slide.getAttribute( 'data-background-video' ), backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ), + backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ), backgroundIframe = slide.getAttribute( 'data-background-iframe' ); // Images if( backgroundImage ) { - background.style.backgroundImage = 'url('+ backgroundImage +')'; + backgroundContent.style.backgroundImage = 'url('+ encodeURI( backgroundImage ) +')'; } // Videos else if ( backgroundVideo && !isSpeakerNotes() ) { @@ -2774,51 +3488,90 @@ video.setAttribute( 'loop', '' ); } + if( backgroundVideoMuted ) { + video.muted = true; + } + + // Inline video playback works (at least in Mobile Safari) as + // long as the video is muted and the `playsinline` attribute is + // present + if( isMobileDevice ) { + video.muted = true; + video.autoplay = true; + video.setAttribute( 'playsinline', '' ); + } + // Support comma separated lists of video sources backgroundVideo.split( ',' ).forEach( function( source ) { video.innerHTML += ''; } ); - background.appendChild( video ); + backgroundContent.appendChild( video ); } // Iframes - else if( backgroundIframe ) { + else if( backgroundIframe && options.excludeIframes !== true ) { var iframe = document.createElement( 'iframe' ); + iframe.setAttribute( 'allowfullscreen', '' ); + iframe.setAttribute( 'mozallowfullscreen', '' ); + iframe.setAttribute( 'webkitallowfullscreen', '' ); + + // Only load autoplaying content when the slide is shown to + // avoid having it play in the background + if( /autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) { + iframe.setAttribute( 'data-src', backgroundIframe ); + } + else { iframe.setAttribute( 'src', backgroundIframe ); - iframe.style.width = '100%'; - iframe.style.height = '100%'; - iframe.style.maxHeight = '100%'; - iframe.style.maxWidth = '100%'; + } - background.appendChild( iframe ); + iframe.style.width = '100%'; + iframe.style.height = '100%'; + iframe.style.maxHeight = '100%'; + iframe.style.maxWidth = '100%'; + + backgroundContent.appendChild( iframe ); } } + } } /** - * Called when the given slide is moved outside of the - * configured view distance. + * Unloads and hides the given slide. This is called when the + * slide is moved outside of the configured view distance. + * + * @param {HTMLElement} slide */ - function hideSlide( slide ) { + function unloadSlide( slide ) { // Hide the slide element slide.style.display = 'none'; // Hide the corresponding background element - var indices = getIndices( slide ); - var background = getSlideBackground( indices.h, indices.v ); + var background = getSlideBackground( slide ); if( background ) { background.style.display = 'none'; } + // Reset lazy-loaded media elements with src attributes + toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src]' ) ).forEach( function( element ) { + element.setAttribute( 'data-src', element.getAttribute( 'src' ) ); + element.removeAttribute( 'src' ); + } ); + + // Reset lazy-loaded media elements with children + toArray( slide.querySelectorAll( 'video[data-lazy-loaded] source[src], audio source[src]' ) ).forEach( function( source ) { + source.setAttribute( 'data-src', source.getAttribute( 'src' ) ); + source.removeAttribute( 'src' ); + } ); + } /** * Determine what available routes there are for navigation. * - * @return {Object} containing four booleans: left/right/up/down + * @return {{left: boolean, right: boolean, up: boolean, down: boolean}} */ function availableRoutes() { @@ -2826,13 +3579,27 @@ verticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); var routes = { - left: indexh > 0 || config.loop, - right: indexh < horizontalSlides.length - 1 || config.loop, + left: indexh > 0, + right: indexh < horizontalSlides.length - 1, up: indexv > 0, down: indexv < verticalSlides.length - 1 }; - // reverse horizontal controls for rtl + // Looped presentations can always be navigated as long as + // there are slides available + if( config.loop ) { + if( horizontalSlides.length > 1 ) { + routes.left = true; + routes.right = true; + } + + if( verticalSlides.length > 1 ) { + routes.up = true; + routes.down = true; + } + } + + // Reverse horizontal controls for rtl if( config.rtl ) { var left = routes.left; routes.left = routes.right; @@ -2847,7 +3614,7 @@ * Returns an object describing the available fragment * directions. * - * @return {Object} two boolean properties: prev/next + * @return {{prev: boolean, next: boolean}} */ function availableFragments() { @@ -2888,65 +3655,154 @@ _appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' ); _appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' ); + // Always show media controls on mobile devices + if( isMobileDevice ) { + toArray( dom.slides.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + el.controls = true; + } ); + } + } /** * Start playback of any embedded content inside of - * the targeted slide. + * the given element. + * + * @param {HTMLElement} element */ - function startEmbeddedContent( slide ) { + function startEmbeddedContent( element ) { + + if( element && !isSpeakerNotes() ) { - if( slide && !isSpeakerNotes() ) { // Restart GIFs - toArray( slide.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) { // Setting the same unchanged source like this was confirmed // to work in Chrome, FF & Safari el.setAttribute( 'src', el.getAttribute( 'src' ) ); } ); // HTML5 media elements - toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { - if( el.hasAttribute( 'data-autoplay' ) && typeof el.play === 'function' ) { - el.play(); + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + + // Prefer an explicit global autoplay setting + var autoplay = config.autoPlayMedia; + + // If no global setting is available, fall back on the element's + // own autoplay setting + if( typeof autoplay !== 'boolean' ) { + autoplay = el.hasAttribute( 'data-autoplay' ) || !!closestParent( el, '.slide-background' ); + } + + if( autoplay && typeof el.play === 'function' ) { + + // If the media is ready, start playback + if( el.readyState > 1 ) { + startEmbeddedMedia( { target: el } ); + } + // Mobile devices never fire a loaded event so instead + // of waiting, we initiate playback + else if( isMobileDevice ) { + el.play(); + } + // If the media isn't loaded, wait before playing + else { + el.removeEventListener( 'loadeddata', startEmbeddedMedia ); // remove first to avoid dupes + el.addEventListener( 'loadeddata', startEmbeddedMedia ); + } + } } ); // Normal iframes - toArray( slide.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + startEmbeddedIframe( { target: el } ); } ); // Lazy loading iframes - toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + if( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) { el.removeEventListener( 'load', startEmbeddedIframe ); // remove first to avoid dupes el.addEventListener( 'load', startEmbeddedIframe ); el.setAttribute( 'src', el.getAttribute( 'data-src' ) ); } } ); + + } + + } + + /** + * Starts playing an embedded video/audio element after + * it has finished loading. + * + * @param {object} event + */ + function startEmbeddedMedia( event ) { + + var isAttachedToDOM = !!closestParent( event.target, 'html' ), + isVisible = !!closestParent( event.target, '.present' ); + + if( isAttachedToDOM && isVisible ) { + event.target.currentTime = 0; + event.target.play(); } + event.target.removeEventListener( 'loadeddata', startEmbeddedMedia ); + } /** * "Starts" the content of an embedded iframe using the - * postmessage API. + * postMessage API. + * + * @param {object} event */ function startEmbeddedIframe( event ) { var iframe = event.target; - // YouTube postMessage API - if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && iframe.hasAttribute( 'data-autoplay' ) ) { - iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' ); - } - // Vimeo postMessage API - else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && iframe.hasAttribute( 'data-autoplay' ) ) { - iframe.contentWindow.postMessage( '{"method":"play"}', '*' ); - } - // Generic postMessage API - else { - iframe.contentWindow.postMessage( 'slide:start', '*' ); + if( iframe && iframe.contentWindow ) { + + var isAttachedToDOM = !!closestParent( event.target, 'html' ), + isVisible = !!closestParent( event.target, '.present' ); + + if( isAttachedToDOM && isVisible ) { + + // Prefer an explicit global autoplay setting + var autoplay = config.autoPlayMedia; + + // If no global setting is available, fall back on the element's + // own autoplay setting + if( typeof autoplay !== 'boolean' ) { + autoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closestParent( iframe, '.slide-background' ); + } + + // YouTube postMessage API + if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) { + iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' ); + } + // Vimeo postMessage API + else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) { + iframe.contentWindow.postMessage( '{"method":"play"}', '*' ); + } + // Generic postMessage API + else { + iframe.contentWindow.postMessage( 'slide:start', '*' ); + } + + } + } } @@ -2954,44 +3810,54 @@ /** * Stop playback of any embedded content inside of * the targeted slide. + * + * @param {HTMLElement} element */ - function stopEmbeddedContent( slide ) { + function stopEmbeddedContent( element, options ) { + + options = extend( { + // Defaults + unloadIframes: true + }, options || {} ); - if( slide && slide.parentNode ) { + if( element && element.parentNode ) { // HTML5 media elements - toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { if( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) { + el.setAttribute('data-paused-by-reveal', ''); el.pause(); } } ); // Generic postMessage API for non-lazy loaded iframes - toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) { - el.contentWindow.postMessage( 'slide:stop', '*' ); + toArray( element.querySelectorAll( 'iframe' ) ).forEach( function( el ) { + if( el.contentWindow ) el.contentWindow.postMessage( 'slide:stop', '*' ); el.removeEventListener( 'load', startEmbeddedIframe ); }); // YouTube postMessage API - toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) { - if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) { + toArray( element.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) { el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' ); } }); // Vimeo postMessage API - toArray( slide.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) { - if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) { + toArray( element.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) { el.contentWindow.postMessage( '{"method":"pause"}', '*' ); } }); - // Lazy loading iframes - toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { - // Only removing the src doesn't actually unload the frame - // in all browsers (Firefox) so we set it to blank first - el.setAttribute( 'src', 'about:blank' ); - el.removeAttribute( 'src' ); - } ); + if( options.unloadIframes === true ) { + // Unload lazy-loaded iframes + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + // Only removing the src doesn't actually unload the frame + // in all browsers (Firefox) so we set it to blank first + el.setAttribute( 'src', 'about:blank' ); + el.removeAttribute( 'src' ); + } ); + } } } @@ -2999,6 +3865,8 @@ /** * Returns the number of past slides. This can be used as a global * flattened index for slides. + * + * @return {number} Past slide count */ function getSlidePastCount() { @@ -3043,6 +3911,8 @@ /** * Returns a value ranging from 0-1 that represents * how far into the presentation we have navigated. + * + * @return {number} */ function getProgress() { @@ -3076,6 +3946,8 @@ /** * Checks if this presentation is running inside of the * speaker notes window. + * + * @return {boolean} */ function isSpeakerNotes() { @@ -3100,12 +3972,15 @@ var element; // Ensure the named link is a valid HTML ID attribute - if( /^[a-zA-Z][\w:.-]*$/.test( name ) ) { - // Find the slide with the specified ID - element = document.getElementById( name ); + try { + element = document.getElementById( decodeURIComponent( name ) ); } + catch ( error ) { } + + // Ensure that we're not already on a slide with the same name + var isSameNameAsCurrentSlide = currentSlide ? currentSlide.getAttribute( 'id' ) === name : false; - if( element ) { + if( element && !isSameNameAsCurrentSlide ) { // Find the position of the named slide and navigate to it var indices = Reveal.getIndices( element ); slide( indices.h, indices.v ); @@ -3116,12 +3991,22 @@ } } else { + var hashIndexBase = config.hashOneBasedIndex ? 1 : 0; + // Read the index components of the hash - var h = parseInt( bits[0], 10 ) || 0, - v = parseInt( bits[1], 10 ) || 0; + var h = ( parseInt( bits[0], 10 ) - hashIndexBase ) || 0, + v = ( parseInt( bits[1], 10 ) - hashIndexBase ) || 0, + f; + + if( config.fragmentInURL ) { + f = parseInt( bits[2], 10 ); + if( isNaN( f ) ) { + f = undefined; + } + } - if( h !== indexh || v !== indexv ) { - slide( h, v ); + if( h !== indexh || v !== indexv || f !== undefined ) { + slide( h, v, f ); } } @@ -3131,7 +4016,7 @@ * Updates the page URL (hash) to reflect the current * state. * - * @param {Number} delay The time in ms to wait before + * @param {number} delay The time in ms to wait before * writing the hash */ function writeURL( delay ) { @@ -3146,40 +4031,20 @@ writeURLTimeout = setTimeout( writeURL, delay ); } else if( currentSlide ) { - var url = '/'; - - // Attempt to create a named link based on the slide's ID - var id = currentSlide.getAttribute( 'id' ); - if( id ) { - id = id.toLowerCase(); - id = id.replace( /[^a-zA-Z0-9\-\_\:\.]/g, '' ); - } - - // If the current slide has an ID, use that as a named link - if( typeof id === 'string' && id.length ) { - url = '/' + id; - } - // Otherwise use the /h/v index - else { - if( indexh > 0 || indexv > 0 ) url += indexh; - if( indexv > 0 ) url += '/' + indexv; - } - - window.location.hash = url; + window.location.hash = locationHash(); } } } - /** - * Retrieves the h/v location of the current, or specified, - * slide. + * Retrieves the h/v location and fragment of the current, + * or specified, slide. * - * @param {HTMLElement} slide If specified, the returned + * @param {HTMLElement} [slide] If specified, the returned * index will be for this slide rather than the currently * active one * - * @return {Object} { h: , v: , f: } + * @return {{h: number, v: number, f: number}} */ function getIndices( slide ) { @@ -3225,17 +4090,30 @@ } + /** + * Retrieves all slides in this presentation. + */ + function getSlides() { + + return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' )); + + } + /** * Retrieves the total number of slides in this presentation. + * + * @return {number} */ function getTotalSlides() { - return dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length; + return getSlides().length; } /** * Returns the slide element matching the specified index. + * + * @return {HTMLElement} */ function getSlide( x, y ) { @@ -3255,31 +4133,49 @@ * All slides, even the ones with no background properties * defined, have a background element so as long as the * index is valid an element will be returned. + * + * @param {mixed} x Horizontal background index OR a slide + * HTML element + * @param {number} y Vertical background index + * @return {(HTMLElement[]|*)} */ function getSlideBackground( x, y ) { - // When printing to PDF the slide backgrounds are nested - // inside of the slides - if( isPrintingPDF() ) { - var slide = getSlide( x, y ); - if( slide ) { - var background = slide.querySelector( '.slide-background' ); - if( background && background.parentNode === slide ) { - return background; - } - } - - return undefined; + var slide = typeof x === 'number' ? getSlide( x, y ) : x; + if( slide ) { + return slide.slideBackgroundElement; } - var horizontalBackground = dom.wrapper.querySelectorAll( '.backgrounds>.slide-background' )[ x ]; - var verticalBackgrounds = horizontalBackground && horizontalBackground.querySelectorAll( '.slide-background' ); + return undefined; + + } + + /** + * Retrieves the speaker notes from a slide. Notes can be + * defined in two ways: + * 1. As a data-notes attribute on the slide
    + * 2. As an
    + +
    + +
    +

    diff --git a/plugin/markdown/example.md b/plugin/markdown/example.md index 6f6f577..89c7534 100644 --- a/plugin/markdown/example.md +++ b/plugin/markdown/example.md @@ -29,3 +29,8 @@ Content 3.1 ## External 3.2 Content 3.2 + + +## External 3.3 + +![External Image](https://s3.amazonaws.com/static.slid.es/logo/v2/slides-symbol-512x512.png) diff --git a/plugin/markdown/markdown.js b/plugin/markdown/markdown.js index 15e3b40..aa08ee5 100644 --- a/plugin/markdown/markdown.js +++ b/plugin/markdown/markdown.js @@ -4,33 +4,26 @@ * of external markdown documents. */ (function( root, factory ) { - if( typeof exports === 'object' ) { + if (typeof define === 'function' && define.amd) { + root.marked = require( './marked' ); + root.RevealMarkdown = factory( root.marked ); + root.RevealMarkdown.initialize(); + } else if( typeof exports === 'object' ) { module.exports = factory( require( './marked' ) ); - } - else { + } else { // Browser globals (root is window) root.RevealMarkdown = factory( root.marked ); root.RevealMarkdown.initialize(); } }( this, function( marked ) { - if( typeof marked === 'undefined' ) { - throw 'The reveal.js Markdown plugin requires marked to be loaded'; - } - - if( typeof hljs !== 'undefined' ) { - marked.setOptions({ - highlight: function( lang, code ) { - return hljs.highlightAuto( lang, code ).value; - } - }); - } - var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$', - DEFAULT_NOTES_SEPARATOR = 'note:', + DEFAULT_NOTES_SEPARATOR = 'notes?:', DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$', DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$'; + var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__'; + /** * Retrieves the markdown contents of a slide section @@ -38,11 +31,15 @@ */ function getMarkdownFromSlide( section ) { - var template = section.querySelector( 'script' ); + // look for a ' ); + var leadingWs = text.match( /^\n?(\s*)/ )[1].length, leadingTabs = text.match( /^\n?(\t*)/ )[1].length; @@ -112,9 +109,13 @@ var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) ); if( notesMatch.length === 2 ) { - content = notesMatch[0] + ''; + content = notesMatch[0] + ''; } + // prevent script end tags in the content from interfering + // with parsing + content = content.replace( /<\/script>/g, SCRIPT_END_PLACEHOLDER ); + return ''; } @@ -177,7 +178,7 @@ markdownSections += '
    '; sectionStack[i].forEach( function( child ) { - markdownSections += '
    ' + createMarkdownSlide( child, options ) + '
    '; + markdownSections += '
    ' + createMarkdownSlide( child, options ) + '
    '; } ); markdownSections += '
    '; @@ -379,6 +380,24 @@ return { initialize: function() { + if( typeof marked === 'undefined' ) { + throw 'The reveal.js Markdown plugin requires marked to be loaded'; + } + + if( typeof hljs !== 'undefined' ) { + marked.setOptions({ + highlight: function( code, lang ) { + return hljs.highlightAuto( code, [lang] ).value; + } + }); + } + + var options = Reveal.getConfig().markdown; + + if ( options ) { + marked.setOptions( options ); + } + processSlides(); convertSlides(); }, diff --git a/plugin/markdown/marked.js b/plugin/markdown/marked.js index 70af29b..555c1dc 100644 --- a/plugin/markdown/marked.js +++ b/plugin/markdown/marked.js @@ -3,4 +3,4 @@ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) * https://github.com/chjj/marked */ -(function(){function e(e){this.tokens=[],this.tokens.links={},this.options=e||a.defaults,this.rules=p.normal,this.options.gfm&&(this.rules=this.options.tables?p.tables:p.gfm)}function t(e,t){if(this.options=t||a.defaults,this.links=e,this.rules=u.normal,this.renderer=this.options.renderer||new n,this.renderer.options=this.options,!this.links)throw new Error("Tokens array requires a `links` property.");this.options.gfm?this.rules=this.options.breaks?u.breaks:u.gfm:this.options.pedantic&&(this.rules=u.pedantic)}function n(e){this.options=e||{}}function r(e){this.tokens=[],this.token=null,this.options=e||a.defaults,this.options.renderer=this.options.renderer||new n,this.renderer=this.options.renderer,this.renderer.options=this.options}function s(e,t){return e.replace(t?/&/g:/&(?!#?\w+;)/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function i(e){return e.replace(/&([#\w]+);/g,function(e,t){return t=t.toLowerCase(),"colon"===t?":":"#"===t.charAt(0)?String.fromCharCode("x"===t.charAt(1)?parseInt(t.substring(2),16):+t.substring(1)):""})}function l(e,t){return e=e.source,t=t||"",function n(r,s){return r?(s=s.source||s,s=s.replace(/(^|[^\[])\^/g,"$1"),e=e.replace(r,s),n):new RegExp(e,t)}}function o(){}function h(e){for(var t,n,r=1;rAn error occured:

    "+s(c.message+"",!0)+"
    ";throw c}}var p={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:o,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:o,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:o,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};p.bullet=/(?:[*+-]|\d+\.)/,p.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/,p.item=l(p.item,"gm")(/bull/g,p.bullet)(),p.list=l(p.list)(/bull/g,p.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+p.def.source+")")(),p.blockquote=l(p.blockquote)("def",p.def)(),p._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+(?!:/|[^\\w\\s@]*@)\\b",p.html=l(p.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,p._tag)(),p.paragraph=l(p.paragraph)("hr",p.hr)("heading",p.heading)("lheading",p.lheading)("blockquote",p.blockquote)("tag","<"+p._tag)("def",p.def)(),p.normal=h({},p),p.gfm=h({},p.normal,{fences:/^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,paragraph:/^/}),p.gfm.paragraph=l(p.paragraph)("(?!","(?!"+p.gfm.fences.source.replace("\\1","\\2")+"|"+p.list.source.replace("\\1","\\3")+"|")(),p.tables=h({},p.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/}),e.rules=p,e.lex=function(t,n){var r=new e(n);return r.lex(t)},e.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},e.prototype.token=function(e,t,n){for(var r,s,i,l,o,h,a,u,c,e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),i[0].length>1&&this.tokens.push({type:"space"})),i=this.rules.code.exec(e))e=e.substring(i[0].length),i=i[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?i:i.replace(/\n+$/,"")});else if(i=this.rules.fences.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"code",lang:i[2],text:i[3]});else if(i=this.rules.heading.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"heading",depth:i[1].length,text:i[2]});else if(t&&(i=this.rules.nptable.exec(e))){for(e=e.substring(i[0].length),h={type:"table",header:i[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:i[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:i[3].replace(/\n$/,"").split("\n")},u=0;u ?/gm,""),this.token(i,t,!0),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l=i[2],this.tokens.push({type:"list_start",ordered:l.length>1}),i=i[0].match(this.rules.item),r=!1,c=i.length,u=0;c>u;u++)h=i[u],a=h.length,h=h.replace(/^ *([*+-]|\d+\.) +/,""),~h.indexOf("\n ")&&(a-=h.length,h=this.options.pedantic?h.replace(/^ {1,4}/gm,""):h.replace(new RegExp("^ {1,"+a+"}","gm"),"")),this.options.smartLists&&u!==c-1&&(o=p.bullet.exec(i[u+1])[0],l===o||l.length>1&&o.length>1||(e=i.slice(u+1).join("\n")+e,u=c-1)),s=r||/\n\n(?!\s*$)/.test(h),u!==c-1&&(r="\n"===h.charAt(h.length-1),s||(s=r)),this.tokens.push({type:s?"loose_item_start":"list_item_start"}),this.token(h,!1,n),this.tokens.push({type:"list_item_end"});this.tokens.push({type:"list_end"})}else if(i=this.rules.html.exec(e))e=e.substring(i[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:"pre"===i[1]||"script"===i[1]||"style"===i[1],text:i[0]});else if(!n&&t&&(i=this.rules.def.exec(e)))e=e.substring(i[0].length),this.tokens.links[i[1].toLowerCase()]={href:i[2],title:i[3]};else if(t&&(i=this.rules.table.exec(e))){for(e=e.substring(i[0].length),h={type:"table",header:i[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:i[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:i[3].replace(/(?: *\| *)?\n$/,"").split("\n")},u=0;u])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:o,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:o,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/,u.link=l(u.link)("inside",u._inside)("href",u._href)(),u.reflink=l(u.reflink)("inside",u._inside)(),u.normal=h({},u),u.pedantic=h({},u.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/}),u.gfm=h({},u.normal,{escape:l(u.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:l(u.text)("]|","~]|")("|","|https?://|")()}),u.breaks=h({},u.gfm,{br:l(u.br)("{2,}","*")(),text:l(u.gfm.text)("{2,}","*")()}),t.rules=u,t.output=function(e,n,r){var s=new t(n,r);return s.output(e)},t.prototype.output=function(e){for(var t,n,r,i,l="";e;)if(i=this.rules.escape.exec(e))e=e.substring(i[0].length),l+=i[1];else if(i=this.rules.autolink.exec(e))e=e.substring(i[0].length),"@"===i[2]?(n=this.mangle(":"===i[1].charAt(6)?i[1].substring(7):i[1]),r=this.mangle("mailto:")+n):(n=s(i[1]),r=n),l+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(e))){if(i=this.rules.tag.exec(e))!this.inLink&&/^/i.test(i[0])&&(this.inLink=!1),e=e.substring(i[0].length),l+=this.options.sanitize?s(i[0]):i[0];else if(i=this.rules.link.exec(e))e=e.substring(i[0].length),this.inLink=!0,l+=this.outputLink(i,{href:i[2],title:i[3]}),this.inLink=!1;else if((i=this.rules.reflink.exec(e))||(i=this.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),t=this.links[t.toLowerCase()],!t||!t.href){l+=i[0].charAt(0),e=i[0].substring(1)+e;continue}this.inLink=!0,l+=this.outputLink(i,t),this.inLink=!1}else if(i=this.rules.strong.exec(e))e=e.substring(i[0].length),l+=this.renderer.strong(this.output(i[2]||i[1]));else if(i=this.rules.em.exec(e))e=e.substring(i[0].length),l+=this.renderer.em(this.output(i[2]||i[1]));else if(i=this.rules.code.exec(e))e=e.substring(i[0].length),l+=this.renderer.codespan(s(i[2],!0));else if(i=this.rules.br.exec(e))e=e.substring(i[0].length),l+=this.renderer.br();else if(i=this.rules.del.exec(e))e=e.substring(i[0].length),l+=this.renderer.del(this.output(i[1]));else if(i=this.rules.text.exec(e))e=e.substring(i[0].length),l+=s(this.smartypants(i[0]));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else e=e.substring(i[0].length),n=s(i[1]),r=n,l+=this.renderer.link(r,null,n);return l},t.prototype.outputLink=function(e,t){var n=s(t.href),r=t.title?s(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,s(e[1]))},t.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/--/g,"—").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},t.prototype.mangle=function(e){for(var t,n="",r=e.length,s=0;r>s;s++)t=e.charCodeAt(s),Math.random()>.5&&(t="x"+t.toString(16)),n+="&#"+t+";";return n},n.prototype.code=function(e,t,n){if(this.options.highlight){var r=this.options.highlight(e,t);null!=r&&r!==e&&(n=!0,e=r)}return t?'
    '+(n?e:s(e,!0))+"\n
    \n":"
    "+(n?e:s(e,!0))+"\n
    "},n.prototype.blockquote=function(e){return"
    \n"+e+"
    \n"},n.prototype.html=function(e){return e},n.prototype.heading=function(e,t,n){return"'+e+"\n"},n.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},n.prototype.list=function(e,t){var n=t?"ol":"ul";return"<"+n+">\n"+e+"\n"},n.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},n.prototype.paragraph=function(e){return"

    "+e+"

    \n"},n.prototype.table=function(e,t){return"\n\n"+e+"\n\n"+t+"\n
    \n"},n.prototype.tablerow=function(e){return"\n"+e+"\n"},n.prototype.tablecell=function(e,t){var n=t.header?"th":"td",r=t.align?"<"+n+' style="text-align:'+t.align+'">':"<"+n+">";return r+e+"\n"},n.prototype.strong=function(e){return""+e+""},n.prototype.em=function(e){return""+e+""},n.prototype.codespan=function(e){return""+e+""},n.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},n.prototype.del=function(e){return""+e+""},n.prototype.link=function(e,t,n){if(this.options.sanitize){try{var r=decodeURIComponent(i(e)).replace(/[^\w:]/g,"").toLowerCase()}catch(s){return""}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:"))return""}var l='
    "},n.prototype.image=function(e,t,n){var r=''+n+'":">"},r.parse=function(e,t,n){var s=new r(t,n);return s.parse(e)},r.prototype.parse=function(e){this.inline=new t(e.links,this.options,this.renderer),this.tokens=e.reverse();for(var n="";this.next();)n+=this.tok();return n},r.prototype.next=function(){return this.token=this.tokens.pop()},r.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},r.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},r.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,s,i="",l="";for(n="",e=0;e[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,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+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();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+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",//)("closed",/<(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:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();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)};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,bq){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,"");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,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);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+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);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:!this.options.sanitizer&&(cap[1]==="pre"||cap[1]==="script"||cap[1]==="style"),text:cap[0]});continue}if(!bq&&top&&(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:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,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)\*\*(?!\*)/,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;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;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);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(cap[0]):escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;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].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.text(escape(this.smartypants(cap[0])));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){if(!this.options.mangle)return text;var out="",l=text.length,i=0,ch;for(;i.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"
    "+(escaped?code:escape(code,true))+"\n
    "}return'
    '+(escaped?code:escape(code,true))+"\n
    \n"};Renderer.prototype.blockquote=function(quote){return"
    \n"+quote+"
    \n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"'+text+"\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+"\n"};Renderer.prototype.listitem=function(text){return"
  • "+text+"
  • \n"};Renderer.prototype.paragraph=function(text){return"

    "+text+"

    \n"};Renderer.prototype.table=function(header,body){return"\n"+"\n"+header+"\n"+"\n"+body+"\n"+"
    \n"};Renderer.prototype.tablerow=function(content){return"\n"+content+"\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"\n"};Renderer.prototype.strong=function(text){return""+text+""};Renderer.prototype.em=function(text){return""+text+""};Renderer.prototype.codespan=function(text){return""+text+""};Renderer.prototype.br=function(){return this.options.xhtml?"
    ":"
    "};Renderer.prototype.del=function(text){return""+text+""};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0||prot.indexOf("vbscript:")===0){return""}}var out='
    ";return out};Renderer.prototype.image=function(href,title,text){var out=''+text+'":">";return out};Renderer.prototype.text=function(text){return text};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);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;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 this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}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=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,sanitizer:null,mangle:true,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&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)}()); \ No newline at end of file diff --git a/plugin/math/math.js b/plugin/math/math.js index 25b7516..7867376 100644 --- a/plugin/math/math.js +++ b/plugin/math/math.js @@ -7,14 +7,17 @@ var RevealMath = window.RevealMath || (function(){ var options = Reveal.getConfig().math || {}; - options.mathjax = options.mathjax || 'https://cdn.mathjax.org/mathjax/latest/MathJax.js'; + options.mathjax = options.mathjax || 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js'; options.config = options.config || 'TeX-AMS_HTML-full'; + options.tex2jax = options.tex2jax || { + inlineMath: [['$','$'],['\\(','\\)']] , + skipTags: ['script','noscript','style','textarea','pre'] }; loadScript( options.mathjax + '?config=' + options.config, function() { MathJax.Hub.Config({ messageStyle: 'none', - tex2jax: { inlineMath: [['$','$'],['\\(','\\)']] }, + tex2jax: options.tex2jax, skipStartupTypeset: true }); diff --git a/plugin/multiplex/client.js b/plugin/multiplex/client.js index e6179f6..3ffd1e0 100644 --- a/plugin/multiplex/client.js +++ b/plugin/multiplex/client.js @@ -8,6 +8,6 @@ if (data.socketId !== socketId) { return; } if( window.location.host === 'localhost:1947' ) return; - Reveal.slide(data.indexh, data.indexv, data.indexf, 'remote'); + Reveal.setState(data.state); }); }()); diff --git a/plugin/multiplex/index.js b/plugin/multiplex/index.js index 6f5d8b1..8195f04 100644 --- a/plugin/multiplex/index.js +++ b/plugin/multiplex/index.js @@ -1,37 +1,45 @@ +var http = require('http'); var express = require('express'); var fs = require('fs'); var io = require('socket.io'); var crypto = require('crypto'); -var app = express.createServer(); -var staticDir = express.static; +var app = express(); +var staticDir = express.static; +var server = http.createServer(app); -io = io.listen(app); +io = io(server); var opts = { - port: 1948, + port: process.env.PORT || 1948, baseDir : __dirname + '/../../' }; -io.sockets.on('connection', function(socket) { - socket.on('slidechanged', function(slideData) { - if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return; - if (createHash(slideData.secret) === slideData.socketId) { - slideData.secret = null; - socket.broadcast.emit(slideData.socketId, slideData); +io.on( 'connection', function( socket ) { + socket.on('multiplex-statechanged', function(data) { + if (typeof data.secret == 'undefined' || data.secret == null || data.secret === '') return; + if (createHash(data.secret) === data.socketId) { + data.secret = null; + socket.broadcast.emit(data.socketId, data); }; }); }); -app.configure(function() { - [ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) { - app.use('/' + dir, staticDir(opts.baseDir + dir)); - }); +[ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) { + app.use('/' + dir, staticDir(opts.baseDir + dir)); }); app.get("/", function(req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); - fs.createReadStream(opts.baseDir + '/index.html').pipe(res); + + var stream = fs.createReadStream(opts.baseDir + '/index.html'); + stream.on('error', function( error ) { + res.write('

    reveal.js multiplex server.

    Generate token'); + res.end(); + }); + stream.on('readable', function() { + stream.pipe(res); + }); }); app.get("/token", function(req,res) { @@ -47,7 +55,7 @@ var createHash = function(secret) { }; // Actually listen -app.listen(opts.port || null); +server.listen( opts.port || null ); var brown = '\033[33m', green = '\033[32m', diff --git a/plugin/multiplex/master.js b/plugin/multiplex/master.js index b6a7eb7..7f4bf45 100644 --- a/plugin/multiplex/master.js +++ b/plugin/multiplex/master.js @@ -1,51 +1,34 @@ (function() { + // Don't emit events from inside of notes windows if ( window.location.search.match( /receiver/gi ) ) { return; } var multiplex = Reveal.getConfig().multiplex; - var socket = io.connect(multiplex.url); - - var notify = function( slideElement, indexh, indexv, origin ) { - if( typeof origin === 'undefined' && origin !== 'remote' ) { - var nextindexh; - var nextindexv; - - var fragmentindex = Reveal.getIndices().f; - if (typeof fragmentindex == 'undefined') { - fragmentindex = 0; - } - - if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') { - nextindexh = indexh; - nextindexv = indexv + 1; - } else { - nextindexh = indexh + 1; - nextindexv = 0; - } - - var slideData = { - indexh : indexh, - indexv : indexv, - indexf : fragmentindex, - nextindexh : nextindexh, - nextindexv : nextindexv, - secret: multiplex.secret, - socketId : multiplex.id - }; - - socket.emit('slidechanged', slideData); - } - } - - Reveal.addEventListener( 'slidechanged', function( event ) { - notify( event.currentSlide, event.indexh, event.indexv, event.origin ); - } ); - - var fragmentNotify = function( event ) { - notify( Reveal.getCurrentSlide(), Reveal.getIndices().h, Reveal.getIndices().v, event.origin ); + var socket = io.connect( multiplex.url ); + + function post() { + + var messageData = { + state: Reveal.getState(), + secret: multiplex.secret, + socketId: multiplex.id + }; + + socket.emit( 'multiplex-statechanged', messageData ); + }; - Reveal.addEventListener( 'fragmentshown', fragmentNotify ); - Reveal.addEventListener( 'fragmenthidden', fragmentNotify ); -}()); \ No newline at end of file + // post once the page is loaded, so the client follows also on "open URL". + window.addEventListener( 'load', post ); + + // Monitor events that trigger a change in state + Reveal.addEventListener( 'slidechanged', post ); + Reveal.addEventListener( 'fragmentshown', post ); + Reveal.addEventListener( 'fragmenthidden', post ); + Reveal.addEventListener( 'overviewhidden', post ); + Reveal.addEventListener( 'overviewshown', post ); + Reveal.addEventListener( 'paused', post ); + Reveal.addEventListener( 'resumed', post ); + +}()); diff --git a/plugin/multiplex/package.json b/plugin/multiplex/package.json new file mode 100644 index 0000000..bbed77a --- /dev/null +++ b/plugin/multiplex/package.json @@ -0,0 +1,19 @@ +{ + "name": "reveal-js-multiplex", + "version": "1.0.0", + "description": "reveal.js multiplex server", + "homepage": "http://revealjs.com", + "scripts": { + "start": "node index.js" + }, + "engines": { + "node": "~4.1.1" + }, + "dependencies": { + "express": "~4.13.3", + "grunt-cli": "~0.1.13", + "mustache": "~2.2.1", + "socket.io": "~1.3.7" + }, + "license": "MIT" +} diff --git a/plugin/notes-server/client.js b/plugin/notes-server/client.js index 628586f..00b277b 100644 --- a/plugin/notes-server/client.js +++ b/plugin/notes-server/client.js @@ -41,10 +41,15 @@ } // When a new notes window connects, post our current state - socket.on( 'connect', function( data ) { + socket.on( 'new-subscriber', function( data ) { post(); } ); + // When the state changes from inside of the speaker view + socket.on( 'statechanged-speaker', function( data ) { + Reveal.setState( data.state ); + } ); + // Monitor events that trigger a change in state Reveal.addEventListener( 'slidechanged', post ); Reveal.addEventListener( 'fragmentshown', post ); diff --git a/plugin/notes-server/index.js b/plugin/notes-server/index.js index df917f1..b95f071 100644 --- a/plugin/notes-server/index.js +++ b/plugin/notes-server/index.js @@ -1,39 +1,42 @@ +var http = require('http'); var express = require('express'); var fs = require('fs'); var io = require('socket.io'); -var _ = require('underscore'); var Mustache = require('mustache'); -var app = express.createServer(); +var app = express(); var staticDir = express.static; +var server = http.createServer(app); -io = io.listen(app); +io = io(server); var opts = { port : 1947, baseDir : __dirname + '/../../' }; -io.sockets.on( 'connection', function( socket ) { +io.on( 'connection', function( socket ) { - socket.on( 'connect', function( data ) { - socket.broadcast.emit( 'connect', data ); + socket.on( 'new-subscriber', function( data ) { + socket.broadcast.emit( 'new-subscriber', data ); }); socket.on( 'statechanged', function( data ) { + delete data.state.overview; socket.broadcast.emit( 'statechanged', data ); }); -}); - -app.configure( function() { - - [ 'css', 'js', 'images', 'plugin', 'lib' ].forEach( function( dir ) { - app.use( '/' + dir, staticDir( opts.baseDir + dir ) ); + socket.on( 'statechanged-speaker', function( data ) { + delete data.state.overview; + socket.broadcast.emit( 'statechanged-speaker', data ); }); }); +[ 'css', 'js', 'images', 'plugin', 'lib' ].forEach( function( dir ) { + app.use( '/' + dir, staticDir( opts.baseDir + dir ) ); +}); + app.get('/', function( req, res ) { res.writeHead( 200, { 'Content-Type': 'text/html' } ); @@ -52,7 +55,7 @@ app.get( '/notes/:socketId', function( req, res ) { }); // Actually listen -app.listen( opts.port || null ); +server.listen( opts.port || null ); var brown = '\033[33m', green = '\033[32m', @@ -62,5 +65,5 @@ var slidesLocation = 'http://localhost' + ( opts.port ? ( ':' + opts.port ) : '' console.log( brown + 'reveal.js - Speaker Notes' + reset ); console.log( '1. Open the slides at ' + green + slidesLocation + reset ); -console.log( '2. Click on the link your JS console to go to the notes page' ); +console.log( '2. Click on the link in your JS console to go to the notes page' ); console.log( '3. Advance through your slides and your notes will advance automatically' ); diff --git a/plugin/notes-server/notes.html b/plugin/notes-server/notes.html index 72d0317..ab8c5b1 100644 --- a/plugin/notes-server/notes.html +++ b/plugin/notes-server/notes.html @@ -8,6 +8,7 @@ @@ -152,7 +247,7 @@
    -
    UPCOMING:
    +
    Upcoming

    Time Click to Reset

    @@ -170,6 +265,10 @@

    Notes

    +
    + + +
    @@ -182,11 +281,20 @@

    Notes

    currentState, currentSlide, upcomingSlide, + layoutLabel, + layoutDropdown, connected = false; var socket = io.connect( window.location.origin ), socketId = '{{socketId}}'; + var SPEAKER_LAYOUTS = { + 'default': 'Default', + 'wide': 'Wide', + 'tall': 'Tall', + 'notes-only': 'Notes only' + }; + socket.on( 'statechanged', function( data ) { // ignore data from sockets that aren't ours @@ -195,7 +303,6 @@

    Notes

    if( connected === false ) { connected = true; - setupIframes( data ); setupKeyboard(); setupNotes(); setupTimer(); @@ -206,13 +313,28 @@

    Notes

    } ); + setupLayout(); + + // Load our presentation iframes + setupIframes(); + + // Once the iframes have loaded, emit a signal saying there's + // a new subscriber which will trigger a 'statechanged' + // message to be sent back window.addEventListener( 'message', function( event ) { var data = JSON.parse( event.data ); if( data && data.namespace === 'reveal' ) { if( /ready/.test( data.eventName ) ) { - socket.emit( 'connect', { socketId: socketId } ); + socket.emit( 'new-subscriber', { socketId: socketId } ); + } + } + + // Messages sent by reveal.js inside of the current slide preview + if( data && data.namespace === 'reveal' ) { + if( /slidechanged|fragmentshown|fragmenthidden|overviewshown|overviewhidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) { + socket.emit( 'statechanged-speaker', { state: data.state } ); } } @@ -267,7 +389,7 @@

    Notes

    /** * Creates the preview iframes. */ - function setupIframes( data ) { + function setupIframes() { var params = [ 'receiver', @@ -277,9 +399,8 @@

    Notes

    'backgroundTransition=none' ].join( '&' ); - var hash = '#/' + data.state.indexh + '/' + data.state.indexv; - var currentURL = '/?' + params + '&postMessageEvents=true' + hash; - var upcomingURL = '/?' + params + '&controls=false' + hash; + var currentURL = '/?' + params + '&postMessageEvents=true'; + var upcomingURL = '/?' + params + '&controls=false'; currentSlide = document.createElement( 'iframe' ); currentSlide.setAttribute( 'width', 1280 ); @@ -351,6 +472,74 @@

    Notes

    } + /** + * Sets up the speaker view layout and layout selector. + */ + function setupLayout() { + + layoutDropdown = document.querySelector( '.speaker-layout-dropdown' ); + layoutLabel = document.querySelector( '.speaker-layout-label' ); + + // Render the list of available layouts + for( var id in SPEAKER_LAYOUTS ) { + var option = document.createElement( 'option' ); + option.setAttribute( 'value', id ); + option.textContent = SPEAKER_LAYOUTS[ id ]; + layoutDropdown.appendChild( option ); + } + + // Monitor the dropdown for changes + layoutDropdown.addEventListener( 'change', function( event ) { + + setLayout( layoutDropdown.value ); + + }, false ); + + // Restore any currently persisted layout + setLayout( getLayout() ); + + } + + /** + * Sets a new speaker view layout. The layout is persisted + * in local storage. + */ + function setLayout( value ) { + + var title = SPEAKER_LAYOUTS[ value ]; + + layoutLabel.innerHTML = 'Layout' + ( title ? ( ': ' + title ) : '' ); + layoutDropdown.value = value; + + document.body.setAttribute( 'data-speaker-layout', value ); + + // Persist locally + if( window.localStorage ) { + window.localStorage.setItem( 'reveal-speaker-layout', value ); + } + + } + + /** + * Returns the ID of the most recently set speaker layout + * or our default layout if none has been set. + */ + function getLayout() { + + if( window.localStorage ) { + var layout = window.localStorage.getItem( 'reveal-speaker-layout' ); + if( layout ) { + return layout; + } + } + + // Default to the first record in the layouts hash + for( var id in SPEAKER_LAYOUTS ) { + return id; + } + + } + function zeroPadInteger( num ) { var str = '00' + parseInt( num ); diff --git a/plugin/notes/notes.html b/plugin/notes/notes.html index 0cc8cf6..0c4eca5 100644 --- a/plugin/notes/notes.html +++ b/plugin/notes/notes.html @@ -8,6 +8,7 @@