diff --git a/.gitignore b/.gitignore index 34058c4..9ffdbc7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ log/*.log tmp/** node_modules/ +.sass-cache \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2d6cd8f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.8 +before_script: + - npm install -g grunt-cli \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..22502f6 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,82 @@ +/* global module:false */ +module.exports = function(grunt) { + + // 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://lab.hakim.se/reveal-js\n' + + ' * MIT licensed\n' + + ' *\n' + + ' * Copyright (C) 2013 Hakim El Hattab, http://hakim.se\n' + + ' */' + }, + + jshint: { + files: [ 'Gruntfile.js', 'js/reveal.js' ] + }, + + // Tests will be added soon + qunit: { + files: [ 'test/**/*.html' ] + }, + + uglify: { + options: { + banner: '<%= meta.banner %>\n' + }, + build: { + src: 'js/reveal.js', + dest: 'js/reveal.min.js' + } + }, + + cssmin: { + compress: { + files: { + 'css/reveal.min.css': [ 'css/reveal.css' ] + } + } + }, + + jshint: { + options: { + curly: false, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + eqnull: true, + browser: true, + expr: true + }, + globals: { + head: false, + module: false, + console: false + } + }, + + watch: { + files: [ 'Gruntfile.js', 'js/reveal.js', 'css/reveal.css' ], + tasks: 'default' + } + + }); + + // Dependencies + grunt.loadNpmTasks( 'grunt-contrib-jshint' ); + grunt.loadNpmTasks( 'grunt-contrib-cssmin' ); + grunt.loadNpmTasks( 'grunt-contrib-uglify' ); + grunt.loadNpmTasks( 'grunt-contrib-watch' ); + + // Default task + grunt.registerTask( 'default', [ 'jshint', 'cssmin', 'uglify' ] ); + +}; diff --git a/LICENSE b/LICENSE index 1485053..e1e8bf7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2012 Hakim El Hattab, http://hakim.se +Copyright (C) 2013 Hakim El Hattab, http://hakim.se Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9d2ceaa..2778015 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,29 @@ -# reveal.js +# reveal.js [![Build Status](https://travis-ci.org/hakimel/reveal.js.png?branch=master)](https://travis-ci.org/hakimel/reveal.js) -A CSS 3D slideshow tool for quickly creating good looking HTML presentations. Doesn't _rely_ on any external libraries but [highlight.js](http://softwaremaniacs.org/soft/highlight/en/description/) is included by default for code highlighting. +A framework for easily creating beautiful presentations using HTML. [Check out the live demo](http://lab.hakim.se/reveal-js/). -Note that this requires a browser with support for CSS 3D transforms and ``classList``. If CSS 3D support is not detected, the presentation will degrade to less exciting 2D transitions. A [classList polyfill](http://purl.eligrey.com/github/classList.js/blob/master/classList.js) is incuded to make this work in < iOS 5, < Safari 5.1 and IE. +reveal.js comes with a broad range of features including [nested slides](https://github.com/hakimel/reveal.js#markup), [markdown contents](https://github.com/hakimel/reveal.js#markdown), [PDF export](https://github.com/hakimel/reveal.js#pdf-export), [speaker notes](https://github.com/hakimel/reveal.js#speaker-notes) and a [JavaScript API](https://github.com/hakimel/reveal.js#api). It's best viewed in a browser with support for CSS 3D transforms but [fallbacks](https://github.com/hakimel/reveal.js/wiki/Browser-Support) are available to make sure your presentation can still be viewed elsewhere. -Curious about how it looks in action? [Check out the demo page](http://lab.hakim.se/reveal-js/). -## Usage +#### More reading in the Wiki: +- [Changelog](https://github.com/hakimel/reveal.js/wiki/Changelog): Up-to-date version history. +- [Examples](https://github.com/hakimel/reveal.js/wiki/Example-Presentations): Presentations created with reveal.js, add your own! +- [Browser Support](https://github.com/hakimel/reveal.js/wiki/Browser-Support): Explanation of browser support and fallbacks. + +## rvl.io + +Slides are written using HTML or markdown but there's also an online editor for those of you who prefer a more traditional user interface. Give it a try at [www.rvl.io](http://www.rvl.io). + + +## Instructions ### Markup -Markup heirarchy needs to be ``
`` where the ``
`` represents one slide and can be repeated indefinitely. If you place multiple ``
``'s inside of another ``
`` they will be shown as vertical slides. For example: +Markup heirarchy needs to be ``
`` where the ``
`` represents one slide and can be repeated indefinitely. If you place multiple ``
``'s inside of another ``
`` they will be shown as vertical slides. The first of the vertical slides is the "root" of the others (at the top), and it will be included in the horizontal sequence. For example: ```html
-
+
Single Horizontal Slide
Vertical Slide 1
@@ -26,25 +35,35 @@ Markup heirarchy needs to be ``
``` elements and reveal.js will automatically load the JavaScript parser. +It's possible to write your slides using Markdown. To enable Markdown, add the ```data-markdown``` attribute to your ```
``` elements and wrap the contents in a ```
``` +#### External Markdown + +You can write your content as a separate file and have reveal.js load it at runtime. Note the separator arguments which determine how slides are delimited in the external file. + +```html +
+``` ### Configuration -At the end of your page, after ````, you need to initialize reveal by running the following code. Note that all config values are optional and will default as specified below. +At the end of your page you need to initialize reveal by running the following code. Note that all config values are optional and will default as specified below. ```javascript Reveal.initialize({ + // Display controls in the bottom right corner controls: true, @@ -57,40 +76,117 @@ Reveal.initialize({ // Enable keyboard shortcuts for navigation keyboard: true, + // Enable the slide overview mode + overview: true, + + // Vertical centering of slides + center: true, + // Loop the presentation loop: false, + // Change the presentation direction to be RTL + rtl: false, + // Number of milliseconds between automatically proceeding to the - // next slide, disabled when set to 0 + // next slide, disabled when set to 0, this value can be overwritten + // by using a data-autoslide attribute on your slides autoSlide: 0, // Enable slide navigation via mouse wheel - mouseWheel: true, + mouseWheel: false, // Apply a 3D roll to links on hover rollingLinks: true, - // UI style - theme: 'default', // default/neon/beige - // Transition style - transition: 'default' // default/cube/page/concave/linear(2d) + transition: 'default' // default/cube/page/concave/zoom/linear/fade/none + }); ``` +Note that the new default vertical centering option will break compatibility with slides that were using transitions with backgrounds (`cube` and `page`). To restore the previous behavior, set `center` to `false`. + + +### Presentation Size + +All presentations have a normal size, that is the resolution at which they are authored. The framework will automatically scale presentations uniformly based on this size to ensure that everything fits on any given display or viewport. + +See below for a list of configuration options related to sizing, including default values: + +```javascript +Reveal.initialize({ + + ... + + // The "normal" size of the presentation, aspect ratio will be preserved + // when the presentation is scaled to fit different resolutions. Can be + // specified using percentage units. + width: 960, + height: 700, + + // Factor of the display size that should remain empty around the content + margin: 0.1, + + // Bounds for smallest/largest possible scale to apply to content + minScale: 0.2, + maxScale: 1.0 + +}); +``` + + +### Dependencies + +Reveal.js doesn't _rely_ on any third party scripts to work but a few optional libraries are included by default. These libraries are loaded as dependencies in the order they appear, for example: + +```javascript +Reveal.initialize({ + dependencies: [ + // Cross-browser shim that fully implements classList - https://github.com/eligrey/classList.js/ + { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } }, + + // Interpret Markdown in
elements + { src: 'plugin/markdown/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, + { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, + + // Syntax highlight for elements + { src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, + + // Zoom in and out with Alt+click + { src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } }, + + // Speaker notes + { src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }, + + // Remote control your reveal.js presentation using a touch device + { src: 'plugin/remotes/remotes.js', async: true, condition: function() { return !!document.body.classList; } } + ] +}); +``` + +You can add your own extensions using the same syntax. The following properties are available for each dependency object: +- **src**: Path to the script to load +- **async**: [optional] Flags if the script should load after reveal.js has started, defaults to false +- **callback**: [optional] Function to execute when the script has loaded +- **condition**: [optional] Function which must return true for the script to be loaded + + ### API The Reveal class provides a minimal JavaScript API for controlling navigation and reading state: ```javascript // Navigation -Reveal.navigateTo( indexh, indexv ); -Reveal.navigateLeft(); -Reveal.navigateRight(); -Reveal.navigateUp(); -Reveal.navigateDown(); -Reveal.navigatePrev(); -Reveal.navigateNext(); +Reveal.slide( indexh, indexv, indexf ); +Reveal.left(); +Reveal.right(); +Reveal.up(); +Reveal.down(); +Reveal.prev(); +Reveal.next(); +Reveal.prevFragment(); +Reveal.nextFragment(); Reveal.toggleOverview(); // Retrieves the previous and current slide elements @@ -112,16 +208,86 @@ Reveal.addEventListener( 'somestate', function() { }, false ); ``` +### Ready event + +The 'ready' event is fired when reveal.js has loaded all (synchronous) dependencies and is ready to start navigating. + +```javascript +Reveal.addEventListener( 'ready', function( event ) { + // event.currentSlide, event.indexh, event.indexv +} ); +``` + ### Slide change event An 'slidechanged' event is fired each time the slide is changed (regardless of state). The event object holds the index values of the current slide as well as a reference to the previous and current slide HTML nodes. +Some libraries, like MathJax (see [#226](https://github.com/hakimel/reveal.js/issues/226#issuecomment-10261609)), get confused by the transforms and display states of slides. Often times, this can be fixed by calling their update or render function from this callback. + ```javascript Reveal.addEventListener( 'slidechanged', function( event ) { // event.previousSlide, event.currentSlide, event.indexh, event.indexv } ); ``` +### Internal links + +It's easy to link between slides. The first example below targets the index of another slide whereas the second targets a slide with an ID attribute (```
```): + +```html +Link +Link +``` + +You can also add relative navigation links, similar to the built in reveal.js controls, by appending one of the following classes on any element. Note that each element is automatically given an ```enabled``` class when it's a valid navigation route based on the current slide. + +```html + + + + + + +``` + + +### Fragments +Fragments are used to highlight individual elements on a slide. Every elmement with the class ```fragment``` will be stepped through before moving on to the next slide. Here's an example: http://lab.hakim.se/reveal-js/#/16 + +The default fragment style is to start out invisible and fade in. This style can be changed by appending a different class to the fragment: + +```html +
+

grow

+

shrink

+

roll-in

+

fade-out

+

highlight-red

+

highlight-green

+

highlight-blue

+
+``` + +Multiple fragments can be applied to the same element sequentially by wrapping it, this will fade in the text on the first step and fade it back out on the second. + +```html +
+ + I'll fade in, then out + +
+``` + +The display order of fragments can be controlled using the ```data-fragment-index``` attribute. + +```html +
+

Appears last

+

Appears first

+

Appears second

+
+``` + ### Fragment events When a slide fragment is either shown or hidden reveal.js will dispatch an event. @@ -135,27 +301,38 @@ Reveal.addEventListener( 'fragmenthidden', function( event ) { } ); ``` -### Folder Structure -- **css/** Core styles without which the project does not function -- **js/** Like above but for JavaScript -- **plugin/** Components that have been developed as extensions to reveal.js -- **lib/** All other third party assets (JavaScript, CSS, fonts) +### Code syntax higlighting -## Speaker Notes +By default, Reveal is configured with [highlight.js](http://softwaremaniacs.org/soft/highlight/en/) for code syntax highlighting. Below is an example with clojure code that will be syntax highlighted: -If you're interested in using speaker notes, reveal.js comes with a Node server that allows you to deliver your presentation in one browser while viewing speaker notes in another. +```html +
+

+(def lazy-fib
+  (concat
+   [0 1]
+   ((fn rfib [a b]
+        (lazy-cons (+ a b) (rfib b (+ a b)))) 0 1)))
+	
+
+``` -To include speaker notes in your presentation, simply add an `
- +

Fantastic Ordered List

    @@ -124,31 +130,51 @@
- ## Markdown support - - For those of you who like that sort of thing. - Instructions and a bit more info available [here](https://github.com/hakimel/reveal.js#markdown). +
-
+

Transition Styles

- You can select from different transitions, like: + You can select from different transitions, like:
+ Cube - + Page - + Concave - + Zoom - + Linear - + Fade - + None - + Default +

+
+ +
+

Themes

+

+ Reveal.js comes with a few themes built in:
+ Sky - + Beige - + Simple - + Serif - + Night - + Default +

+

+ + * Theme demos are loaded after the presentation which leads to flicker. In production you should load your theme in the <head> using a <link>. +

-
@@ -156,23 +182,23 @@

Global State

Set data-state="something" on a slide and "something" - will be added as a class to the document element when the slide is open. This let's you + will be added as a class to the document element when the slide is open. This lets you apply broader style changes, like switching the background.

- - + + Down arrow

"blackout"

- - + + Down arrow

"soothe"

- - + + Up arrow
@@ -195,20 +221,20 @@ The nice thing about standards is that there are so many to choose from and block:

- For years there has been a theory that millions of monkeys typing at random on millions of typewriters would + For years there has been a theory that millions of monkeys typing at random on millions of typewriters would reproduce the entire works of Shakespeare. The Internet has proven this theory to be untrue.
- +

Pretty Code


 function linkify( selector ) {
   if( supports3DTransforms ) {
-    
+
     var nodes = document.querySelectorAll( selector );
 
-    for( var i = 0, len = nodes.length; i < len; i++ ) {
+    for( var i = 0, len = nodes.length; i < len; i++ ) {
       var node = nodes[i];
 
       if( !node.className ) ) {
@@ -220,138 +246,131 @@ function linkify( selector ) {
 					

Courtesy of highlight.js.

- +

Intergalactic Interconnections

- You can link between slides internally,
+ You can link between slides internally, like this.

-

Fragmented Views

-

Hit the next arrow...

-

... to step through ...

-
    -
  1. any type
  2. -
  3. of view
  4. -
  5. fragments
  6. -
+
+

Fragmented Views

+

Hit the next arrow...

+

... to step through ...

+
    +
  1. any type
  2. +
  3. of view
  4. +
  5. fragments
  6. +
+ + +
+
+

Fragment Styles

+

There's a few styles of fragments, like:

+

grow

+

shrink

+

roll-in

+

fade-out

+

highlight-red

+

highlight-green

+

highlight-blue

+
- +

Spectacular image!

- - + + Meny
- + +
+

Export to PDF

+

Presentations can be exported to PDF, below is an example that's been uploaded to SlideShare.

+ + +
+ +
+

Take a Moment

+

+ Press b or period on your keyboard to enter the 'paused' mode. This mode is helpful when you want to take distracting slides off the screen + during a presentation. +

+
+

Stellar Links

- + +
+

It's free

+

+ reveal.js and rvl.io are entirely free but if you'd like to support the projects you can donate below. + Donations will go towards hosting and domain costs. +

+
+ + + + + + + + +
+
+

THE END

-

BY Hakim El Hattab / hakim.se

+

BY Hakim El Hattab / hakim.se

+
- - - - -
-
+ diff --git a/js/reveal.js b/js/reveal.js index d820943..2fa74df 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -1,19 +1,34 @@ /*! - * reveal.js 1.5 r11 + * reveal.js * http://lab.hakim.se/reveal-js * MIT licensed - * - * Copyright (C) 2012 Hakim El Hattab, http://hakim.se + * + * Copyright (C) 2013 Hakim El Hattab, http://hakim.se */ var Reveal = (function(){ - - var HORIZONTAL_SLIDES_SELECTOR = '.reveal .slides>section', + + 'use strict'; + + var SLIDES_SELECTOR = '.reveal .slides section', + HORIZONTAL_SLIDES_SELECTOR = '.reveal .slides>section', VERTICAL_SLIDES_SELECTOR = '.reveal .slides>section.present>section', + HOME_SLIDE_SELECTOR = '.reveal .slides>section:first-child', - IS_TOUCH_DEVICE = !!( 'ontouchstart' in window ), - - // Configurations defaults, can be overridden at initialization time + // Configurations defaults, can be overridden at initialization time config = { + + // The "normal" size of the presentation, aspect ratio will be preserved + // when the presentation is scaled to fit different resolutions + width: 960, + height: 700, + + // Factor of the display size that should remain empty around the content + margin: 0.1, + + // Bounds for smallest/largest possible scale to apply to content + minScale: 0.2, + maxScale: 1.0, + // Display controls in the bottom right corner controls: true, @@ -26,27 +41,47 @@ var Reveal = (function(){ // Enable keyboard shortcuts for navigation keyboard: true, + // Enable the slide overview mode + overview: true, + + // Vertical centring of slides + center: true, + + // Enables touch navigation on devices with touch input + touch: true, + // Loop the presentation loop: false, - // Number of milliseconds between automatically proceeding to the - // next slide, disabled when set to 0 + // Change the presentation direction to be RTL + rtl: false, + + // 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 autoSlide: 0, // Enable slide navigation via mouse wheel - mouseWheel: true, + mouseWheel: false, // Apply a 3D roll to links on hover rollingLinks: true, - // UI style - theme: 'default', // default/neon/beige + // Theme (see /css/theme) + theme: null, // Transition style - transition: 'default' // default/cube/page/concave/linear(2d) + transition: 'default', // default/cube/page/concave/zoom/linear/fade/none + + // Script dependencies to load + dependencies: [] }, - // The horizontal and verical index of the currently active slide + // Stores if the next slide should be shown automatically + // after n milliseconds + autoSlide = config.autoSlide, + + // The horizontal and vertical index of the currently active slide indexh = 0, indexv = 0, @@ -54,30 +89,31 @@ var Reveal = (function(){ previousSlide, currentSlide, - // 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 + // 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. state = [], + // The current scale of the presentation (see width/height config) + scale = 1, + // Cached references to DOM elements dom = {}, // Detect support for CSS 3D transforms supports3DTransforms = '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, - - supports2DTransforms = '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, + 'MozPerspective' in document.body.style || + 'msPerspective' in document.body.style || + 'OPerspective' in document.body.style || + 'perspective' in document.body.style, + + // Detect support for CSS 2D transforms + supports2DTransforms = '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, - // Detect support for elem.classList - supportsClassList = !!document.body.classList; - // Throttles mouse wheel navigation mouseWheelTimeout = 0, @@ -87,6 +123,15 @@ var Reveal = (function(){ // Delays updates to the URL due to a Chrome thumbnailer bug writeURLTimeout = 0, + // A delay used to activate the overview mode + activateOverviewTimeout = 0, + + // A delay used to deactivate the overview mode + deactivateOverviewTimeout = 0, + + // Flags if the interaction event listeners are bound + eventsAreBound = false, + // Holds information about the currently ongoing touch input touch = { startX: 0, @@ -94,42 +139,177 @@ var Reveal = (function(){ startSpan: 0, startCount: 0, handled: false, - threshold: 40 + threshold: 80 }; - - + /** - * Starts up the slideshow by applying configuration - * options and binding various events. + * Starts up the presentation if the client is capable. */ function initialize( options ) { - - if( ( !supports2DTransforms && !supports3DTransforms ) || !supportsClassList ) { + + if( !supports2DTransforms && !supports3DTransforms ) { document.body.setAttribute( 'class', 'no-transforms' ); - // If the browser doesn't support core features we won't be + // If the browser doesn't support core features we won't be // using JavaScript to control the presentation return; } - // Cache references to DOM elements - dom.wrapper = document.querySelector( '.reveal' ); - dom.progress = document.querySelector( '.reveal .progress' ); - dom.progressbar = document.querySelector( '.reveal .progress span' ); - - if ( config.controls ) { - dom.controls = document.querySelector( '.reveal .controls' ); - dom.controlsLeft = document.querySelector( '.reveal .controls .left' ); - dom.controlsRight = document.querySelector( '.reveal .controls .right' ); - dom.controlsUp = document.querySelector( '.reveal .controls .up' ); - dom.controlsDown = document.querySelector( '.reveal .controls .down' ); - } - - addEventListeners(); + // Force a layout when the whole page, incl fonts, has loaded + window.addEventListener( 'load', layout, false ); // Copy options over to our config object extend( config, options ); + // Hide the address bar in mobile browsers + hideAddressBar(); + + // Loads the dependencies and continues to #start() once done + load(); + + } + + /** + * Finds and stores references to DOM elements which are + * required by the presentation. If a required element is + * not found, it is created. + */ + function setupDOM() { + + // Cache references to key DOM elements + dom.theme = document.querySelector( '#theme' ); + dom.wrapper = document.querySelector( '.reveal' ); + dom.slides = document.querySelector( '.reveal .slides' ); + + // Progress bar + if( !dom.wrapper.querySelector( '.progress' ) && config.progress ) { + var progressElement = document.createElement( 'div' ); + progressElement.classList.add( 'progress' ); + progressElement.innerHTML = ''; + dom.wrapper.appendChild( progressElement ); + } + + // Arrow controls + if( !dom.wrapper.querySelector( '.controls' ) && config.controls ) { + var controlsElement = document.createElement( 'aside' ); + controlsElement.classList.add( 'controls' ); + controlsElement.innerHTML = '' + + '' + + '' + + ''; + dom.wrapper.appendChild( controlsElement ); + } + + // Presentation background element + if( !dom.wrapper.querySelector( '.state-background' ) ) { + var backgroundElement = document.createElement( 'div' ); + backgroundElement.classList.add( 'state-background' ); + dom.wrapper.appendChild( backgroundElement ); + } + + // Overlay graphic which is displayed during the paused mode + if( !dom.wrapper.querySelector( '.pause-overlay' ) ) { + var pausedElement = document.createElement( 'div' ); + pausedElement.classList.add( 'pause-overlay' ); + dom.wrapper.appendChild( pausedElement ); + } + + // Cache references to elements + dom.progress = document.querySelector( '.reveal .progress' ); + dom.progressbar = document.querySelector( '.reveal .progress span' ); + + if ( config.controls ) { + dom.controls = document.querySelector( '.reveal .controls' ); + + // There can be multiple instances of controls throughout the page + dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) ); + dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) ); + dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) ); + dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) ); + dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) ); + dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) ); + } + + } + + /** + * Hides the address bar if we're on a mobile device. + */ + function hideAddressBar() { + + if( /iphone|ipod|android/gi.test( navigator.userAgent ) && !/crios/gi.test( navigator.userAgent ) ) { + // Events that should trigger the address bar to hide + window.addEventListener( 'load', removeAddressBar, false ); + window.addEventListener( 'orientationchange', removeAddressBar, false ); + } + + } + + /** + * 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 = [], + scriptsAsync = []; + + for( var i = 0, len = config.dependencies.length; i < len; i++ ) { + var s = config.dependencies[i]; + + // Load if there's no condition or the condition is truthy + if( !s.condition || s.condition() ) { + if( s.async ) { + scriptsAsync.push( s.src ); + } + else { + scripts.push( s.src ); + } + + // Extension may contain callback functions + if( typeof s.callback === 'function' ) { + head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], s.callback ); + } + } + } + + // Called once synchronous scripts finish loading + function proceed() { + if( scriptsAsync.length ) { + // Load asynchronous scripts + head.js.apply( null, scriptsAsync ); + } + + start(); + } + + if( scripts.length ) { + head.ready( proceed ); + + // Load synchronous scripts + head.js.apply( null, scripts ); + } + else { + proceed(); + } + + } + + /** + * Starts up reveal.js by binding input events and navigating + * to the current URL deeplink if there is one. + */ + function start() { + + // Make sure we've got all the DOM elements we need + setupDOM(); + + // Subscribe to input + addEventListeners(); + // Updates the presentation to match the current configuration values configure(); @@ -139,450 +319,840 @@ var Reveal = (function(){ // Start auto-sliding if it's enabled cueAutoSlide(); - // Set up hiding of the browser address bar - if( navigator.userAgent.match( /(iphone|ipod|android)/i ) ) { - // Give the page some scrollable overflow - document.documentElement.style.overflow = 'scroll'; - document.body.style.height = '120%'; + // Notify listeners that the presentation is ready but use a 1ms + // timeout to ensure it's not fired synchronously after #initialize() + setTimeout( function() { + dispatchEvent( 'ready', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + }, 1 ); - // Events that should trigger the address bar to hide - window.addEventListener( 'load', removeAddressBar, false ); - window.addEventListener( 'orientationchange', removeAddressBar, false ); - } - } - function configure() { - if( supports3DTransforms === false ) { - // Fall back on the 2D transform theme 'linear' - config.transition = 'linear'; + /** + * Applies the configuration settings from the config object. + */ + function configure( options ) { + + dom.wrapper.classList.remove( 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 ); + + // Force linear transition based on browser capabilities + if( supports3DTransforms === false ) config.transition = 'linear'; + + dom.wrapper.classList.add( config.transition ); + + if( dom.controls ) { + dom.controls.style.display = ( config.controls && dom.controls ) ? 'block' : 'none'; } - if( config.controls && dom.controls ) { - dom.controls.style.display = 'block'; + if( dom.progress ) { + dom.progress.style.display = ( config.progress && dom.progress ) ? 'block' : 'none'; } - if( config.progress && dom.progress ) { - dom.progress.style.display = 'block'; + if( config.rtl ) { + dom.wrapper.classList.add( 'rtl' ); + } + else { + dom.wrapper.classList.remove( 'rtl' ); } - if( config.transition !== 'default' ) { - dom.wrapper.classList.add( config.transition ); + if( config.center ) { + dom.wrapper.classList.add( 'center' ); } - - if( config.theme !== 'default' ) { - document.documentElement.classList.add( 'theme-' + config.theme ); + else { + dom.wrapper.classList.remove( 'center' ); } if( config.mouseWheel ) { document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF document.addEventListener( 'mousewheel', onDocumentMouseScroll, false ); } - - if( config.rollingLinks ) { - // Add some 3D magic to our anchors - linkify(); + else { + document.removeEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF + document.removeEventListener( 'mousewheel', onDocumentMouseScroll, false ); } + + // 3D links + if( config.rollingLinks ) { + enable3DLinks(); + } + else { + disable3DLinks(); + } + + // Load the theme in the config, if it's not already loaded + if( config.theme && dom.theme ) { + var themeURL = dom.theme.getAttribute( 'href' ); + var themeFinder = /[^\/]*?(?=\.css)/; + var themeName = themeURL.match(themeFinder)[0]; + + if( config.theme !== themeName ) { + themeURL = themeURL.replace(themeFinder, config.theme); + dom.theme.setAttribute( 'href', themeURL ); + } + } + + // Force a layout to make sure the current config is accounted for + layout(); + } + /** + * Binds all event listeners. + */ function addEventListeners() { - document.addEventListener( 'touchstart', onDocumentTouchStart, false ); - document.addEventListener( 'touchmove', onDocumentTouchMove, false ); - document.addEventListener( 'touchend', onDocumentTouchEnd, false ); + + eventsAreBound = true; + window.addEventListener( 'hashchange', onWindowHashChange, false ); + window.addEventListener( 'resize', onWindowResize, false ); + + if( config.touch ) { + document.addEventListener( 'touchstart', onDocumentTouchStart, false ); + document.addEventListener( 'touchmove', onDocumentTouchMove, false ); + document.addEventListener( 'touchend', onDocumentTouchEnd, false ); + } if( config.keyboard ) { document.addEventListener( 'keydown', onDocumentKeyDown, false ); } - if ( config.controls && dom.controls ) { - dom.controlsLeft.addEventListener( 'click', preventAndForward( navigateLeft ), false ); - dom.controlsRight.addEventListener( 'click', preventAndForward( navigateRight ), false ); - dom.controlsUp.addEventListener( 'click', preventAndForward( navigateUp ), false ); - dom.controlsDown.addEventListener( 'click', preventAndForward( navigateDown ), false ); + if ( config.progress && dom.progress ) { + dom.progress.addEventListener( 'click', onProgressClicked, false ); } - } - function removeEventListeners() { - document.removeEventListener( 'keydown', onDocumentKeyDown, false ); - document.removeEventListener( 'touchstart', onDocumentTouchStart, false ); - document.removeEventListener( 'touchmove', onDocumentTouchMove, false ); - document.removeEventListener( 'touchend', onDocumentTouchEnd, false ); - window.removeEventListener( 'hashchange', onWindowHashChange, false ); - if ( config.controls && dom.controls ) { - dom.controlsLeft.removeEventListener( 'click', preventAndForward( navigateLeft ), false ); - dom.controlsRight.removeEventListener( 'click', preventAndForward( navigateRight ), false ); - dom.controlsUp.removeEventListener( 'click', preventAndForward( navigateUp ), false ); - dom.controlsDown.removeEventListener( 'click', preventAndForward( navigateDown ), false ); + var actionEvent = 'ontouchstart' in window && window.ontouchstart != null ? 'touchstart' : 'click'; + dom.controlsLeft.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateLeftClicked, false ); } ); + dom.controlsRight.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateRightClicked, false ); } ); + dom.controlsUp.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateUpClicked, false ); } ); + dom.controlsDown.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateDownClicked, false ); } ); + dom.controlsPrev.forEach( function( el ) { el.addEventListener( actionEvent, onNavigatePrevClicked, false ); } ); + dom.controlsNext.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateNextClicked, false ); } ); } + } /** - * Extend object a with the properties of object b. + * Unbinds all event listeners. + */ + function removeEventListeners() { + + eventsAreBound = false; + + document.removeEventListener( 'keydown', onDocumentKeyDown, false ); + window.removeEventListener( 'hashchange', onWindowHashChange, false ); + window.removeEventListener( 'resize', onWindowResize, false ); + + if( config.touch ) { + document.removeEventListener( 'touchstart', onDocumentTouchStart, false ); + document.removeEventListener( 'touchmove', onDocumentTouchMove, false ); + document.removeEventListener( 'touchend', onDocumentTouchEnd, false ); + } + + if ( config.progress && dom.progress ) { + dom.progress.removeEventListener( 'click', onProgressClicked, false ); + } + + if ( config.controls && dom.controls ) { + var actionEvent = 'ontouchstart' in window && window.ontouchstart != null ? 'touchstart' : 'click'; + dom.controlsLeft.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateLeftClicked, false ); } ); + dom.controlsRight.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateRightClicked, false ); } ); + dom.controlsUp.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateUpClicked, false ); } ); + dom.controlsDown.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateDownClicked, false ); } ); + dom.controlsPrev.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigatePrevClicked, false ); } ); + dom.controlsNext.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateNextClicked, false ); } ); + } + + } + + /** + * Extend object a with the properties of object b. * If there's a conflict, object b takes precedence. */ function extend( a, b ) { + for( var i in b ) { a[ i ] = b[ i ]; } + + } + + /** + * Converts the target object to an array. + */ + function toArray( o ) { + + return Array.prototype.slice.call( o ); + } /** * Measures the distance in pixels between point a - * and point b. - * + * and point b. + * * @param {Object} a point with x/y properties * @param {Object} b point with x/y properties */ function distanceBetween( a, b ) { + var dx = a.x - b.x, dy = a.y - b.y; return Math.sqrt( dx*dx + dy*dy ); + } /** - * Prevents an events defaults behavior calls the - * specified delegate. - * - * @param {Function} delegate The method to call - * after the wrapper has been executed - */ - function preventAndForward( delegate ) { - return function( event ) { - event.preventDefault(); - delegate.call(); - } - } - - /** - * Causes the address bar to hide on mobile devices, + * Causes the address bar to hide on mobile devices, * more vertical space ftw. */ function removeAddressBar() { + + if( window.orientation === 0 ) { + document.documentElement.style.overflow = 'scroll'; + document.body.style.height = '120%'; + } + else { + document.documentElement.style.overflow = ''; + document.body.style.height = '100%'; + } + setTimeout( function() { window.scrollTo( 0, 1 ); - }, 0 ); - } - - /** - * Handler for the document level 'keydown' event. - * - * @param {Object} event - */ - function onDocumentKeyDown( event ) { - // FFT: Use document.querySelector( ':focus' ) === null - // instead of checking contentEditable? - - // Disregard the event if the target is editable or a - // modifier is present - if ( event.target.contentEditable != 'inherit' || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return; - - var triggered = false; - - switch( event.keyCode ) { - // p, page up - case 80: case 33: navigatePrev(); triggered = true; break; - // n, page down - case 78: case 34: navigateNext(); triggered = true; break; - // h, left - case 72: case 37: navigateLeft(); triggered = true; break; - // l, right - case 76: case 39: navigateRight(); triggered = true; break; - // k, up - case 75: case 38: navigateUp(); triggered = true; break; - // j, down - case 74: case 40: navigateDown(); triggered = true; break; - // home - case 36: navigateTo( 0 ); triggered = true; break; - // end - case 35: navigateTo( Number.MAX_VALUE ); triggered = true; break; - // space - case 32: overviewIsActive() ? deactivateOverview() : navigateNext(); triggered = true; break; - // return - case 13: if( overviewIsActive() ) { deactivateOverview(); triggered = true; } break; - } - - // If the input resulted in a triggered action we should prevent - // the browsers default behavior - if( triggered ) { - event.preventDefault(); - } - else if ( event.keyCode === 27 && supports3DTransforms ) { - toggleOverview(); - - event.preventDefault(); - } - - // If auto-sliding is enabled we need to cue up - // another timeout - cueAutoSlide(); + }, 10 ); } /** - * Handler for the document level 'touchstart' event, - * enables support for swipe and pinch gestures. + * Dispatches an event of the specified type from the + * reveal DOM element. */ - function onDocumentTouchStart( event ) { - touch.startX = event.touches[0].clientX; - touch.startY = event.touches[0].clientY; - touch.startCount = event.touches.length; + function dispatchEvent( type, properties ) { - // If there's two touches we need to memorize the distance - // between those two points to detect pinching - if( event.touches.length === 2 ) { - touch.startSpan = distanceBetween( { - x: event.touches[1].clientX, - y: event.touches[1].clientY - }, { - x: touch.startX, - y: touch.startY - } ); - } - } - - /** - * Handler for the document level 'touchmove' event. - */ - function onDocumentTouchMove( event ) { - // Each touch should only trigger one action - if( !touch.handled ) { - var currentX = event.touches[0].clientX; - var currentY = event.touches[0].clientY; + var event = document.createEvent( "HTMLEvents", 1, 2 ); + event.initEvent( type, true, true ); + extend( event, properties ); + dom.wrapper.dispatchEvent( event ); - // If the touch started off with two points and still has - // two active touches; test for the pinch gesture - if( event.touches.length === 2 && touch.startCount === 2 ) { - - // The current distance in pixels between the two touch points - var currentSpan = distanceBetween( { - x: event.touches[1].clientX, - y: event.touches[1].clientY - }, { - x: touch.startX, - y: touch.startY - } ); - - // If the span is larger than the desire amount we've got - // ourselves a pinch - if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) { - touch.handled = true; - - if( currentSpan < touch.startSpan ) { - activateOverview(); - } - else { - deactivateOverview(); - } - } - - } - // There was only one touch point, look for a swipe - else if( event.touches.length === 1 ) { - var deltaX = currentX - touch.startX, - deltaY = currentY - touch.startY; - - if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) { - touch.handled = true; - navigateLeft(); - } - else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) { - touch.handled = true; - navigateRight(); - } - else if( deltaY > touch.threshold ) { - touch.handled = true; - navigateUp(); - } - else if( deltaY < -touch.threshold ) { - touch.handled = true; - navigateDown(); - } - } - - event.preventDefault(); - } - } - - /** - * Handler for the document level 'touchend' event. - */ - function onDocumentTouchEnd( event ) { - touch.handled = false; - } - - /** - * Handles mouse wheel scrolling, throttled to avoid - * skipping multiple slides. - */ - function onDocumentMouseScroll( event ){ - clearTimeout( mouseWheelTimeout ); - - mouseWheelTimeout = setTimeout( function() { - var delta = event.detail || -event.wheelDelta; - if( delta > 0 ) { - navigateNext(); - } - else { - navigatePrev(); - } - }, 100 ); - } - - /** - * Handler for the window level 'hashchange' event. - * - * @param {Object} event - */ - function onWindowHashChange( event ) { - readURL(); } /** * Wrap all links in 3D goodness. */ - function linkify() { - if( supports3DTransforms && !( 'msPerspective' in document.body.style ) ) { - var nodes = document.querySelectorAll( '.reveal .slides section a:not(.image)' ); + function enable3DLinks() { + + if( supports3DTransforms && !( 'msPerspective' in document.body.style ) ) { + var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a:not(.image)' ); + + for( var i = 0, len = anchors.length; i < len; i++ ) { + var anchor = anchors[i]; + + if( anchor.textContent && !anchor.querySelector( '*' ) && ( !anchor.className || !anchor.classList.contains( anchor, 'roll' ) ) ) { + var span = document.createElement('span'); + span.setAttribute('data-title', anchor.text); + span.innerHTML = anchor.innerHTML; + + anchor.classList.add( 'roll' ); + anchor.innerHTML = ''; + anchor.appendChild(span); + } + } + } - for( var i = 0, len = nodes.length; i < len; i++ ) { - var node = nodes[i]; - - if( node.textContent && !node.querySelector( 'img' ) && ( !node.className || !node.classList.contains( node, 'roll' ) ) ) { - node.classList.add( 'roll' ); - node.innerHTML = '' + node.innerHTML + ''; - } - }; - } } /** - * Displays the overview of slides (quick nav) by + * Unwrap all 3D links. + */ + function disable3DLinks() { + + var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a.roll' ); + + for( var i = 0, len = anchors.length; i < len; i++ ) { + var anchor = anchors[i]; + var span = anchor.querySelector( 'span' ); + + if( span ) { + anchor.classList.remove( 'roll' ); + anchor.innerHTML = span.innerHTML; + } + } + + } + + /** + * Return a sorted fragments list, ordered by an increasing + * "data-fragment-index" attribute. + * + * Fragments will be revealed in the order that they are returned by + * this function, so you can use the index attributes to control the + * order of fragment appearance. + * + * To maintain a sensible default fragment order, fragments are presumed + * to be passed in document order. This function adds a "fragment-index" + * attribute to each node if such an attribute is not already present, + * and sets that attribute to an integer value which is the position of + * the fragment within the fragments list. + */ + function sortFragments( fragments ) { + + var a = toArray( fragments ); + + a.forEach( function( el, idx ) { + if( !el.hasAttribute( 'data-fragment-index' ) ) { + el.setAttribute( 'data-fragment-index', idx ); + } + } ); + + a.sort( function( l, r ) { + return l.getAttribute( 'data-fragment-index' ) - r.getAttribute( 'data-fragment-index'); + } ); + + return a + + } + + /** + * Applies JavaScript-controlled layout rules to the + * presentation. + */ + function layout() { + + if( dom.wrapper ) { + + // Available space to scale within + var availableWidth = dom.wrapper.offsetWidth, + availableHeight = dom.wrapper.offsetHeight; + + // Reduce available space by margin + availableWidth -= ( availableHeight * config.margin ); + availableHeight -= ( availableHeight * config.margin ); + + // Dimensions of the content + var slideWidth = config.width, + slideHeight = config.height; + + // Slide width may be a percentage of available width + if( typeof slideWidth === 'string' && /%$/.test( slideWidth ) ) { + slideWidth = parseInt( slideWidth, 10 ) / 100 * availableWidth; + } + + // Slide height may be a percentage of available height + if( typeof slideHeight === 'string' && /%$/.test( slideHeight ) ) { + slideHeight = parseInt( slideHeight, 10 ) / 100 * availableHeight; + } + + dom.slides.style.width = slideWidth + 'px'; + dom.slides.style.height = slideHeight + 'px'; + + // Determine scale of content to fit within available space + scale = Math.min( availableWidth / slideWidth, availableHeight / slideHeight ); + + // Respect max/min scale settings + scale = Math.max( scale, config.minScale ); + scale = Math.min( scale, config.maxScale ); + + // Prefer applying scale via zoom since Chrome blurs scaled content + // with nested transforms + if( typeof dom.slides.style.zoom !== 'undefined' && !navigator.userAgent.match( /(iphone|ipod|ipad|android)/gi ) ) { + dom.slides.style.zoom = scale; + } + // Apply scale transform as a fallback + else { + var transform = 'translate(-50%, -50%) scale('+ scale +') translate(50%, 50%)'; + + dom.slides.style.WebkitTransform = transform; + dom.slides.style.MozTransform = transform; + dom.slides.style.msTransform = transform; + dom.slides.style.OTransform = transform; + dom.slides.style.transform = transform; + } + + // Select all slides, vertical and horizontal + var slides = toArray( document.querySelectorAll( SLIDES_SELECTOR ) ); + + 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; + } + + if( config.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( - ( slide.offsetHeight / 2 ) - 20, -slideHeight / 2 ) + 'px'; + } + } + else { + slide.style.top = ''; + } + + } + + } + + } + + /** + * Stores the vertical index of a stack so that the same + * vertical slide can be selected when navigating to and + * from the stack. + * + * @param {HTMLElement} stack The vertical stack element + * @param {int} v Index to memorize + */ + function setPreviousVerticalIndex( stack, v ) { + + if( stack ) { + stack.setAttribute( 'data-previous-indexv', v || 0 ); + } + + } + + /** + * Retrieves the vertical index which was stored using + * #setPreviousVerticalIndex() or 0 if no previous index + * exists. + * + * @param {HTMLElement} stack The vertical stack element + */ + function getPreviousVerticalIndex( stack ) { + + if( stack && stack.classList.contains( 'stack' ) ) { + return parseInt( stack.getAttribute( 'data-previous-indexv' ) || 0, 10 ); + } + + return 0; + + } + + /** + * Displays the overview of slides (quick nav) by * scaling down and arranging all slide elements. - * - * Experimental feature, might be dropped if perf + * + * Experimental feature, might be dropped if perf * can't be improved. */ function activateOverview() { - - dom.wrapper.classList.add( 'overview' ); - var horizontalSlides = Array.prototype.slice.call( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + // Only proceed if enabled in config + if( config.overview ) { - for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) { - var hslide = horizontalSlides[i], - htransform = 'translateZ(-2500px) translate(' + ( ( i - indexh ) * 105 ) + '%, 0%)'; - - hslide.setAttribute( 'data-index-h', i ); - hslide.style.display = 'block'; - hslide.style.WebkitTransform = htransform; - hslide.style.MozTransform = htransform; - hslide.style.msTransform = htransform; - hslide.style.OTransform = htransform; - hslide.style.transform = htransform; - - if( !hslide.classList.contains( 'stack' ) ) { - // Navigate to this slide on click - hslide.addEventListener( 'click', onOverviewSlideClicked, true ); - } - - var verticalSlides = Array.prototype.slice.call( hslide.querySelectorAll( 'section' ) ); + // Don't auto-slide while in overview mode + cancelAutoSlide(); - for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) { - var vslide = verticalSlides[j], - vtransform = 'translate(0%, ' + ( ( j - indexv ) * 105 ) + '%)'; + var wasActive = dom.wrapper.classList.contains( 'overview' ); - vslide.setAttribute( 'data-index-h', i ); - vslide.setAttribute( 'data-index-v', j ); - vslide.style.display = 'block'; - vslide.style.WebkitTransform = vtransform; - vslide.style.MozTransform = vtransform; - vslide.style.msTransform = vtransform; - vslide.style.OTransform = vtransform; - vslide.style.transform = vtransform; + dom.wrapper.classList.add( 'overview' ); + dom.wrapper.classList.remove( 'exit-overview' ); + + clearTimeout( activateOverviewTimeout ); + clearTimeout( deactivateOverviewTimeout ); + + // Not the pretties solution, but need to let the overview + // class apply first so that slides are measured accurately + // before we can position them + activateOverviewTimeout = setTimeout( function(){ + + var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); + + for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) { + var hslide = horizontalSlides[i], + htransform = 'translateZ(-2500px) translate(' + ( ( i - indexh ) * 105 ) + '%, 0%)'; + + hslide.setAttribute( 'data-index-h', i ); + hslide.style.display = 'block'; + hslide.style.WebkitTransform = htransform; + hslide.style.MozTransform = htransform; + hslide.style.msTransform = htransform; + hslide.style.OTransform = htransform; + hslide.style.transform = htransform; + + if( hslide.classList.contains( 'stack' ) ) { + + var verticalSlides = hslide.querySelectorAll( 'section' ); + + for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) { + var verticalIndex = i === indexh ? indexv : getPreviousVerticalIndex( hslide ); + + var vslide = verticalSlides[j], + vtransform = 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)'; + + vslide.setAttribute( 'data-index-h', i ); + vslide.setAttribute( 'data-index-v', j ); + vslide.style.display = 'block'; + vslide.style.WebkitTransform = vtransform; + vslide.style.MozTransform = vtransform; + vslide.style.msTransform = vtransform; + vslide.style.OTransform = vtransform; + vslide.style.transform = vtransform; + + // Navigate to this slide on click + vslide.addEventListener( 'click', onOverviewSlideClicked, true ); + } + + } + else { + + // Navigate to this slide on click + hslide.addEventListener( 'click', onOverviewSlideClicked, true ); + + } + } + + layout(); + + if( !wasActive ) { + // Notify observers of the overview showing + dispatchEvent( 'overviewshown', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + } + + }, 10 ); - // Navigate to this slide on click - vslide.addEventListener( 'click', onOverviewSlideClicked, true ); - } - } + } - + /** * Exits the slide overview and enters the currently * active slide. */ function deactivateOverview() { - dom.wrapper.classList.remove( 'overview' ); - var slides = Array.prototype.slice.call( document.querySelectorAll( '.reveal .slides section' ) ); + // Only proceed if enabled in config + if( config.overview ) { - for( var i = 0, len = slides.length; i < len; i++ ) { - var element = slides[i]; + clearTimeout( activateOverviewTimeout ); + clearTimeout( deactivateOverviewTimeout ); - // Resets all transforms to use the external styles - element.style.WebkitTransform = ''; - element.style.MozTransform = ''; - element.style.msTransform = ''; - element.style.OTransform = ''; - element.style.transform = ''; + dom.wrapper.classList.remove( 'overview' ); - element.removeEventListener( 'click', onOverviewSlideClicked ); + // Temporarily add a class so that transitions can do different things + // depending on whether they are exiting/entering overview, or just + // moving from slide to slide + dom.wrapper.classList.add( 'exit-overview' ); + + deactivateOverviewTimeout = setTimeout( function () { + dom.wrapper.classList.remove( 'exit-overview' ); + }, 10); + + // Select all slides + var slides = toArray( document.querySelectorAll( SLIDES_SELECTOR ) ); + + for( var i = 0, len = slides.length; i < len; i++ ) { + var element = slides[i]; + + element.style.display = ''; + + // Resets all transforms to use the external styles + element.style.WebkitTransform = ''; + element.style.MozTransform = ''; + element.style.msTransform = ''; + element.style.OTransform = ''; + element.style.transform = ''; + + element.removeEventListener( 'click', onOverviewSlideClicked, true ); + } + + slide( indexh, indexv ); + + cueAutoSlide(); + + // Notify observers of the overview hiding + dispatchEvent( 'overviewhidden', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + + } + } + + /** + * Toggles the slide overview mode on and off. + * + * @param {Boolean} override Optional flag which overrides the + * toggle logic and forcibly sets the desired state. True means + * overview is open, false means it's closed. + */ + function toggleOverview( override ) { + + if( typeof override === 'boolean' ) { + override ? activateOverview() : deactivateOverview(); + } + else { + isOverview() ? deactivateOverview() : activateOverview(); } - slide(); } /** * Checks if the overview is currently active. - * + * * @return {Boolean} true if the overview is active, * false otherwise */ - function overviewIsActive() { + function isOverview() { + return dom.wrapper.classList.contains( 'overview' ); + } /** - * Invoked when a slide is and we're in the overview. + * Handling the fullscreen functionality via the fullscreen API + * + * @see http://fullscreen.spec.whatwg.org/ + * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode */ - function onOverviewSlideClicked( event ) { - // TODO There's a bug here where the event listeners are not - // removed after deactivating the overview. - if( overviewIsActive() ) { - event.preventDefault(); + function enterFullscreen() { - deactivateOverview(); + var element = document.body; - indexh = this.getAttribute( 'data-index-h' ); - indexv = this.getAttribute( 'data-index-v' ); + // Check which implementation is available + var requestMethod = element.requestFullScreen || + element.webkitRequestFullScreen || + element.mozRequestFullScreen || + element.msRequestFullScreen; - slide(); + if( requestMethod ) { + requestMethod.apply( element ); } + + } + + /** + * Enters the paused mode which fades everything on screen to + * black. + */ + function pause() { + + var wasPaused = dom.wrapper.classList.contains( 'paused' ); + + cancelAutoSlide(); + dom.wrapper.classList.add( 'paused' ); + + if( wasPaused === false ) { + dispatchEvent( 'paused' ); + } + + } + + /** + * Exits from the paused mode. + */ + function resume() { + + var wasPaused = dom.wrapper.classList.contains( 'paused' ); + + cueAutoSlide(); + dom.wrapper.classList.remove( 'paused' ); + + if( wasPaused ) { + dispatchEvent( 'resumed' ); + } + + } + + /** + * Toggles the paused mode on and off. + */ + function togglePause() { + + if( isPaused() ) { + resume(); + } + else { + pause(); + } + + } + + /** + * Checks if we are currently in the paused mode. + */ + function isPaused() { + + return dom.wrapper.classList.contains( 'paused' ); + + } + + /** + * Steps from the current point in the presentation to the + * 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 + * target slide to activate + */ + function slide( h, v, f ) { + + // Remember where we were at before + previousSlide = currentSlide; + + // Query all horizontal slides in the deck + var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); + + // If no vertical index is specified and the upcoming slide is a + // stack, resume at its previous vertical index + if( v === undefined ) { + v = getPreviousVerticalIndex( horizontalSlides[ h ] ); + } + + // If we were on a vertical stack, remember what vertical index + // it was on so we can resume at the same position when returning + if( previousSlide && previousSlide.parentNode && previousSlide.parentNode.classList.contains( 'stack' ) ) { + setPreviousVerticalIndex( previousSlide.parentNode, indexv ); + } + + // Remember the state before this slide + var stateBefore = state.concat(); + + // Reset the state array + state.length = 0; + + var indexhBefore = indexh, + indexvBefore = indexv; + + // Activate and transition to the new slide + indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h ); + indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v ); + + layout(); + + // Apply the new state + stateLoop: for( var i = 0, len = state.length; i < len; i++ ) { + // Check if this state existed on the previous slide. If it + // did, we will avoid adding it repeatedly + for( var j = 0; j < stateBefore.length; j++ ) { + if( stateBefore[j] === state[i] ) { + stateBefore.splice( j, 1 ); + continue stateLoop; + } + } + + document.documentElement.classList.add( state[i] ); + + // Dispatch custom event matching the state's name + dispatchEvent( state[i] ); + } + + // Clean up the remains of the previous state + while( stateBefore.length ) { + document.documentElement.classList.remove( stateBefore.pop() ); + } + + // If the overview is active, re-activate it to update positions + if( isOverview() ) { + activateOverview(); + } + + // Update the URL hash after a delay since updating it mid-transition + // is likely to cause visual lag + writeURL( 1500 ); + + // Find the current horizontal slide and any possible vertical slides + // within it + var currentHorizontalSlide = horizontalSlides[ indexh ], + currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' ); + + // Store references to the previous and current slides + currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide; + + + // Show fragment, if specified + if( typeof f !== 'undefined' ) { + var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment' ) ); + + toArray( fragments ).forEach( function( fragment, indexf ) { + if( indexf < f ) { + fragment.classList.add( 'visible' ); + } + else { + fragment.classList.remove( 'visible' ); + } + } ); + } + + // Dispatch an event if the slide changed + if( indexh !== indexhBefore || indexv !== indexvBefore ) { + dispatchEvent( 'slidechanged', { + 'indexh': indexh, + 'indexv': indexv, + 'previousSlide': previousSlide, + 'currentSlide': currentSlide + } ); + } + else { + // Ensure that the previous slide is never the same as the current + previousSlide = null; + } + + // Solves an edge case where the previous slide maintains the + // 'present' class when navigating between adjacent vertical + // stacks + if( previousSlide ) { + previousSlide.classList.remove( 'present' ); + + // Reset all slides upon navigate to home + // Issue: #285 + if ( document.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) { + // Launch async task + setTimeout( function () { + var slides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i; + for( i in slides ) { + if( slides[i] ) { + // Reset stack + setPreviousVerticalIndex( slides[i], 0 ); + } + } + }, 0 ); + } + } + + updateControls(); + updateProgress(); + } /** * Updates one dimension of slides by showing the slide * with the specified index. - * + * * @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 * shown - * + * * @return {Number} The index of the slide that is now shown, - * might differ from the passed in index if it was out of + * might differ from the passed in index if it was out of * bounds. */ function updateSlides( selector, index ) { - + // Select all slides and convert the NodeList result to // an array - var slides = Array.prototype.slice.call( document.querySelectorAll( selector ) ), + var slides = toArray( document.querySelectorAll( selector ) ), slidesLength = slides.length; - + if( slidesLength ) { // Should the index loop? @@ -593,21 +1163,21 @@ var Reveal = (function(){ index = slidesLength + index; } } - + // Enforce max and minimum index bounds index = Math.max( Math.min( index, slidesLength - 1 ), 0 ); - - for( var i = 0; i < slidesLength; i++ ) { - var slide = slides[i]; - // Optimization; hide all slides that are three or more steps + for( var i = 0; i < slidesLength; i++ ) { + var element = slides[i]; + + // Optimization; hide all slides that are three or more steps // away from the present slide - if( overviewIsActive() === false ) { + if( isOverview() === false ) { // The distance loops so that it measures 1 between the first // and last slides var distance = Math.abs( ( index - i ) % ( slidesLength - 3 ) ) || 0; - slide.style.display = distance > 3 ? 'none' : 'block'; + element.style.display = distance > 3 ? 'none' : 'block'; } slides[i].classList.remove( 'past' ); @@ -624,7 +1194,7 @@ var Reveal = (function(){ } // If this element contains vertical slides - if( slide.querySelector( 'section' ) ) { + if( element.querySelector( 'section' ) ) { slides[i].classList.add( 'stack' ); } } @@ -638,138 +1208,118 @@ var Reveal = (function(){ if( slideState ) { state = state.concat( slideState.split( ' ' ) ); } + + // If this slide has a data-autoslide attribtue associated use this as + // autoSlide value otherwise use the global configured time + var slideAutoSlide = slides[index].getAttribute( 'data-autoslide' ); + if( slideAutoSlide ) { + autoSlide = parseInt( slideAutoSlide, 10 ); + } + else { + autoSlide = config.autoSlide; + } + } else { - // Since there are no slides we can't be anywhere beyond the + // Since there are no slides we can't be anywhere beyond the // zeroth index index = 0; } - + return index; - + } - + /** - * Updates the visual slides to represent the currently - * set indices. + * Updates the progress bar to reflect the current slide. */ - function slide( h, v, origin ) { - // Remember where we were at before - previousSlide = currentSlide; - - // Remember the state before this slide - var stateBefore = state.concat(); - - // Reset the state array - state.length = 0; - - var indexhBefore = indexh, - indexvBefore = indexv; - - // Activate and transition to the new slide - indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h ); - indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v ); - - // Apply the new state - stateLoop: for( var i = 0, len = state.length; i < len; i++ ) { - // Check if this state existed on the previous slide. If it - // did, we will avoid adding it repeatedly. - for( var j = 0; j < stateBefore.length; j++ ) { - if( stateBefore[j] === state[i] ) { - stateBefore.splice( j, 1 ); - continue stateLoop; - } - } - - document.documentElement.classList.add( state[i] ); - - // Dispatch custom event matching the state's name - dispatchEvent( state[i] ); - } - - // Clean up the remaints of the previous state - while( stateBefore.length ) { - document.documentElement.classList.remove( stateBefore.pop() ); - } + function updateProgress() { // Update progress if enabled if( config.progress && dom.progress ) { - dom.progressbar.style.width = ( indexh / ( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length - 1 ) ) * window.innerWidth + 'px'; + + var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + // The number of past and total slides + var totalCount = document.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length; + var pastCount = 0; + + // Step through all slides and count the past ones + mainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) { + + var horizontalSlide = horizontalSlides[i]; + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); + + for( var j = 0; j < verticalSlides.length; j++ ) { + + // Stop as soon as we arrive at the present + if( verticalSlides[j].classList.contains( 'present' ) ) { + break mainLoop; + } + + pastCount++; + + } + + // Stop as soon as we arrive at the present + if( horizontalSlide.classList.contains( 'present' ) ) { + break; + } + + // Don't count the wrapping section for vertical slides + if( horizontalSlide.classList.contains( 'stack' ) === false ) { + pastCount++; + } + + } + + dom.progressbar.style.width = ( pastCount / ( totalCount - 1 ) ) * window.innerWidth + 'px'; + } - // Close the overview if it's active - if( overviewIsActive() ) { - activateOverview(); - } - - updateControls(); - - clearTimeout( writeURLTimeout ); - writeURLTimeout = setTimeout( writeURL, 1500 ); - - // Query all horizontal slides in the deck - var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); - - // Find the current horizontal slide and any possible vertical slides - // within it - var currentHorizontalSlide = horizontalSlides[ indexh ], - currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' ); - - // Store references to the previous and current slides - currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide; - - // Dispatch an event if the slide changed - if( indexh !== indexhBefore || indexv !== indexvBefore ) { - dispatchEvent( 'slidechanged', { - 'origin': origin, - 'indexh': indexh, - 'indexv': indexv, - 'previousSlide': previousSlide, - 'currentSlide': currentSlide - } ); - } - else { - // Ensure that the previous slide is never the same as the current - previousSlide = null; - } - - // Solves an edge case where the previous slide maintains the - // 'present' class when navigating between adjacent vertical - // stacks - if( previousSlide ) { - previousSlide.classList.remove( 'present' ); - } } /** - * Updates the state and link pointers of the controls. + * Updates the state of all control/navigation arrows. */ function updateControls() { - if ( !config.controls || !dom.controls ) { - return; + + if ( config.controls && dom.controls ) { + + var routes = availableRoutes(); + + // Remove the 'enabled' class from all directions + dom.controlsLeft.concat( dom.controlsRight ) + .concat( dom.controlsUp ) + .concat( dom.controlsDown ) + .concat( dom.controlsPrev ) + .concat( dom.controlsNext ).forEach( function( node ) { + node.classList.remove( 'enabled' ); + } ); + + // 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' ); } ); + + // 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' ); } ); + } - - var routes = availableRoutes(); - // Remove the 'enabled' class from all directions - [ dom.controlsLeft, dom.controlsRight, dom.controlsUp, dom.controlsDown ].forEach( function( node ) { - node.classList.remove( 'enabled' ); - } ) - - if( routes.left ) dom.controlsLeft.classList.add( 'enabled' ); - if( routes.right ) dom.controlsRight.classList.add( 'enabled' ); - if( routes.up ) dom.controlsUp.classList.add( 'enabled' ); - if( routes.down ) dom.controlsDown.classList.add( 'enabled' ); } /** * Determine what available routes there are for navigation. - * + * * @return {Object} containing four booleans: left/right/up/down */ function availableRoutes() { - var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); - var verticalSlides = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); + + var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ), + verticalSlides = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); return { left: indexh > 0, @@ -777,60 +1327,132 @@ var Reveal = (function(){ up: indexv > 0, down: indexv < verticalSlides.length - 1 }; + } - + /** * Reads the current URL (hash) and navigates accordingly. */ function readURL() { - // Break the hash down to separate components - var bits = window.location.hash.slice(2).split('/'); - // Read the index components of the hash - var h = parseInt( bits[0] ) || 0 ; - var v = parseInt( bits[1] ) || 0 ; + var hash = window.location.hash; + + // Attempt to parse the hash as either an index or name + var bits = hash.slice( 2 ).split( '/' ), + name = hash.replace( /#|\//gi, '' ); + + // If the first bit is invalid and there is a name we can + // assume that this is a named link + if( isNaN( parseInt( bits[0], 10 ) ) && name.length ) { + // Find the slide with the specified name + var element = document.querySelector( '#' + name ); + + if( element ) { + // Find the position of the named slide and navigate to it + var indices = Reveal.getIndices( element ); + slide( indices.h, indices.v ); + } + // If the slide doesn't exist, navigate to the current slide + else { + slide( indexh, indexv ); + } + } + else { + // Read the index components of the hash + var h = parseInt( bits[0], 10 ) || 0, + v = parseInt( bits[1], 10 ) || 0; + + slide( h, v ); + } - navigateTo( h, v ); } - + /** * Updates the page URL (hash) to reflect the current - * state. + * state. + * + * @param {Number} delay The time in ms to wait before + * writing the hash */ - function writeURL() { + function writeURL( delay ) { + if( config.history ) { - var url = '/'; - - // Only include the minimum possible number of components in - // the URL - if( indexh > 0 || indexv > 0 ) url += indexh; - if( indexv > 0 ) url += '/' + indexv; - - window.location.hash = url; + + // Make sure there's never more than one timeout running + clearTimeout( writeURLTimeout ); + + // If a delay is specified, timeout this call + if( typeof delay === 'number' ) { + writeURLTimeout = setTimeout( writeURL, delay ); + } + else { + var url = '/'; + + // If the current slide has an ID, use that as a named link + if( currentSlide && typeof currentSlide.getAttribute( 'id' ) === 'string' ) { + url = '/' + currentSlide.getAttribute( 'id' ); + } + // Otherwise use the /h/v index + else { + if( indexh > 0 || indexv > 0 ) url += indexh; + if( indexv > 0 ) url += '/' + indexv; + } + + window.location.hash = url; + } } + } /** - * Dispatches an event of the specified type from the - * reveal DOM element. + * Retrieves the h/v location of the current, or specified, + * slide. + * + * @param {HTMLElement} slide If specified, the returned + * index will be for this slide rather than the currently + * active one + * + * @return {Object} { h: , v: } */ - function dispatchEvent( type, properties ) { - var event = document.createEvent( "HTMLEvents", 1, 2 ); - event.initEvent( type, true, true ); - extend( event, properties ); - dom.wrapper.dispatchEvent( event ); + function getIndices( slide ) { + + // By default, return the current indices + var h = indexh, + v = indexv; + + // If a slide is specified, return the indices of that slide + if( slide ) { + var isVertical = !!slide.parentNode.nodeName.match( /section/gi ); + var slideh = isVertical ? slide.parentNode : slide; + + // Select all horizontal slides + var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + // Now that we know which the horizontal slide is, get its index + h = Math.max( horizontalSlides.indexOf( slideh ), 0 ); + + // If this is a vertical slide, grab the vertical index + if( isVertical ) { + v = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 ); + } + } + + return { h: h, v: v }; + } /** * Navigate to the next slide fragment. - * + * * @return {Boolean} true if there was a next fragment, * false otherwise */ function nextFragment() { + // Vertical slides: if( document.querySelector( VERTICAL_SLIDES_SELECTOR + '.present' ) ) { - var verticalFragments = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR + '.present .fragment:not(.visible)' ); + var verticalFragments = sortFragments( document.querySelectorAll( VERTICAL_SLIDES_SELECTOR + '.present .fragment:not(.visible)' ) ); + if( verticalFragments.length ) { verticalFragments[0].classList.add( 'visible' ); @@ -841,7 +1463,8 @@ var Reveal = (function(){ } // Horizontal slides: else { - var horizontalFragments = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.present .fragment:not(.visible)' ); + var horizontalFragments = sortFragments( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.present .fragment:not(.visible)' ) ); + if( horizontalFragments.length ) { horizontalFragments[0].classList.add( 'visible' ); @@ -852,18 +1475,21 @@ var Reveal = (function(){ } return false; + } /** * Navigate to the previous slide fragment. - * + * * @return {Boolean} true if there was a previous fragment, * false otherwise */ function previousFragment() { + // Vertical slides: if( document.querySelector( VERTICAL_SLIDES_SELECTOR + '.present' ) ) { - var verticalFragments = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR + '.present .fragment.visible' ); + var verticalFragments = sortFragments( document.querySelectorAll( VERTICAL_SLIDES_SELECTOR + '.present .fragment.visible' ) ); + if( verticalFragments.length ) { verticalFragments[ verticalFragments.length - 1 ].classList.remove( 'visible' ); @@ -874,7 +1500,8 @@ var Reveal = (function(){ } // Horizontal slides: else { - var horizontalFragments = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.present .fragment.visible' ); + var horizontalFragments = sortFragments( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.present .fragment.visible' ) ); + if( horizontalFragments.length ) { horizontalFragments[ horizontalFragments.length - 1 ].classList.remove( 'visible' ); @@ -883,52 +1510,68 @@ var Reveal = (function(){ return true; } } - + return false; + } + /** + * Cues a new automated slide if enabled in the config. + */ function cueAutoSlide() { + clearTimeout( autoSlideTimeout ); // Cue the next auto-slide if enabled - if( config.autoSlide ) { - autoSlideTimeout = setTimeout( navigateNext, config.autoSlide ); + if( autoSlide && !isPaused() && !isOverview() ) { + autoSlideTimeout = setTimeout( navigateNext, autoSlide ); } + } - + /** - * Triggers a navigation to the specified indices. - * - * @param {Number} h The horizontal index of the slide to show - * @param {Number} v The vertical index of the slide to show + * Cancels any ongoing request to auto-slide. */ - function navigateTo( h, v, origin ) { - slide( h, v, origin ); + function cancelAutoSlide() { + + clearTimeout( autoSlideTimeout ); + } - + function navigateLeft() { + // Prioritize hiding fragments - if( overviewIsActive() || previousFragment() === false ) { - slide( indexh - 1, 0 ); + if( availableRoutes().left && isOverview() || previousFragment() === false ) { + slide( indexh - 1 ); } + } + function navigateRight() { + // Prioritize revealing fragments - if( overviewIsActive() || nextFragment() === false ) { - slide( indexh + 1, 0 ); + if( availableRoutes().right && isOverview() || nextFragment() === false ) { + slide( indexh + 1 ); } + } + function navigateUp() { + // Prioritize hiding fragments - if( overviewIsActive() || previousFragment() === false ) { + if( availableRoutes().up && isOverview() || previousFragment() === false ) { slide( indexh, indexv - 1 ); } + } + function navigateDown() { + // Prioritize revealing fragments - if( overviewIsActive() || nextFragment() === false ) { + if( availableRoutes().down && isOverview() || nextFragment() === false ) { slide( indexh, indexv + 1 ); } + } /** @@ -938,6 +1581,7 @@ var Reveal = (function(){ * 3) Previous horizontal slide */ function navigatePrev() { + // Prioritize revealing fragments if( previousFragment() === false ) { if( availableRoutes().up ) { @@ -945,75 +1589,388 @@ var Reveal = (function(){ } else { // Fetch the previous horizontal slide, if there is one - var previousSlide = document.querySelector( '.reveal .slides>section.past:nth-child(' + indexh + ')' ); + var previousSlide = document.querySelector( HORIZONTAL_SLIDES_SELECTOR + '.past:nth-child(' + indexh + ')' ); if( previousSlide ) { - indexv = ( previousSlide.querySelectorAll('section').length + 1 ) || 0; + indexv = ( previousSlide.querySelectorAll( 'section' ).length + 1 ) || undefined; indexh --; slide(); } } } + } /** * Same as #navigatePrev() but navigates forwards. */ function navigateNext() { + // Prioritize revealing fragments if( nextFragment() === false ) { availableRoutes().down ? navigateDown() : navigateRight(); } - // If auto-sliding is enabled we need to cue up + // If auto-sliding is enabled we need to cue up // another timeout cueAutoSlide(); + + } + + + // --------------------------------------------------------------------// + // ----------------------------- EVENTS -------------------------------// + // --------------------------------------------------------------------// + + + /** + * Handler for the document level 'keydown' event. + * + * @param {Object} event + */ + function onDocumentKeyDown( event ) { + + // Check if there's a focused element that could be using + // the keyboard + var activeElement = document.activeElement; + var hasFocus = !!( document.activeElement && ( document.activeElement.type || document.activeElement.href || document.activeElement.contentEditable !== 'inherit' ) ); + + // Disregard the event if there's a focused element or a + // keyboard modifier key is present + if( hasFocus || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return; + + var triggered = true; + + // while paused only allow "unpausing" keyboard events (b and .) + if( isPaused() && [66,190,191].indexOf( event.keyCode ) === -1 ) { + return false; + } + + switch( event.keyCode ) { + // p, page up + case 80: case 33: navigatePrev(); break; + // n, page down + case 78: case 34: navigateNext(); break; + // h, left + case 72: case 37: navigateLeft(); break; + // l, right + case 76: case 39: navigateRight(); break; + // k, up + case 75: case 38: navigateUp(); break; + // j, down + case 74: case 40: navigateDown(); break; + // home + case 36: slide( 0 ); break; + // end + case 35: slide( Number.MAX_VALUE ); break; + // space + case 32: isOverview() ? deactivateOverview() : navigateNext(); break; + // return + case 13: isOverview() ? deactivateOverview() : triggered = false; break; + // b, period, Logitech presenter tools "black screen" button + case 66: case 190: case 191: togglePause(); break; + // f + case 70: enterFullscreen(); break; + default: + triggered = false; + } + + // If the input resulted in a triggered action we should prevent + // the browsers default behavior + if( triggered ) { + event.preventDefault(); + } + else if ( event.keyCode === 27 && supports3DTransforms ) { + toggleOverview(); + + event.preventDefault(); + } + + // If auto-sliding is enabled we need to cue up + // another timeout + cueAutoSlide(); + } /** - * Toggles the slide overview mode on and off. + * Handler for the document level 'touchstart' event, + * enables support for swipe and pinch gestures. */ - function toggleOverview() { - if( overviewIsActive() ) { - deactivateOverview(); - } - else { - activateOverview(); + function onDocumentTouchStart( event ) { + + touch.startX = event.touches[0].clientX; + touch.startY = event.touches[0].clientY; + touch.startCount = event.touches.length; + + // If there's two touches we need to memorize the distance + // between those two points to detect pinching + if( event.touches.length === 2 && config.overview ) { + touch.startSpan = distanceBetween( { + x: event.touches[1].clientX, + y: event.touches[1].clientY + }, { + x: touch.startX, + y: touch.startY + } ); } + } - - // Expose some methods publicly + + /** + * Handler for the document level 'touchmove' event. + */ + function onDocumentTouchMove( event ) { + + // Each touch should only trigger one action + if( !touch.handled ) { + var currentX = event.touches[0].clientX; + var currentY = event.touches[0].clientY; + + // If the touch started off with two points and still has + // two active touches; test for the pinch gesture + if( event.touches.length === 2 && touch.startCount === 2 && config.overview ) { + + // The current distance in pixels between the two touch points + var currentSpan = distanceBetween( { + x: event.touches[1].clientX, + y: event.touches[1].clientY + }, { + x: touch.startX, + y: touch.startY + } ); + + // If the span is larger than the desire amount we've got + // ourselves a pinch + if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) { + touch.handled = true; + + if( currentSpan < touch.startSpan ) { + activateOverview(); + } + else { + deactivateOverview(); + } + } + + event.preventDefault(); + + } + // There was only one touch point, look for a swipe + else if( event.touches.length === 1 && touch.startCount !== 2 ) { + + var deltaX = currentX - touch.startX, + deltaY = currentY - touch.startY; + + if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) { + touch.handled = true; + navigateLeft(); + } + else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) { + touch.handled = true; + navigateRight(); + } + else if( deltaY > touch.threshold ) { + touch.handled = true; + navigateUp(); + } + else if( deltaY < -touch.threshold ) { + touch.handled = true; + navigateDown(); + } + + event.preventDefault(); + + } + } + // There's a bug with swiping on some Android devices unless + // the default action is always prevented + else if( navigator.userAgent.match( /android/gi ) ) { + event.preventDefault(); + } + + } + + /** + * Handler for the document level 'touchend' event. + */ + function onDocumentTouchEnd( event ) { + + touch.handled = false; + + } + + /** + * Handles mouse wheel scrolling, throttled to avoid skipping + * multiple slides. + */ + function onDocumentMouseScroll( event ) { + + clearTimeout( mouseWheelTimeout ); + + mouseWheelTimeout = setTimeout( function() { + var delta = event.detail || -event.wheelDelta; + if( delta > 0 ) { + navigateNext(); + } + else { + navigatePrev(); + } + }, 100 ); + + } + + /** + * Clicking on the progress bar results in a navigation to the + * closest approximate horizontal slide using this equation: + * + * ( clickX / presentationWidth ) * numberOfSlides + */ + function onProgressClicked( event ) { + + event.preventDefault(); + + var slidesTotal = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).length; + var slideIndex = Math.floor( ( event.clientX / dom.wrapper.offsetWidth ) * slidesTotal ); + + slide( slideIndex ); + + } + + /** + * Event handler for navigation control buttons. + */ + function onNavigateLeftClicked( event ) { event.preventDefault(); navigateLeft(); } + function onNavigateRightClicked( event ) { event.preventDefault(); navigateRight(); } + function onNavigateUpClicked( event ) { event.preventDefault(); navigateUp(); } + function onNavigateDownClicked( event ) { event.preventDefault(); navigateDown(); } + function onNavigatePrevClicked( event ) { event.preventDefault(); navigatePrev(); } + function onNavigateNextClicked( event ) { event.preventDefault(); navigateNext(); } + + /** + * Handler for the window level 'hashchange' event. + */ + function onWindowHashChange( event ) { + + readURL(); + + } + + /** + * Handler for the window level 'resize' event. + */ + function onWindowResize( event ) { + + layout(); + + } + + /** + * Invoked when a slide is and we're in the overview. + */ + function onOverviewSlideClicked( event ) { + + // TODO There's a bug here where the event listeners are not + // removed after deactivating the overview. + if( eventsAreBound && isOverview() ) { + event.preventDefault(); + + var element = event.target; + + while( element && !element.nodeName.match( /section/gi ) ) { + element = element.parentNode; + } + + if( element && !element.classList.contains( 'disabled' ) ) { + + deactivateOverview(); + + if( element.nodeName.match( /section/gi ) ) { + var h = parseInt( element.getAttribute( 'data-index-h' ), 10 ), + v = parseInt( element.getAttribute( 'data-index-v' ), 10 ); + + slide( h, v ); + } + + } + } + + } + + + // --------------------------------------------------------------------// + // ------------------------------- API --------------------------------// + // --------------------------------------------------------------------// + + return { initialize: initialize, - navigateTo: navigateTo, + configure: configure, + + // Navigation methods + slide: slide, + left: navigateLeft, + right: navigateRight, + up: navigateUp, + down: navigateDown, + prev: navigatePrev, + next: navigateNext, + prevFragment: previousFragment, + nextFragment: nextFragment, + + // Deprecated aliases + navigateTo: slide, navigateLeft: navigateLeft, navigateRight: navigateRight, navigateUp: navigateUp, navigateDown: navigateDown, navigatePrev: navigatePrev, navigateNext: navigateNext, + + // Forces an update in slide layout + layout: layout, + + // Toggles the overview mode on/off toggleOverview: toggleOverview, + // Toggles the "black screen" mode on/off + togglePause: togglePause, + + // State checks + isOverview: isOverview, + isPaused: isPaused, + // Adds or removes all internal event listeners (such as keyboard) addEventListeners: addEventListeners, removeEventListeners: removeEventListeners, - // Returns the indices of the current slide - getIndices: function() { - return { - h: indexh, - v: indexv - }; + // Returns the indices of the current, or specified, slide + getIndices: getIndices, + + // Returns the slide at the specified index, y is optional + getSlide: function( x, y ) { + var horizontalSlide = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR )[ x ]; + var verticalSlides = horizontalSlide && horizontalSlide.querySelectorAll( 'section' ); + + if( typeof y !== 'undefined' ) { + return verticalSlides ? verticalSlides[ y ] : undefined; + } + + return horizontalSlide; }, // Returns the previous slide element, may be null getPreviousSlide: function() { - return previousSlide + return previousSlide; }, // Returns the current slide element getCurrentSlide: function() { - return currentSlide + return currentSlide; + }, + + // Returns the current scale of the presentation content + getScale: function() { + return scale; }, // Helper method, retrieves query string as a key/value hash @@ -1027,6 +1984,21 @@ var Reveal = (function(){ return query; }, + // Returns true if we're currently on the first slide + isFirstSlide: function() { + return document.querySelector( SLIDES_SELECTOR + '.past' ) == null ? true : false; + }, + + // Returns true if we're currently on the last slide + isLastSlide: function() { + if( currentSlide && currentSlide.classList.contains( '.stack' ) ) { + return currentSlide.querySelector( SLIDES_SELECTOR + '.future' ) == null ? true : false; + } + else { + return document.querySelector( SLIDES_SELECTOR + '.future' ) == null ? true : false; + } + }, + // Forward event binding to the reveal DOM element addEventListener: function( type, listener, useCapture ) { if( 'addEventListener' in window ) { @@ -1039,6 +2011,5 @@ var Reveal = (function(){ } } }; - -})(); +})(); diff --git a/js/reveal.min.js b/js/reveal.min.js new file mode 100644 index 0000000..98019d6 --- /dev/null +++ b/js/reveal.min.js @@ -0,0 +1,8 @@ +/*! + * reveal.js 2.3.0 (2013-02-28, 17:03) + * http://lab.hakim.se/reveal-js + * MIT licensed + * + * Copyright (C) 2013 Hakim El Hattab, http://hakim.se + */ +var Reveal=function(){"use strict";function e(e){return bt||wt?(window.addEventListener("load",h,!1),c(ft,e),n(),r(),void 0):(document.body.setAttribute("class","no-transforms"),void 0)}function t(){if(Lt.theme=document.querySelector("#theme"),Lt.wrapper=document.querySelector(".reveal"),Lt.slides=document.querySelector(".reveal .slides"),!Lt.wrapper.querySelector(".progress")&&ft.progress){var e=document.createElement("div");e.classList.add("progress"),e.innerHTML="",Lt.wrapper.appendChild(e)}if(!Lt.wrapper.querySelector(".controls")&&ft.controls){var t=document.createElement("aside");t.classList.add("controls"),t.innerHTML='',Lt.wrapper.appendChild(t)}if(!Lt.wrapper.querySelector(".state-background")){var n=document.createElement("div");n.classList.add("state-background"),Lt.wrapper.appendChild(n)}if(!Lt.wrapper.querySelector(".pause-overlay")){var r=document.createElement("div");r.classList.add("pause-overlay"),Lt.wrapper.appendChild(r)}Lt.progress=document.querySelector(".reveal .progress"),Lt.progressbar=document.querySelector(".reveal .progress span"),ft.controls&&(Lt.controls=document.querySelector(".reveal .controls"),Lt.controlsLeft=l(document.querySelectorAll(".navigate-left")),Lt.controlsRight=l(document.querySelectorAll(".navigate-right")),Lt.controlsUp=l(document.querySelectorAll(".navigate-up")),Lt.controlsDown=l(document.querySelectorAll(".navigate-down")),Lt.controlsPrev=l(document.querySelectorAll(".navigate-prev")),Lt.controlsNext=l(document.querySelectorAll(".navigate-next")))}function n(){/iphone|ipod|android/gi.test(navigator.userAgent)&&!/crios/gi.test(navigator.userAgent)&&(window.addEventListener("load",u,!1),window.addEventListener("orientationchange",u,!1))}function r(){function e(){n.length&&head.js.apply(null,n),o()}for(var t=[],n=[],r=0,s=ft.dependencies.length;s>r;r++){var a=ft.dependencies[r];(!a.condition||a.condition())&&(a.async?n.push(a.src):t.push(a.src),"function"==typeof a.callback&&head.ready(a.src.match(/([\w\d_\-]*)\.?js$|[^\\\/]*$/i)[0],a.callback))}t.length?(head.ready(e),head.js.apply(null,t)):e()}function o(){t(),a(),s(),P(),R(),setTimeout(function(){v("ready",{indexh:pt,indexv:ht,currentSlide:ct})},1)}function s(e){if(Lt.wrapper.classList.remove(ft.transition),"object"==typeof e&&c(ft,e),wt===!1&&(ft.transition="linear"),Lt.wrapper.classList.add(ft.transition),Lt.controls&&(Lt.controls.style.display=ft.controls&&Lt.controls?"block":"none"),Lt.progress&&(Lt.progress.style.display=ft.progress&&Lt.progress?"block":"none"),ft.rtl?Lt.wrapper.classList.add("rtl"):Lt.wrapper.classList.remove("rtl"),ft.center?Lt.wrapper.classList.add("center"):Lt.wrapper.classList.remove("center"),ft.mouseWheel?(document.addEventListener("DOMMouseScroll",V,!1),document.addEventListener("mousewheel",V,!1)):(document.removeEventListener("DOMMouseScroll",V,!1),document.removeEventListener("mousewheel",V,!1)),ft.rollingLinks?f():m(),ft.theme&&Lt.theme){var t=Lt.theme.getAttribute("href"),n=/[^\/]*?(?=\.css)/,r=t.match(n)[0];ft.theme!==r&&(t=t.replace(n,ft.theme),Lt.theme.setAttribute("href",t))}h()}function a(){if(xt=!0,window.addEventListener("hashchange",ot,!1),window.addEventListener("resize",st,!1),ft.touch&&(document.addEventListener("touchstart",Z,!1),document.addEventListener("touchmove",_,!1),document.addEventListener("touchend",Q,!1)),ft.keyboard&&document.addEventListener("keydown",$,!1),ft.progress&&Lt.progress&&Lt.progress.addEventListener("click",B,!1),ft.controls&&Lt.controls){var e="ontouchstart"in window&&null!=window.ontouchstart?"touchstart":"click";Lt.controlsLeft.forEach(function(t){t.addEventListener(e,G,!1)}),Lt.controlsRight.forEach(function(t){t.addEventListener(e,J,!1)}),Lt.controlsUp.forEach(function(t){t.addEventListener(e,et,!1)}),Lt.controlsDown.forEach(function(t){t.addEventListener(e,tt,!1)}),Lt.controlsPrev.forEach(function(t){t.addEventListener(e,nt,!1)}),Lt.controlsNext.forEach(function(t){t.addEventListener(e,rt,!1)})}}function i(){if(xt=!1,document.removeEventListener("keydown",$,!1),window.removeEventListener("hashchange",ot,!1),window.removeEventListener("resize",st,!1),ft.touch&&(document.removeEventListener("touchstart",Z,!1),document.removeEventListener("touchmove",_,!1),document.removeEventListener("touchend",Q,!1)),ft.progress&&Lt.progress&&Lt.progress.removeEventListener("click",B,!1),ft.controls&&Lt.controls){var e="ontouchstart"in window&&null!=window.ontouchstart?"touchstart":"click";Lt.controlsLeft.forEach(function(t){t.removeEventListener(e,G,!1)}),Lt.controlsRight.forEach(function(t){t.removeEventListener(e,J,!1)}),Lt.controlsUp.forEach(function(t){t.removeEventListener(e,et,!1)}),Lt.controlsDown.forEach(function(t){t.removeEventListener(e,tt,!1)}),Lt.controlsPrev.forEach(function(t){t.removeEventListener(e,nt,!1)}),Lt.controlsNext.forEach(function(t){t.removeEventListener(e,rt,!1)})}}function c(e,t){for(var n in t)e[n]=t[n]}function l(e){return Array.prototype.slice.call(e)}function d(e,t){var n=e.x-t.x,r=e.y-t.y;return Math.sqrt(n*n+r*r)}function u(){0===window.orientation?(document.documentElement.style.overflow="scroll",document.body.style.height="120%"):(document.documentElement.style.overflow="",document.body.style.height="100%"),setTimeout(function(){window.scrollTo(0,1)},10)}function v(e,t){var n=document.createEvent("HTMLEvents",1,2);n.initEvent(e,!0,!0),c(n,t),Lt.wrapper.dispatchEvent(n)}function f(){if(wt&&!("msPerspective"in document.body.style))for(var e=document.querySelectorAll(lt+" a:not(.image)"),t=0,n=e.length;n>t;t++){var r=e[t];if(!(!r.textContent||r.querySelector("*")||r.className&&r.classList.contains(r,"roll"))){var o=document.createElement("span");o.setAttribute("data-title",r.text),o.innerHTML=r.innerHTML,r.classList.add("roll"),r.innerHTML="",r.appendChild(o)}}}function m(){for(var e=document.querySelectorAll(lt+" a.roll"),t=0,n=e.length;n>t;t++){var r=e[t],o=r.querySelector("span");o&&(r.classList.remove("roll"),r.innerHTML=o.innerHTML)}}function p(e){var t=l(e);return t.forEach(function(e,t){e.hasAttribute("data-fragment-index")||e.setAttribute("data-fragment-index",t)}),t.sort(function(e,t){return e.getAttribute("data-fragment-index")-t.getAttribute("data-fragment-index")}),t}function h(){if(Lt.wrapper){var e=Lt.wrapper.offsetWidth,t=Lt.wrapper.offsetHeight;e-=t*ft.margin,t-=t*ft.margin;var n=ft.width,r=ft.height;if("string"==typeof n&&/%$/.test(n)&&(n=parseInt(n,10)/100*e),"string"==typeof r&&/%$/.test(r)&&(r=parseInt(r,10)/100*t),Lt.slides.style.width=n+"px",Lt.slides.style.height=r+"px",yt=Math.min(e/n,t/r),yt=Math.max(yt,ft.minScale),yt=Math.min(yt,ft.maxScale),void 0===Lt.slides.style.zoom||navigator.userAgent.match(/(iphone|ipod|ipad|android)/gi)){var o="translate(-50%, -50%) scale("+yt+") translate(50%, 50%)";Lt.slides.style.WebkitTransform=o,Lt.slides.style.MozTransform=o,Lt.slides.style.msTransform=o,Lt.slides.style.OTransform=o,Lt.slides.style.transform=o}else Lt.slides.style.zoom=yt;for(var s=l(document.querySelectorAll(lt)),a=0,i=s.length;i>a;a++){var c=s[a];"none"!==c.style.display&&(c.style.top=ft.center?c.classList.contains("stack")?0:Math.max(-(c.offsetHeight/2)-20,-r/2)+"px":"")}}}function g(e,t){e&&e.setAttribute("data-previous-indexv",t||0)}function y(e){return e&&e.classList.contains("stack")?parseInt(e.getAttribute("data-previous-indexv")||0,10):0}function L(){if(ft.overview){W();var e=Lt.wrapper.classList.contains("overview");Lt.wrapper.classList.add("overview"),Lt.wrapper.classList.remove("exit-overview"),clearTimeout(At),clearTimeout(kt),At=setTimeout(function(){for(var t=document.querySelectorAll(dt),n=0,r=t.length;r>n;n++){var o=t[n],s="translateZ(-2500px) translate("+105*(n-pt)+"%, 0%)";if(o.setAttribute("data-index-h",n),o.style.display="block",o.style.WebkitTransform=s,o.style.MozTransform=s,o.style.msTransform=s,o.style.OTransform=s,o.style.transform=s,o.classList.contains("stack"))for(var a=o.querySelectorAll("section"),i=0,c=a.length;c>i;i++){var l=n===pt?ht:y(o),d=a[i],u="translate(0%, "+105*(i-l)+"%)";d.setAttribute("data-index-h",n),d.setAttribute("data-index-v",i),d.style.display="block",d.style.WebkitTransform=u,d.style.MozTransform=u,d.style.msTransform=u,d.style.OTransform=u,d.style.transform=u,d.addEventListener("click",at,!0)}else o.addEventListener("click",at,!0)}h(),e||v("overviewshown",{indexh:pt,indexv:ht,currentSlide:ct})},10)}}function w(){if(ft.overview){clearTimeout(At),clearTimeout(kt),Lt.wrapper.classList.remove("overview"),Lt.wrapper.classList.add("exit-overview"),kt=setTimeout(function(){Lt.wrapper.classList.remove("exit-overview")},10);for(var e=l(document.querySelectorAll(lt)),t=0,n=e.length;n>t;t++){var r=e[t];r.style.display="",r.style.WebkitTransform="",r.style.MozTransform="",r.style.msTransform="",r.style.OTransform="",r.style.transform="",r.removeEventListener("click",at,!0)}T(pt,ht),R(),v("overviewhidden",{indexh:pt,indexv:ht,currentSlide:ct})}}function b(e){"boolean"==typeof e?e?L():w():E()?w():L()}function E(){return Lt.wrapper.classList.contains("overview")}function S(){var e=document.body,t=e.requestFullScreen||e.webkitRequestFullScreen||e.mozRequestFullScreen||e.msRequestFullScreen;t&&t.apply(e)}function q(){var e=Lt.wrapper.classList.contains("paused");W(),Lt.wrapper.classList.add("paused"),e===!1&&v("paused")}function A(){var e=Lt.wrapper.classList.contains("paused");R(),Lt.wrapper.classList.remove("paused"),e&&v("resumed")}function k(){x()?A():q()}function x(){return Lt.wrapper.classList.contains("paused")}function T(e,t,n){it=ct;var r=document.querySelectorAll(dt);void 0===t&&(t=y(r[e])),it&&it.parentNode&&it.parentNode.classList.contains("stack")&&g(it.parentNode,ht);var o=gt.concat();gt.length=0;var s=pt,a=ht;pt=M(dt,void 0===e?pt:e),ht=M(ut,void 0===t?ht:t),h();e:for(var i=0,c=gt.length;c>i;i++){for(var d=0;o.length>d;d++)if(o[d]===gt[i]){o.splice(d,1);continue e}document.documentElement.classList.add(gt[i]),v(gt[i])}for(;o.length;)document.documentElement.classList.remove(o.pop());E()&&L(),O(1500);var u=r[pt],f=u.querySelectorAll("section");if(ct=f[ht]||u,n!==void 0){var m=p(ct.querySelectorAll(".fragment"));l(m).forEach(function(e,t){n>t?e.classList.add("visible"):e.classList.remove("visible")})}pt!==s||ht!==a?v("slidechanged",{indexh:pt,indexv:ht,previousSlide:it,currentSlide:ct}):it=null,it&&(it.classList.remove("present"),document.querySelector(vt).classList.contains("present")&&setTimeout(function(){var e,t=l(document.querySelectorAll(dt+".stack"));for(e in t)t[e]&&g(t[e],0)},0)),N(),D()}function M(e,t){var n=l(document.querySelectorAll(e)),r=n.length;if(r){ft.loop&&(t%=r,0>t&&(t=r+t)),t=Math.max(Math.min(t,r-1),0);for(var o=0;r>o;o++){var s=n[o];if(E()===!1){var a=Math.abs((t-o)%(r-3))||0;s.style.display=a>3?"none":"block"}n[o].classList.remove("past"),n[o].classList.remove("present"),n[o].classList.remove("future"),t>o?n[o].classList.add("past"):o>t&&n[o].classList.add("future"),s.querySelector("section")&&n[o].classList.add("stack")}n[t].classList.add("present");var i=n[t].getAttribute("data-state");i&&(gt=gt.concat(i.split(" ")));var c=n[t].getAttribute("data-autoslide");mt=c?parseInt(c,10):ft.autoSlide}else t=0;return t}function D(){if(ft.progress&&Lt.progress){var e=l(document.querySelectorAll(dt)),t=document.querySelectorAll(lt+":not(.stack)").length,n=0;e:for(var r=0;e.length>r;r++){for(var o=e[r],s=l(o.querySelectorAll("section")),a=0;s.length>a;a++){if(s[a].classList.contains("present"))break e;n++}if(o.classList.contains("present"))break;o.classList.contains("stack")===!1&&n++}Lt.progressbar.style.width=n/(t-1)*window.innerWidth+"px"}}function N(){if(ft.controls&&Lt.controls){var e=C();Lt.controlsLeft.concat(Lt.controlsRight).concat(Lt.controlsUp).concat(Lt.controlsDown).concat(Lt.controlsPrev).concat(Lt.controlsNext).forEach(function(e){e.classList.remove("enabled")}),e.left&&Lt.controlsLeft.forEach(function(e){e.classList.add("enabled")}),e.right&&Lt.controlsRight.forEach(function(e){e.classList.add("enabled")}),e.up&&Lt.controlsUp.forEach(function(e){e.classList.add("enabled")}),e.down&&Lt.controlsDown.forEach(function(e){e.classList.add("enabled")}),(e.left||e.up)&&Lt.controlsPrev.forEach(function(e){e.classList.add("enabled")}),(e.right||e.down)&&Lt.controlsNext.forEach(function(e){e.classList.add("enabled")})}}function C(){var e=document.querySelectorAll(dt),t=document.querySelectorAll(ut);return{left:pt>0,right:e.length-1>pt,up:ht>0,down:t.length-1>ht}}function P(){var e=window.location.hash,t=e.slice(2).split("/"),n=e.replace(/#|\//gi,"");if(isNaN(parseInt(t[0],10))&&n.length){var r=document.querySelector("#"+n);if(r){var o=Reveal.getIndices(r);T(o.h,o.v)}else T(pt,ht)}else{var s=parseInt(t[0],10)||0,a=parseInt(t[1],10)||0;T(s,a)}}function O(e){if(ft.history)if(clearTimeout(qt),"number"==typeof e)qt=setTimeout(O,e);else{var t="/";ct&&"string"==typeof ct.getAttribute("id")?t="/"+ct.getAttribute("id"):((pt>0||ht>0)&&(t+=pt),ht>0&&(t+="/"+ht)),window.location.hash=t}}function z(e){var t=pt,n=ht;if(e){var r=!!e.parentNode.nodeName.match(/section/gi),o=r?e.parentNode:e,s=l(document.querySelectorAll(dt));t=Math.max(s.indexOf(o),0),r&&(n=Math.max(l(e.parentNode.querySelectorAll("section")).indexOf(e),0))}return{h:t,v:n}}function H(){if(document.querySelector(ut+".present")){var e=p(document.querySelectorAll(ut+".present .fragment:not(.visible)"));if(e.length)return e[0].classList.add("visible"),v("fragmentshown",{fragment:e[0]}),!0}else{var t=p(document.querySelectorAll(dt+".present .fragment:not(.visible)"));if(t.length)return t[0].classList.add("visible"),v("fragmentshown",{fragment:t[0]}),!0}return!1}function I(){if(document.querySelector(ut+".present")){var e=p(document.querySelectorAll(ut+".present .fragment.visible"));if(e.length)return e[e.length-1].classList.remove("visible"),v("fragmenthidden",{fragment:e[e.length-1]}),!0}else{var t=p(document.querySelectorAll(dt+".present .fragment.visible"));if(t.length)return t[t.length-1].classList.remove("visible"),v("fragmenthidden",{fragment:t[t.length-1]}),!0}return!1}function R(){clearTimeout(St),!mt||x()||E()||(St=setTimeout(K,mt))}function W(){clearTimeout(St)}function X(){(C().left&&E()||I()===!1)&&T(pt-1)}function Y(){(C().right&&E()||H()===!1)&&T(pt+1)}function F(){(C().up&&E()||I()===!1)&&T(pt,ht-1)}function U(){(C().down&&E()||H()===!1)&&T(pt,ht+1)}function j(){if(I()===!1)if(C().up)F();else{var e=document.querySelector(dt+".past:nth-child("+pt+")");e&&(ht=e.querySelectorAll("section").length+1||void 0,pt--,T())}}function K(){H()===!1&&(C().down?U():Y()),R()}function $(e){document.activeElement;var t=!(!document.activeElement||!document.activeElement.type&&!document.activeElement.href&&"inherit"===document.activeElement.contentEditable);if(!(t||e.shiftKey||e.altKey||e.ctrlKey||e.metaKey)){var n=!0;if(x()&&-1===[66,190,191].indexOf(e.keyCode))return!1;switch(e.keyCode){case 80:case 33:j();break;case 78:case 34:K();break;case 72:case 37:X();break;case 76:case 39:Y();break;case 75:case 38:F();break;case 74:case 40:U();break;case 36:T(0);break;case 35:T(Number.MAX_VALUE);break;case 32:E()?w():K();break;case 13:E()?w():n=!1;break;case 66:case 190:case 191:k();break;case 70:S();break;default:n=!1}n?e.preventDefault():27===e.keyCode&&wt&&(b(),e.preventDefault()),R()}}function Z(e){Tt.startX=e.touches[0].clientX,Tt.startY=e.touches[0].clientY,Tt.startCount=e.touches.length,2===e.touches.length&&ft.overview&&(Tt.startSpan=d({x:e.touches[1].clientX,y:e.touches[1].clientY},{x:Tt.startX,y:Tt.startY}))}function _(e){if(Tt.handled)navigator.userAgent.match(/android/gi)&&e.preventDefault();else{var t=e.touches[0].clientX,n=e.touches[0].clientY;if(2===e.touches.length&&2===Tt.startCount&&ft.overview){var r=d({x:e.touches[1].clientX,y:e.touches[1].clientY},{x:Tt.startX,y:Tt.startY});Math.abs(Tt.startSpan-r)>Tt.threshold&&(Tt.handled=!0,Tt.startSpan>r?L():w()),e.preventDefault()}else if(1===e.touches.length&&2!==Tt.startCount){var o=t-Tt.startX,s=n-Tt.startY;o>Tt.threshold&&Math.abs(o)>Math.abs(s)?(Tt.handled=!0,X()):-Tt.threshold>o&&Math.abs(o)>Math.abs(s)?(Tt.handled=!0,Y()):s>Tt.threshold?(Tt.handled=!0,F()):-Tt.threshold>s&&(Tt.handled=!0,U()),e.preventDefault()}}}function Q(){Tt.handled=!1}function V(e){clearTimeout(Et),Et=setTimeout(function(){var t=e.detail||-e.wheelDelta;t>0?K():j()},100)}function B(e){e.preventDefault();var t=l(document.querySelectorAll(dt)).length,n=Math.floor(e.clientX/Lt.wrapper.offsetWidth*t);T(n)}function G(e){e.preventDefault(),X()}function J(e){e.preventDefault(),Y()}function et(e){e.preventDefault(),F()}function tt(e){e.preventDefault(),U()}function nt(e){e.preventDefault(),j()}function rt(e){e.preventDefault(),K()}function ot(){P()}function st(){h()}function at(e){if(xt&&E()){e.preventDefault();for(var t=e.target;t&&!t.nodeName.match(/section/gi);)t=t.parentNode;if(t&&!t.classList.contains("disabled")&&(w(),t.nodeName.match(/section/gi))){var n=parseInt(t.getAttribute("data-index-h"),10),r=parseInt(t.getAttribute("data-index-v"),10);T(n,r)}}}var it,ct,lt=".reveal .slides section",dt=".reveal .slides>section",ut=".reveal .slides>section.present>section",vt=".reveal .slides>section:first-child",ft={width:960,height:700,margin:.1,minScale:.2,maxScale:1,controls:!0,progress:!0,history:!1,keyboard:!0,overview:!0,center:!0,touch:!0,loop:!1,rtl:!1,autoSlide:0,mouseWheel:!1,rollingLinks:!0,theme:null,transition:"default",dependencies:[]},mt=ft.autoSlide,pt=0,ht=0,gt=[],yt=1,Lt={},wt="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,bt="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,Et=0,St=0,qt=0,At=0,kt=0,xt=!1,Tt={startX:0,startY:0,startSpan:0,startCount:0,handled:!1,threshold:80};return{initialize:e,configure:s,slide:T,left:X,right:Y,up:F,down:U,prev:j,next:K,prevFragment:I,nextFragment:H,navigateTo:T,navigateLeft:X,navigateRight:Y,navigateUp:F,navigateDown:U,navigatePrev:j,navigateNext:K,layout:h,toggleOverview:b,togglePause:k,isOverview:E,isPaused:x,addEventListeners:a,removeEventListeners:i,getIndices:z,getSlide:function(e,t){var n=document.querySelectorAll(dt)[e],r=n&&n.querySelectorAll("section");return t!==void 0?r?r[t]:void 0:n},getPreviousSlide:function(){return it},getCurrentSlide:function(){return ct},getScale:function(){return yt},getQueryHash:function(){var e={};return location.search.replace(/[A-Z0-9]+?=(\w*)/gi,function(t){e[t.split("=").shift()]=t.split("=").pop()}),e},isFirstSlide:function(){return null==document.querySelector(lt+".past")?!0:!1},isLastSlide:function(){return ct&&ct.classList.contains(".stack")?null==ct.querySelector(lt+".future")?!0:!1:null==document.querySelector(lt+".future")?!0:!1},addEventListener:function(e,t,n){"addEventListener"in window&&(Lt.wrapper||document.querySelector(".reveal")).addEventListener(e,t,n)},removeEventListener:function(e,t,n){"addEventListener"in window&&(Lt.wrapper||document.querySelector(".reveal")).removeEventListener(e,t,n)}}}(); \ No newline at end of file diff --git a/lib/font/league_gothic-webfont.svg b/lib/font/league_gothic-webfont.svg new file mode 100644 index 0000000..201cfe1 --- /dev/null +++ b/lib/font/league_gothic-webfont.svg @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/font/league_gothic-webfont.woff b/lib/font/league_gothic-webfont.woff new file mode 100644 index 0000000..71117fb Binary files /dev/null and b/lib/font/league_gothic-webfont.woff differ diff --git a/lib/js/data-markdown.js b/lib/js/data-markdown.js deleted file mode 100644 index b10592a..0000000 --- a/lib/js/data-markdown.js +++ /dev/null @@ -1,27 +0,0 @@ -// From https://gist.github.com/1343518 -// Modified by Hakim to handle markdown indented with tabs -(function(){ - - var slides = document.querySelectorAll('[data-markdown]'); - - for( var i = 0, len = slides.length; i < len; i++ ) { - var elem = slides[i]; - - // strip leading whitespace so it isn't evaluated as code - var text = elem.innerHTML; - - var leadingWs = text.match(/^\n?(\s*)/)[1].length, - leadingTabs = text.match(/^\n?(\t*)/)[1].length; - - if( leadingTabs > 0 ) { - text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' ); - } - else if( leadingWs > 1 ) { - text = text.replace( new RegExp('\\n? {' + leadingWs + '}','g'), '\n' ); - } - - // here, have sum HTML - elem.innerHTML = (new Showdown.converter()).makeHtml(text); - } - -})(); \ No newline at end of file diff --git a/lib/js/highlight.js b/lib/js/highlight.js deleted file mode 100644 index 12d24df..0000000 --- a/lib/js/highlight.js +++ /dev/null @@ -1,5 +0,0 @@ -/* -Syntax highlighting with language autodetection. -http://softwaremaniacs.org/soft/highlight/ -*/ -var hljs=new function(){function m(p){return p.replace(/&/gm,"&").replace(/"}while(y.length||z.length){var v=u().splice(0,1)[0];w+=m(x.substr(r,v.offset-r));r=v.offset;if(v.event=="start"){w+=s(v.node);t.push(v.node)}else{if(v.event=="stop"){var q=t.length;do{q--;var p=t[q];w+=("")}while(p!=v.node);t.splice(q,1);while(q'+m(L[0])+""}else{N+=m(L[0])}P=O.lR.lastIndex;L=O.lR.exec(M)}N+=m(M.substr(P,M.length-P));return N}function K(r,M){if(M.sL&&d[M.sL]){var L=e(M.sL,r);t+=L.keyword_count;return L.value}else{return F(r,M)}}function I(M,r){var L=M.cN?'':"";if(M.rB){q+=L;M.buffer=""}else{if(M.eB){q+=m(r)+L;M.buffer=""}else{q+=L;M.buffer=r}}C.push(M);B+=M.r}function E(O,L,Q){var R=C[C.length-1];if(Q){q+=K(R.buffer+O,R);return false}var M=z(L,R);if(M){q+=K(R.buffer+O,R);I(M,L);return M.rB}var r=w(C.length-1,L);if(r){var N=R.cN?"":"";if(R.rE){q+=K(R.buffer+O,R)+N}else{if(R.eE){q+=K(R.buffer+O,R)+N+m(L)}else{q+=K(R.buffer+O+L,R)+N}}while(r>1){N=C[C.length-2].cN?"":"";q+=N;r--;C.length--}var P=C[C.length-1];C.length--;C[C.length-1].buffer="";if(P.starts){I(P.starts,"")}return R.rE}if(x(L,R)){throw"Illegal"}}var H=d[J];var C=[H.dM];var B=0;var t=0;var q="";try{var v=0;H.dM.buffer="";do{var y=s(D,v);var u=E(y[0],y[1],y[2]);v+=y[0].length;if(!u){v+=y[1].length}}while(!y[2]);if(C.length>1){throw"Illegal"}return{r:B,keyword_count:t,value:q}}catch(G){if(G=="Illegal"){return{r:0,keyword_count:0,value:m(D)}}else{throw G}}}function f(t){var r={keyword_count:0,r:0,value:m(t)};var q=r;for(var p in d){if(!d.hasOwnProperty(p)){continue}var s=e(p,t);s.language=p;if(s.keyword_count+s.r>q.keyword_count+q.r){q=s}if(s.keyword_count+s.r>r.keyword_count+r.r){q=r;r=s}}if(q.language){r.second_best=q}return r}function h(r,q,p){if(q){r=r.replace(/^((<[^>]+>|\t)+)/gm,function(t,w,v,u){return w.replace(/\t/g,q)})}if(p){r=r.replace(/\n/g,"
")}return r}function o(u,x,q){var y=g(u,q);var s=a(u);if(s=="no-highlight"){return}if(s){var w=e(s,y)}else{var w=f(y);s=w.language}var p=b(u);if(p.length){var r=document.createElement("pre");r.innerHTML=w.value;w.value=l(p,b(r),y)}w.value=h(w.value,x,q);var t=u.className;if(!t.match("(\\s|^)(language-)?"+s+"(\\s|$)")){t=t?(t+" "+s):s}if(/MSIE [678]/.test(navigator.userAgent)&&u.tagName=="CODE"&&u.parentNode.tagName=="PRE"){var r=u.parentNode;var v=document.createElement("div");v.innerHTML="
"+w.value+"
";u=v.firstChild.firstChild;v.firstChild.cN=r.cN;r.parentNode.replaceChild(v.firstChild,r)}else{u.innerHTML=w.value}u.className=t;u.result={language:s,kw:w.keyword_count,re:w.r};if(w.second_best){u.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function k(){if(k.called){return}k.called=true;var r=document.getElementsByTagName("pre");for(var p=0;p|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\.",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.inherit=function(p,s){var r={};for(var q in p){r[q]=p[q]}if(s){for(var q in s){r[q]=s[q]}}return r}}();hljs.LANGUAGES.cs={dM:{k:{"abstract":1,as:1,base:1,bool:1,"break":1,"byte":1,"case":1,"catch":1,"char":1,checked:1,"class":1,"const":1,"continue":1,decimal:1,"default":1,delegate:1,"do":1,"do":1,"double":1,"else":1,"enum":1,event:1,explicit:1,extern:1,"false":1,"finally":1,fixed:1,"float":1,"for":1,foreach:1,"goto":1,"if":1,implicit:1,"in":1,"int":1,"interface":1,internal:1,is:1,lock:1,"long":1,namespace:1,"new":1,"null":1,object:1,operator:1,out:1,override:1,params:1,"private":1,"protected":1,"public":1,readonly:1,ref:1,"return":1,sbyte:1,sealed:1,"short":1,sizeof:1,stackalloc:1,"static":1,string:1,struct:1,"switch":1,"this":1,"throw":1,"true":1,"try":1,"typeof":1,uint:1,ulong:1,unchecked:1,unsafe:1,ushort:1,using:1,virtual:1,"volatile":1,"void":1,"while":1,ascending:1,descending:1,from:1,get:1,group:1,into:1,join:1,let:1,orderby:1,partial:1,select:1,set:1,value:1,"var":1,where:1,yield:1},c:[{cN:"comment",b:"///",e:"$",rB:true,c:[{cN:"xmlDocTag",b:"///|"},{cN:"xmlDocTag",b:""}]},hljs.CLCM,hljs.CBLCLM,{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},hljs.ASM,hljs.QSM,hljs.CNM]}};hljs.LANGUAGES.ruby=function(){var g="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?";var a="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var n={keyword:{and:1,"false":1,then:1,defined:1,module:1,"in":1,"return":1,redo:1,"if":1,BEGIN:1,retry:1,end:1,"for":1,"true":1,self:1,when:1,next:1,until:1,"do":1,begin:1,unless:1,END:1,rescue:1,nil:1,"else":1,"break":1,undef:1,not:1,"super":1,"class":1,"case":1,require:1,yield:1,alias:1,"while":1,ensure:1,elsif:1,or:1,def:1},keymethods:{__id__:1,__send__:1,abort:1,abs:1,"all?":1,allocate:1,ancestors:1,"any?":1,arity:1,assoc:1,at:1,at_exit:1,autoload:1,"autoload?":1,"between?":1,binding:1,binmode:1,"block_given?":1,call:1,callcc:1,caller:1,capitalize:1,"capitalize!":1,casecmp:1,"catch":1,ceil:1,center:1,chomp:1,"chomp!":1,chop:1,"chop!":1,chr:1,"class":1,class_eval:1,"class_variable_defined?":1,class_variables:1,clear:1,clone:1,close:1,close_read:1,close_write:1,"closed?":1,coerce:1,collect:1,"collect!":1,compact:1,"compact!":1,concat:1,"const_defined?":1,const_get:1,const_missing:1,const_set:1,constants:1,count:1,crypt:1,"default":1,default_proc:1,"delete":1,"delete!":1,delete_at:1,delete_if:1,detect:1,display:1,div:1,divmod:1,downcase:1,"downcase!":1,downto:1,dump:1,dup:1,each:1,each_byte:1,each_index:1,each_key:1,each_line:1,each_pair:1,each_value:1,each_with_index:1,"empty?":1,entries:1,eof:1,"eof?":1,"eql?":1,"equal?":1,"eval":1,exec:1,exit:1,"exit!":1,extend:1,fail:1,fcntl:1,fetch:1,fileno:1,fill:1,find:1,find_all:1,first:1,flatten:1,"flatten!":1,floor:1,flush:1,for_fd:1,foreach:1,fork:1,format:1,freeze:1,"frozen?":1,fsync:1,getc:1,gets:1,global_variables:1,grep:1,gsub:1,"gsub!":1,"has_key?":1,"has_value?":1,hash:1,hex:1,id:1,include:1,"include?":1,included_modules:1,index:1,indexes:1,indices:1,induced_from:1,inject:1,insert:1,inspect:1,instance_eval:1,instance_method:1,instance_methods:1,"instance_of?":1,"instance_variable_defined?":1,instance_variable_get:1,instance_variable_set:1,instance_variables:1,"integer?":1,intern:1,invert:1,ioctl:1,"is_a?":1,isatty:1,"iterator?":1,join:1,"key?":1,keys:1,"kind_of?":1,lambda:1,last:1,length:1,lineno:1,ljust:1,load:1,local_variables:1,loop:1,lstrip:1,"lstrip!":1,map:1,"map!":1,match:1,max:1,"member?":1,merge:1,"merge!":1,method:1,"method_defined?":1,method_missing:1,methods:1,min:1,module_eval:1,modulo:1,name:1,nesting:1,"new":1,next:1,"next!":1,"nil?":1,nitems:1,"nonzero?":1,object_id:1,oct:1,open:1,pack:1,partition:1,pid:1,pipe:1,pop:1,popen:1,pos:1,prec:1,prec_f:1,prec_i:1,print:1,printf:1,private_class_method:1,private_instance_methods:1,"private_method_defined?":1,private_methods:1,proc:1,protected_instance_methods:1,"protected_method_defined?":1,protected_methods:1,public_class_method:1,public_instance_methods:1,"public_method_defined?":1,public_methods:1,push:1,putc:1,puts:1,quo:1,raise:1,rand:1,rassoc:1,read:1,read_nonblock:1,readchar:1,readline:1,readlines:1,readpartial:1,rehash:1,reject:1,"reject!":1,remainder:1,reopen:1,replace:1,require:1,"respond_to?":1,reverse:1,"reverse!":1,reverse_each:1,rewind:1,rindex:1,rjust:1,round:1,rstrip:1,"rstrip!":1,scan:1,seek:1,select:1,send:1,set_trace_func:1,shift:1,singleton_method_added:1,singleton_methods:1,size:1,sleep:1,slice:1,"slice!":1,sort:1,"sort!":1,sort_by:1,split:1,sprintf:1,squeeze:1,"squeeze!":1,srand:1,stat:1,step:1,store:1,strip:1,"strip!":1,sub:1,"sub!":1,succ:1,"succ!":1,sum:1,superclass:1,swapcase:1,"swapcase!":1,sync:1,syscall:1,sysopen:1,sysread:1,sysseek:1,system:1,syswrite:1,taint:1,"tainted?":1,tell:1,test:1,"throw":1,times:1,to_a:1,to_ary:1,to_f:1,to_hash:1,to_i:1,to_int:1,to_io:1,to_proc:1,to_s:1,to_str:1,to_sym:1,tr:1,"tr!":1,tr_s:1,"tr_s!":1,trace_var:1,transpose:1,trap:1,truncate:1,"tty?":1,type:1,ungetc:1,uniq:1,"uniq!":1,unpack:1,unshift:1,untaint:1,untrace_var:1,upcase:1,"upcase!":1,update:1,upto:1,"value?":1,values:1,values_at:1,warn:1,write:1,write_nonblock:1,"zero?":1,zip:1}};var h={cN:"yardoctag",b:"@[A-Za-z]+"};var d={cN:"comment",b:"#",e:"$",c:[h]};var c={cN:"comment",b:"^\\=begin",e:"^\\=end",c:[h],r:10};var b={cN:"comment",b:"^__END__",e:"\\n$"};var u={cN:"subst",b:"#\\{",e:"}",l:g,k:n};var p=[hljs.BE,u];var s={cN:"string",b:"'",e:"'",c:p,r:0};var r={cN:"string",b:'"',e:'"',c:p,r:0};var q={cN:"string",b:"%[qw]?\\(",e:"\\)",c:p,r:10};var o={cN:"string",b:"%[qw]?\\[",e:"\\]",c:p,r:10};var m={cN:"string",b:"%[qw]?{",e:"}",c:p,r:10};var l={cN:"string",b:"%[qw]?<",e:">",c:p,r:10};var k={cN:"string",b:"%[qw]?/",e:"/",c:p,r:10};var j={cN:"string",b:"%[qw]?%",e:"%",c:p,r:10};var i={cN:"string",b:"%[qw]?-",e:"-",c:p,r:10};var t={cN:"string",b:"%[qw]?\\|",e:"\\|",c:p,r:10};var e={cN:"function",b:"\\bdef\\s+",e:" |$|;",l:g,k:n,c:[{cN:"title",b:a,l:g,k:n},{cN:"params",b:"\\(",e:"\\)",l:g,k:n},d,c,b]};var f={cN:"identifier",b:g,l:g,k:n,r:0};var v=[d,c,b,s,r,q,o,m,l,k,j,i,t,{cN:"class",b:"\\b(class|module)\\b",e:"$|;",k:{"class":1,module:1},c:[{cN:"title",b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?",r:0},{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+hljs.IR+"::)?"+hljs.IR}]},d,c,b]},e,{cN:"constant",b:"(::)?([A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:":",c:[s,r,q,o,m,l,k,j,i,t,f],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"number",b:"\\?\\w"},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},f,{b:"("+hljs.RSR+")\\s*",c:[d,c,b,{cN:"regexp",b:"/",e:"/[a-z]*",i:"\\n",c:[hljs.BE]}],r:0}];u.c=v;e.c[1].c=v;return{dM:{l:g,k:n,c:v}}}();hljs.LANGUAGES.javascript={dM:{k:{keyword:{"in":1,"if":1,"for":1,"while":1,"finally":1,"var":1,"new":1,"function":1,"do":1,"return":1,"void":1,"else":1,"break":1,"catch":1,"instanceof":1,"with":1,"throw":1,"case":1,"default":1,"try":1,"this":1,"switch":1,"continue":1,"typeof":1,"delete":1},literal:{"true":1,"false":1,"null":1}},c:[hljs.ASM,hljs.QSM,hljs.CLCM,hljs.CBLCLM,hljs.CNM,{b:"("+hljs.RSR+"|case|return|throw)\\s*",k:{"return":1,"throw":1,"case":1},c:[hljs.CLCM,hljs.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",c:[{b:"\\\\/"}]}],r:0},{cN:"function",b:"\\bfunction\\b",e:"{",k:{"function":1},c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[hljs.ASM,hljs.QSM,hljs.CLCM,hljs.CBLCLM]}]}]}};hljs.LANGUAGES.css=function(){var a={cN:"function",b:hljs.IR+"\\(",e:"\\)",c:[{eW:true,eE:true,c:[hljs.NM,hljs.ASM,hljs.QSM]}]};return{cI:true,dM:{i:"[=/|']",c:[hljs.CBLCLM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:{"font-face":1,page:1}},{cN:"at_rule",b:"@",e:"[{;]",eE:true,k:{"import":1,page:1,media:1,charset:1},c:[a,hljs.ASM,hljs.QSM,hljs.NM]},{cN:"tag",b:hljs.IR,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[hljs.CBLCLM,{cN:"rule",b:"[^\\s]",rB:true,e:";",eW:true,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:true,i:"[^\\s]",starts:{cN:"value",eW:true,eE:true,c:[a,hljs.NM,hljs.QSM,hljs.ASM,hljs.CBLCLM,{cN:"hexcolor",b:"\\#[0-9A-F]+"},{cN:"important",b:"!important"}]}}]}]}]}}}();hljs.LANGUAGES.xml=function(){var b="[A-Za-z0-9\\._:-]+";var a={eW:true,c:[{cN:"attribute",b:b,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,dM:{c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",k:{title:{style:1}},c:[a],starts:{cN:"css",e:"",rE:true,sL:"css"}},{cN:"tag",b:"",k:{title:{script:1}},c:[a],starts:{cN:"javascript",e:"<\/script>",rE:true,sL:"javascript"}},{cN:"vbscript",b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},a]}]}}}();hljs.LANGUAGES.java={dM:{k:{"false":1,"synchronized":1,"int":1,"abstract":1,"float":1,"private":1,"char":1,"interface":1,"boolean":1,"static":1,"null":1,"if":1,"const":1,"for":1,"true":1,"while":1,"long":1,"throw":1,strictfp:1,"finally":1,"protected":1,"extends":1,"import":1,"native":1,"final":1,"implements":1,"return":1,"void":1,"enum":1,"else":1,"break":1,"transient":1,"new":1,"catch":1,"instanceof":1,"byte":1,"super":1,"class":1,"volatile":1,"case":1,assert:1,"short":1,"package":1,"default":1,"double":1,"public":1,"try":1,"this":1,"switch":1,"continue":1,"throws":1},c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",c:[{cN:"javadoctag",b:"@[A-Za-z]+"}],r:10},hljs.CLCM,hljs.CBLCLM,hljs.ASM,hljs.QSM,{cN:"class",b:"(class |interface )",e:"{",k:{"class":1,"interface":1},i:":",c:[{b:"(implements|extends)",k:{"extends":1,"implements":1},r:10},{cN:"title",b:hljs.UIR}]},hljs.CNM,{cN:"annotation",b:"@[A-Za-z]+"}]}};hljs.LANGUAGES.php={cI:true,dM:{k:{and:1,include_once:1,list:1,"abstract":1,global:1,"private":1,echo:1,"interface":1,as:1,"static":1,endswitch:1,array:1,"null":1,"if":1,endwhile:1,or:1,"const":1,"for":1,endforeach:1,self:1,"var":1,"while":1,isset:1,"public":1,"protected":1,exit:1,foreach:1,"throw":1,elseif:1,"extends":1,include:1,__FILE__:1,empty:1,require_once:1,"function":1,"do":1,xor:1,"return":1,"implements":1,parent:1,clone:1,use:1,__CLASS__:1,__LINE__:1,"else":1,"break":1,print:1,"eval":1,"new":1,"catch":1,__METHOD__:1,"class":1,"case":1,exception:1,php_user_filter:1,"default":1,die:1,require:1,__FUNCTION__:1,enddeclare:1,"final":1,"try":1,"this":1,"switch":1,"continue":1,endfor:1,endif:1,declare:1,unset:1,"true":1,"false":1,namespace:1},c:[hljs.CLCM,hljs.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+",r:10}]},hljs.CNM,hljs.inherit(hljs.ASM,{i:null}),hljs.inherit(hljs.QSM,{i:null}),{cN:"variable",b:"\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*"},{cN:"preprocessor",b:"<\\?php",r:10},{cN:"preprocessor",b:"\\?>"}]}};hljs.LANGUAGES.python=function(){var c={cN:"string",b:"(u|b)?r?'''",e:"'''",r:10};var b={cN:"string",b:'(u|b)?r?"""',e:'"""',r:10};var a={cN:"string",b:"(u|r|ur|b|br)'",e:"'",c:[hljs.BE],r:10};var f={cN:"string",b:'(u|r|ur|b|br)"',e:'"',c:[hljs.BE],r:10};var d={cN:"title",b:hljs.UIR};var e={cN:"params",b:"\\(",e:"\\)",c:[c,b,a,f,hljs.ASM,hljs.QSM]};return{dM:{k:{keyword:{and:1,elif:1,is:1,global:1,as:1,"in":1,"if":1,from:1,raise:1,"for":1,except:1,"finally":1,print:1,"import":1,pass:1,"return":1,exec:1,"else":1,"break":1,not:1,"with":1,"class":1,assert:1,yield:1,"try":1,"while":1,"continue":1,del:1,or:1,def:1,lambda:1,nonlocal:10},built_in:{None:1,True:1,False:1,Ellipsis:1,NotImplemented:1}},i:"(|\\?)",c:[hljs.HCM,c,b,a,f,hljs.ASM,hljs.QSM,{cN:"function",b:"\\bdef ",e:":",i:"$",k:{def:1},c:[d,e],r:10},{cN:"class",b:"\\bclass ",e:":",i:"[${]",k:{"class":1},c:[d,e],r:10},hljs.CNM,{cN:"decorator",b:"@",e:"$"}]}}}();hljs.LANGUAGES.perl=function(){var c={getpwent:1,getservent:1,quotemeta:1,msgrcv:1,scalar:1,kill:1,dbmclose:1,undef:1,lc:1,ma:1,syswrite:1,tr:1,send:1,umask:1,sysopen:1,shmwrite:1,vec:1,qx:1,utime:1,local:1,oct:1,semctl:1,localtime:1,readpipe:1,"do":1,"return":1,format:1,read:1,sprintf:1,dbmopen:1,pop:1,getpgrp:1,not:1,getpwnam:1,rewinddir:1,qq:1,fileno:1,qw:1,endprotoent:1,wait:1,sethostent:1,bless:1,s:1,opendir:1,"continue":1,each:1,sleep:1,endgrent:1,shutdown:1,dump:1,chomp:1,connect:1,getsockname:1,die:1,socketpair:1,close:1,flock:1,exists:1,index:1,shmget:1,sub:1,"for":1,endpwent:1,redo:1,lstat:1,msgctl:1,setpgrp:1,abs:1,exit:1,select:1,print:1,ref:1,gethostbyaddr:1,unshift:1,fcntl:1,syscall:1,"goto":1,getnetbyaddr:1,join:1,gmtime:1,symlink:1,semget:1,splice:1,x:1,getpeername:1,recv:1,log:1,setsockopt:1,cos:1,last:1,reverse:1,gethostbyname:1,getgrnam:1,study:1,formline:1,endhostent:1,times:1,chop:1,length:1,gethostent:1,getnetent:1,pack:1,getprotoent:1,getservbyname:1,rand:1,mkdir:1,pos:1,chmod:1,y:1,substr:1,endnetent:1,printf:1,next:1,open:1,msgsnd:1,readdir:1,use:1,unlink:1,getsockopt:1,getpriority:1,rindex:1,wantarray:1,hex:1,system:1,getservbyport:1,endservent:1,"int":1,chr:1,untie:1,rmdir:1,prototype:1,tell:1,listen:1,fork:1,shmread:1,ucfirst:1,setprotoent:1,"else":1,sysseek:1,link:1,getgrgid:1,shmctl:1,waitpid:1,unpack:1,getnetbyname:1,reset:1,chdir:1,grep:1,split:1,require:1,caller:1,lcfirst:1,until:1,warn:1,"while":1,values:1,shift:1,telldir:1,getpwuid:1,my:1,getprotobynumber:1,"delete":1,and:1,sort:1,uc:1,defined:1,srand:1,accept:1,"package":1,seekdir:1,getprotobyname:1,semop:1,our:1,rename:1,seek:1,"if":1,q:1,chroot:1,sysread:1,setpwent:1,no:1,crypt:1,getc:1,chown:1,sqrt:1,write:1,setnetent:1,setpriority:1,foreach:1,tie:1,sin:1,msgget:1,map:1,stat:1,getlogin:1,unless:1,elsif:1,truncate:1,exec:1,keys:1,glob:1,tied:1,closedir:1,ioctl:1,socket:1,readlink:1,"eval":1,xor:1,readline:1,binmode:1,setservent:1,eof:1,ord:1,bind:1,alarm:1,pipe:1,atan2:1,getgrent:1,exp:1,time:1,push:1,setgrent:1,gt:1,lt:1,or:1,ne:1,m:1};var d={cN:"subst",b:"[$@]\\{",e:"}",k:c,r:10};var b={cN:"variable",b:"\\$\\d"};var a={cN:"variable",b:"[\\$\\%\\@\\*](\\^\\w\\b|#\\w+(\\:\\:\\w+)*|[^\\s\\w{]|{\\w+}|\\w+(\\:\\:\\w*)*)"};var g=[hljs.BE,d,b,a];var f={b:"->",c:[{b:hljs.IR},{b:"{",e:"}"}]};var e=[b,a,hljs.HCM,{cN:"comment",b:"^(__END__|__DATA__)",e:"\\n$",r:5},f,{cN:"string",b:"q[qwxr]?\\s*\\(",e:"\\)",c:g,r:5},{cN:"string",b:"q[qwxr]?\\s*\\[",e:"\\]",c:g,r:5},{cN:"string",b:"q[qwxr]?\\s*\\{",e:"\\}",c:g,r:5},{cN:"string",b:"q[qwxr]?\\s*\\|",e:"\\|",c:g,r:5},{cN:"string",b:"q[qwxr]?\\s*\\<",e:"\\>",c:g,r:5},{cN:"string",b:"qw\\s+q",e:"q",c:g,r:5},{cN:"string",b:"'",e:"'",c:[hljs.BE],r:0},{cN:"string",b:'"',e:'"',c:g,r:0},{cN:"string",b:"`",e:"`",c:[hljs.BE]},{cN:"string",b:"{\\w+}",r:0},{cN:"string",b:"-?\\w+\\s*\\=\\>",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[hljs.BE],r:0},{cN:"sub",b:"\\bsub\\b",e:"(\\s*\\(.*?\\))?[;{]",k:{sub:1},r:5},{cN:"operator",b:"-\\w\\b",r:0},{cN:"pod",b:"\\=\\w",e:"\\=cut"}];d.c=e;f.c[1].c=e;return{dM:{k:c,c:e}}}();hljs.LANGUAGES.cpp=function(){var b={keyword:{"false":1,"int":1,"float":1,"while":1,"private":1,"char":1,"catch":1,"export":1,virtual:1,operator:2,sizeof:2,dynamic_cast:2,typedef:2,const_cast:2,"const":1,struct:1,"for":1,static_cast:2,union:1,namespace:1,unsigned:1,"long":1,"throw":1,"volatile":2,"static":1,"protected":1,bool:1,template:1,mutable:1,"if":1,"public":1,friend:2,"do":1,"return":1,"goto":1,auto:1,"void":2,"enum":1,"else":1,"break":1,"new":1,extern:1,using:1,"true":1,"class":1,asm:1,"case":1,typeid:1,"short":1,reinterpret_cast:2,"default":1,"double":1,register:1,explicit:1,signed:1,typename:1,"try":1,"this":1,"switch":1,"continue":1,wchar_t:1,inline:1,"delete":1,alignof:1,char16_t:1,char32_t:1,constexpr:1,decltype:1,noexcept:1,nullptr:1,static_assert:1,thread_local:1},built_in:{std:1,string:1,cin:1,cout:1,cerr:1,clog:1,stringstream:1,istringstream:1,ostringstream:1,auto_ptr:1,deque:1,list:1,queue:1,stack:1,vector:1,map:1,set:1,bitset:1,multiset:1,multimap:1,unordered_set:1,unordered_map:1,unordered_multiset:1,unordered_multimap:1,array:1,shared_ptr:1}};var a={cN:"stl_container",b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:b,r:10};a.c=[a];return{dM:{k:b,i:" -// -// Redistributable under a BSD-style open source license. -// See license.txt for more information. -// -// The full source distribution is at: -// -// A A L -// T C A -// T K B -// -// -// - -// -// Wherever possible, Showdown is a straight, line-by-line port -// of the Perl version of Markdown. -// -// This is not a normal parser design; it's basically just a -// series of string substitutions. It's hard to read and -// maintain this way, but keeping Showdown close to the original -// design makes it easier to port new features. -// -// More importantly, Showdown behaves like markdown.pl in most -// edge cases. So web applications can do client-side preview -// in Javascript, and then build identical HTML on the server. -// -// This port needs the new RegExp functionality of ECMA 262, -// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers -// should do fine. Even with the new regular expression features, -// We do a lot of work to emulate Perl's regex functionality. -// The tricky changes in this file mostly have the "attacklab:" -// label. Major or self-explanatory changes don't. -// -// Smart diff tools like Araxis Merge will be able to match up -// this file with markdown.pl in a useful way. A little tweaking -// helps: in a copy of markdown.pl, replace "#" with "//" and -// replace "$text" with "text". Be sure to ignore whitespace -// and line endings. -// - - -// -// Showdown usage: -// -// var text = "Markdown *rocks*."; -// -// var converter = new Showdown.converter(); -// var html = converter.makeHtml(text); -// -// alert(html); -// -// Note: move the sample code to the bottom of this -// file before uncommenting it. -// - - -// -// Showdown namespace -// -var Showdown = {}; - -// -// converter -// -// Wraps all "globals" so that the only thing -// exposed is makeHtml(). -// -Showdown.converter = function() { - -// -// Globals: -// - -// Global hashes, used by various utility routines -var g_urls; -var g_titles; -var g_html_blocks; - -// Used to track when we're inside an ordered or unordered list -// (see _ProcessListItems() for details): -var g_list_level = 0; - - -this.makeHtml = function(text) { -// -// Main function. The order in which other subs are called here is -// essential. Link and image substitutions need to happen before -// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the -// and tags get encoded. -// - - // Clear the global hashes. If we don't clear these, you get conflicts - // from other articles when generating a page which contains more than - // one article (e.g. an index page that shows the N most recent - // articles): - g_urls = new Array(); - g_titles = new Array(); - g_html_blocks = new Array(); - - // attacklab: Replace ~ with ~T - // This lets us use tilde as an escape char to avoid md5 hashes - // The choice of character is arbitray; anything that isn't - // magic in Markdown will work. - text = text.replace(/~/g,"~T"); - - // attacklab: Replace $ with ~D - // RegExp interprets $ as a special character - // when it's in a replacement string - text = text.replace(/\$/g,"~D"); - - // Standardize line endings - text = text.replace(/\r\n/g,"\n"); // DOS to Unix - text = text.replace(/\r/g,"\n"); // Mac to Unix - - // Make sure text begins and ends with a couple of newlines: - text = "\n\n" + text + "\n\n"; - - // Convert all tabs to spaces. - text = _Detab(text); - - // Strip any lines consisting only of spaces and tabs. - // This makes subsequent regexen easier to write, because we can - // match consecutive blank lines with /\n+/ instead of something - // contorted like /[ \t]*\n+/ . - text = text.replace(/^[ \t]+$/mg,""); - - // Handle github codeblocks prior to running HashHTML so that - // HTML contained within the codeblock gets escaped propertly - text = _DoGithubCodeBlocks(text); - - // Turn block-level HTML blocks into hash entries - text = _HashHTMLBlocks(text); - - // Strip link definitions, store in hashes. - text = _StripLinkDefinitions(text); - - text = _RunBlockGamut(text); - - text = _UnescapeSpecialChars(text); - - // attacklab: Restore dollar signs - text = text.replace(/~D/g,"$$"); - - // attacklab: Restore tildes - text = text.replace(/~T/g,"~"); - - return text; -}; - - -var _StripLinkDefinitions = function(text) { -// -// Strips link definitions from text, stores the URLs and titles in -// hash references. -// - - // Link defs are in the form: ^[id]: url "optional title" - - /* - var text = text.replace(/ - ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 - [ \t]* - \n? // maybe *one* newline - [ \t]* - ? // url = $2 - [ \t]* - \n? // maybe one newline - [ \t]* - (?: - (\n*) // any lines skipped = $3 attacklab: lookbehind removed - ["(] - (.+?) // title = $4 - [")] - [ \t]* - )? // title is optional - (?:\n+|$) - /gm, - function(){...}); - */ - var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm, - function (wholeMatch,m1,m2,m3,m4) { - m1 = m1.toLowerCase(); - g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive - if (m3) { - // Oops, found blank lines, so it's not a title. - // Put back the parenthetical statement we stole. - return m3+m4; - } else if (m4) { - g_titles[m1] = m4.replace(/"/g,"""); - } - - // Completely remove the definition from the text - return ""; - } - ); - - return text; -} - - -var _HashHTMLBlocks = function(text) { - // attacklab: Double up blank lines to reduce lookaround - text = text.replace(/\n/g,"\n\n"); - - // Hashify HTML blocks: - // We only want to do this for block-level HTML tags, such as headers, - // lists, and tables. That's because we still want to wrap

s around - // "paragraphs" that are wrapped in non-block-level tags, such as anchors, - // phrase emphasis, and spans. The list of tags we're looking for is - // hard-coded: - var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside"; - var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside"; - - // First, look for nested blocks, e.g.: - //

- //
- // tags for inner block must be indented. - //
- //
- // - // The outermost tags must start at the left margin for this to match, and - // the inner nested divs must be indented. - // We need to do this before the next, more liberal match, because the next - // match will start at the first `
` and stop at the first `
`. - - // attacklab: This regex can be expensive when it fails. - /* - var text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_a) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*?\n // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement); - - // - // Now match more liberally, simply from `\n` to `\n` - // - - /* - var text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_b) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*? // any number of lines, minimally matching - .* // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement); - - // Special case just for
. It was easier to make a special case than - // to make the other regex more complicated. - - /* - text = text.replace(/ - ( // save in $1 - \n\n // Starting after a blank line - [ ]{0,3} - (<(hr) // start tag = $2 - \b // word break - ([^<>])*? // - \/?>) // the matching end tag - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement); - - // Special case for standalone HTML comments: - - /* - text = text.replace(/ - ( // save in $1 - \n\n // Starting after a blank line - [ ]{0,3} // attacklab: g_tab_width - 1 - - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,hashElement); - - // PHP and ASP-style processor instructions ( and <%...%>) - - /* - text = text.replace(/ - (?: - \n\n // Starting after a blank line - ) - ( // save in $1 - [ ]{0,3} // attacklab: g_tab_width - 1 - (?: - <([?%]) // $2 - [^\r]*? - \2> - ) - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement); - - // attacklab: Undo double lines (see comment at top of this function) - text = text.replace(/\n\n/g,"\n"); - return text; -} - -var hashElement = function(wholeMatch,m1) { - var blockText = m1; - - // Undo double lines - blockText = blockText.replace(/\n\n/g,"\n"); - blockText = blockText.replace(/^\n/,""); - - // strip trailing blank lines - blockText = blockText.replace(/\n+$/g,""); - - // Replace the element text with a marker ("~KxK" where x is its key) - blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n"; - - return blockText; -}; - -var _RunBlockGamut = function(text) { -// -// These are all the transformations that form block-level -// tags like paragraphs, headers, and list items. -// - text = _DoHeaders(text); - - // Do Horizontal Rules: - var key = hashBlock("
"); - text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key); - text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key); - text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key); - - text = _DoLists(text); - text = _DoCodeBlocks(text); - text = _DoBlockQuotes(text); - - // We already ran _HashHTMLBlocks() before, in Markdown(), but that - // was to escape raw HTML in the original Markdown source. This time, - // we're escaping the markup we've just created, so that we don't wrap - //

tags around block-level tags. - text = _HashHTMLBlocks(text); - text = _FormParagraphs(text); - - return text; -}; - - -var _RunSpanGamut = function(text) { -// -// These are all the transformations that occur *within* block-level -// tags like paragraphs, headers, and list items. -// - - text = _DoCodeSpans(text); - text = _EscapeSpecialCharsWithinTagAttributes(text); - text = _EncodeBackslashEscapes(text); - - // Process anchor and image tags. Images must come first, - // because ![foo][f] looks like an anchor. - text = _DoImages(text); - text = _DoAnchors(text); - - // Make links out of things like `` - // Must come after _DoAnchors(), because you can use < and > - // delimiters in inline links like [this](). - text = _DoAutoLinks(text); - text = _EncodeAmpsAndAngles(text); - text = _DoItalicsAndBold(text); - - // Do hard breaks: - text = text.replace(/ +\n/g,"
\n"); - - return text; -} - -var _EscapeSpecialCharsWithinTagAttributes = function(text) { -// -// Within tags -- meaning between < and > -- encode [\ ` * _] so they -// don't conflict with their use in Markdown for code, italics and strong. -// - - // Build a regex to find HTML tags and comments. See Friedl's - // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. - var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; - - text = text.replace(regex, function(wholeMatch) { - var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`"); - tag = escapeCharacters(tag,"\\`*_"); - return tag; - }); - - return text; -} - -var _DoAnchors = function(text) { -// -// Turn Markdown link shortcuts into XHTML
tags. -// - // - // First, handle reference-style links: [link text] [id] - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[] // or anything else - )* - ) - \] - - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces - - \[ - (.*?) // id = $3 - \] - )()()()() // pad remaining backreferences - /g,_DoAnchors_callback); - */ - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag); - - // - // Next, inline-style links: [link text](url "optional title") - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[\]] // or anything else - ) - ) - \] - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? // href = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // Title = $7 - \6 // matching quote - [ \t]* // ignore any spaces/tabs between closing quote and ) - )? // title is optional - \) - ) - /g,writeAnchorTag); - */ - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag); - - // - // Last, handle reference-style shortcuts: [link text] - // These must come last in case you've also got [link test][1] - // or [link test](/foo) - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ([^\[\]]+) // link text = $2; can't contain '[' or ']' - \] - )()()()()() // pad rest of backreferences - /g, writeAnchorTag); - */ - text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); - - return text; -} - -var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { - if (m7 == undefined) m7 = ""; - var whole_match = m1; - var link_text = m2; - var link_id = m3.toLowerCase(); - var url = m4; - var title = m7; - - if (url == "") { - if (link_id == "") { - // lower-case and turn embedded newlines into spaces - link_id = link_text.toLowerCase().replace(/ ?\n/g," "); - } - url = "#"+link_id; - - if (g_urls[link_id] != undefined) { - url = g_urls[link_id]; - if (g_titles[link_id] != undefined) { - title = g_titles[link_id]; - } - } - else { - if (whole_match.search(/\(\s*\)$/m)>-1) { - // Special case for explicit empty url - url = ""; - } else { - return whole_match; - } - } - } - - url = escapeCharacters(url,"*_"); - var result = ""; - - return result; -} - - -var _DoImages = function(text) { -// -// Turn Markdown image shortcuts into tags. -// - - // - // First, handle reference-style labeled images: ![alt text][id] - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - !\[ - (.*?) // alt text = $2 - \] - - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces - - \[ - (.*?) // id = $3 - \] - )()()()() // pad rest of backreferences - /g,writeImageTag); - */ - text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag); - - // - // Next, handle inline images: ![alt text](url "optional title") - // Don't forget: encode * and _ - - /* - text = text.replace(/ - ( // wrap whole match in $1 - !\[ - (.*?) // alt text = $2 - \] - \s? // One optional whitespace character - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? // src url = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // title = $7 - \6 // matching quote - [ \t]* - )? // title is optional - \) - ) - /g,writeImageTag); - */ - text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag); - - return text; -} - -var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { - var whole_match = m1; - var alt_text = m2; - var link_id = m3.toLowerCase(); - var url = m4; - var title = m7; - - if (!title) title = ""; - - if (url == "") { - if (link_id == "") { - // lower-case and turn embedded newlines into spaces - link_id = alt_text.toLowerCase().replace(/ ?\n/g," "); - } - url = "#"+link_id; - - if (g_urls[link_id] != undefined) { - url = g_urls[link_id]; - if (g_titles[link_id] != undefined) { - title = g_titles[link_id]; - } - } - else { - return whole_match; - } - } - - alt_text = alt_text.replace(/"/g,"""); - url = escapeCharacters(url,"*_"); - var result = "\""' + _RunSpanGamut(m1) + "");}); - - text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, - function(matchFound,m1){return hashBlock('

' + _RunSpanGamut(m1) + "

");}); - - // atx-style headers: - // # Header 1 - // ## Header 2 - // ## Header 2 with closing hashes ## - // ... - // ###### Header 6 - // - - /* - text = text.replace(/ - ^(\#{1,6}) // $1 = string of #'s - [ \t]* - (.+?) // $2 = Header text - [ \t]* - \#* // optional closing #'s (not counted) - \n+ - /gm, function() {...}); - */ - - text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, - function(wholeMatch,m1,m2) { - var h_level = m1.length; - return hashBlock("' + _RunSpanGamut(m2) + ""); - }); - - function headerId(m) { - return m.replace(/[^\w]/g, '').toLowerCase(); - } - return text; -} - -// This declaration keeps Dojo compressor from outputting garbage: -var _ProcessListItems; - -var _DoLists = function(text) { -// -// Form HTML ordered (numbered) and unordered (bulleted) lists. -// - - // attacklab: add sentinel to hack around khtml/safari bug: - // http://bugs.webkit.org/show_bug.cgi?id=11231 - text += "~0"; - - // Re-usable pattern to match any entirel ul or ol list: - - /* - var whole_list = / - ( // $1 = whole list - ( // $2 - [ ]{0,3} // attacklab: g_tab_width - 1 - ([*+-]|\d+[.]) // $3 = first list item marker - [ \t]+ - ) - [^\r]+? - ( // $4 - ~0 // sentinel for workaround; should be $ - | - \n{2,} - (?=\S) - (?! // Negative lookahead for another list item marker - [ \t]* - (?:[*+-]|\d+[.])[ \t]+ - ) - ) - )/g - */ - var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; - - if (g_list_level) { - text = text.replace(whole_list,function(wholeMatch,m1,m2) { - var list = m1; - var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol"; - - // Turn double returns into triple returns, so that we can make a - // paragraph for the last item in a list, if necessary: - list = list.replace(/\n{2,}/g,"\n\n\n");; - var result = _ProcessListItems(list); - - // Trim any trailing whitespace, to put the closing `` - // up on the preceding line, to get it past the current stupid - // HTML block parser. This is a hack to work around the terrible - // hack that is the HTML block parser. - result = result.replace(/\s+$/,""); - result = "<"+list_type+">" + result + "\n"; - return result; - }); - } else { - whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; - text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) { - var runup = m1; - var list = m2; - - var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol"; - // Turn double returns into triple returns, so that we can make a - // paragraph for the last item in a list, if necessary: - var list = list.replace(/\n{2,}/g,"\n\n\n");; - var result = _ProcessListItems(list); - result = runup + "<"+list_type+">\n" + result + "\n"; - return result; - }); - } - - // attacklab: strip sentinel - text = text.replace(/~0/,""); - - return text; -} - -_ProcessListItems = function(list_str) { -// -// Process the contents of a single ordered or unordered list, splitting it -// into individual list items. -// - // The $g_list_level global keeps track of when we're inside a list. - // Each time we enter a list, we increment it; when we leave a list, - // we decrement. If it's zero, we're not in a list anymore. - // - // We do this because when we're not inside a list, we want to treat - // something like this: - // - // I recommend upgrading to version - // 8. Oops, now this line is treated - // as a sub-list. - // - // As a single paragraph, despite the fact that the second line starts - // with a digit-period-space sequence. - // - // Whereas when we're inside a list (or sub-list), that line will be - // treated as the start of a sub-list. What a kludge, huh? This is - // an aspect of Markdown's syntax that's hard to parse perfectly - // without resorting to mind-reading. Perhaps the solution is to - // change the syntax rules such that sub-lists must start with a - // starting cardinal number; e.g. "1." or "a.". - - g_list_level++; - - // trim trailing blank lines: - list_str = list_str.replace(/\n{2,}$/,"\n"); - - // attacklab: add sentinel to emulate \z - list_str += "~0"; - - /* - list_str = list_str.replace(/ - (\n)? // leading line = $1 - (^[ \t]*) // leading whitespace = $2 - ([*+-]|\d+[.]) [ \t]+ // list marker = $3 - ([^\r]+? // list item text = $4 - (\n{1,2})) - (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+)) - /gm, function(){...}); - */ - list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, - function(wholeMatch,m1,m2,m3,m4){ - var item = m4; - var leading_line = m1; - var leading_space = m2; - - if (leading_line || (item.search(/\n{2,}/)>-1)) { - item = _RunBlockGamut(_Outdent(item)); - } - else { - // Recursion for sub-lists: - item = _DoLists(_Outdent(item)); - item = item.replace(/\n$/,""); // chomp(item) - item = _RunSpanGamut(item); - } - - return "
  • " + item + "
  • \n"; - } - ); - - // attacklab: strip sentinel - list_str = list_str.replace(/~0/g,""); - - g_list_level--; - return list_str; -} - - -var _DoCodeBlocks = function(text) { -// -// Process Markdown `
    ` blocks.
    -//
    -
    -	/*
    -		text = text.replace(text,
    -			/(?:\n\n|^)
    -			(								// $1 = the code block -- one or more lines, starting with a space/tab
    -				(?:
    -					(?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    -					.*\n+
    -				)+
    -			)
    -			(\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
    -		/g,function(){...});
    -	*/
    -
    -	// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    -	text += "~0";
    -
    -	text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
    -		function(wholeMatch,m1,m2) {
    -			var codeblock = m1;
    -			var nextChar = m2;
    -
    -			codeblock = _EncodeCode( _Outdent(codeblock));
    -			codeblock = _Detab(codeblock);
    -			codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
    -			codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
    -
    -			codeblock = "
    " + codeblock + "\n
    "; - - return hashBlock(codeblock) + nextChar; - } - ); - - // attacklab: strip sentinel - text = text.replace(/~0/,""); - - return text; -}; - -var _DoGithubCodeBlocks = function(text) { -// -// Process Github-style code blocks -// Example: -// ```ruby -// def hello_world(x) -// puts "Hello, #{x}" -// end -// ``` -// - - - // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug - text += "~0"; - - text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, - function(wholeMatch,m1,m2) { - var language = m1; - var codeblock = m2; - - codeblock = _EncodeCode(codeblock); - codeblock = _Detab(codeblock); - codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines - codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace - - codeblock = "
    " + codeblock + "\n
    "; - - return hashBlock(codeblock); - } - ); - - // attacklab: strip sentinel - text = text.replace(/~0/,""); - - return text; -} - -var hashBlock = function(text) { - text = text.replace(/(^\n+|\n+$)/g,""); - return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n"; -} - -var _DoCodeSpans = function(text) { -// -// * Backtick quotes are used for spans. -// -// * You can use multiple backticks as the delimiters if you want to -// include literal backticks in the code span. So, this input: -// -// Just type ``foo `bar` baz`` at the prompt. -// -// Will translate to: -// -//

    Just type foo `bar` baz at the prompt.

    -// -// There's no arbitrary limit to the number of backticks you -// can use as delimters. If you need three consecutive backticks -// in your code, use four for delimiters, etc. -// -// * You can use spaces to get literal backticks at the edges: -// -// ... type `` `bar` `` ... -// -// Turns to: -// -// ... type `bar` ... -// - - /* - text = text.replace(/ - (^|[^\\]) // Character before opening ` can't be a backslash - (`+) // $2 = Opening run of ` - ( // $3 = The code block - [^\r]*? - [^`] // attacklab: work around lack of lookbehind - ) - \2 // Matching closer - (?!`) - /gm, function(){...}); - */ - - text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, - function(wholeMatch,m1,m2,m3,m4) { - var c = m3; - c = c.replace(/^([ \t]*)/g,""); // leading whitespace - c = c.replace(/[ \t]*$/g,""); // trailing whitespace - c = _EncodeCode(c); - return m1+""+c+""; - }); - - return text; -} - -var _EncodeCode = function(text) { -// -// Encode/escape certain characters inside Markdown code runs. -// The point is that in code, these characters are literals, -// and lose their special Markdown meanings. -// - // Encode all ampersands; HTML entities are not - // entities within a Markdown code span. - text = text.replace(/&/g,"&"); - - // Do the angle bracket song and dance: - text = text.replace(//g,">"); - - // Now, escape characters that are magic in Markdown: - text = escapeCharacters(text,"\*_{}[]\\",false); - -// jj the line above breaks this: -//--- - -//* Item - -// 1. Subitem - -// special char: * -//--- - - return text; -} - - -var _DoItalicsAndBold = function(text) { - - // must go first: - text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, - "$2"); - - text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, - "$2"); - - return text; -} - - -var _DoBlockQuotes = function(text) { - - /* - text = text.replace(/ - ( // Wrap whole match in $1 - ( - ^[ \t]*>[ \t]? // '>' at the start of a line - .+\n // rest of the first line - (.+\n)* // subsequent consecutive lines - \n* // blanks - )+ - ) - /gm, function(){...}); - */ - - text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, - function(wholeMatch,m1) { - var bq = m1; - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - - bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting - - // attacklab: clean up hack - bq = bq.replace(/~0/g,""); - - bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines - bq = _RunBlockGamut(bq); // recurse - - bq = bq.replace(/(^|\n)/g,"$1 "); - // These leading spaces screw with
     content, so we need to fix that:
    -			bq = bq.replace(
    -					/(\s*
    [^\r]+?<\/pre>)/gm,
    -				function(wholeMatch,m1) {
    -					var pre = m1;
    -					// attacklab: hack around Konqueror 3.5.4 bug:
    -					pre = pre.replace(/^  /mg,"~0");
    -					pre = pre.replace(/~0/g,"");
    -					return pre;
    -				});
    -
    -			return hashBlock("
    \n" + bq + "\n
    "); - }); - return text; -} - - -var _FormParagraphs = function(text) { -// -// Params: -// $text - string to process with html

    tags -// - - // Strip leading and trailing lines: - text = text.replace(/^\n+/g,""); - text = text.replace(/\n+$/g,""); - - var grafs = text.split(/\n{2,}/g); - var grafsOut = new Array(); - - // - // Wrap

    tags. - // - var end = grafs.length; - for (var i=0; i= 0) { - grafsOut.push(str); - } - else if (str.search(/\S/) >= 0) { - str = _RunSpanGamut(str); - str = str.replace(/^([ \t]*)/g,"

    "); - str += "

    " - grafsOut.push(str); - } - - } - - // - // Unhashify HTML blocks - // - end = grafsOut.length; - for (var i=0; i= 0) { - var blockText = g_html_blocks[RegExp.$1]; - blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs - grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText); - } - } - - return grafsOut.join("\n\n"); -} - - -var _EncodeAmpsAndAngles = function(text) { -// Smart processing for ampersands and angle brackets that need to be encoded. - - // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: - // http://bumppo.net/projects/amputator/ - text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"); - - // Encode naked <'s - text = text.replace(/<(?![a-z\/?\$!])/gi,"<"); - - return text; -} - - -var _EncodeBackslashEscapes = function(text) { -// -// Parameter: String. -// Returns: The string, with after processing the following backslash -// escape sequences. -// - - // attacklab: The polite way to do this is with the new - // escapeCharacters() function: - // - // text = escapeCharacters(text,"\\",true); - // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); - // - // ...but we're sidestepping its use of the (slow) RegExp constructor - // as an optimization for Firefox. This function gets called a LOT. - - text = text.replace(/\\(\\)/g,escapeCharacters_callback); - text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback); - return text; -} - - -var _DoAutoLinks = function(text) { - - text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"
    $1"); - - // Email addresses: - - /* - text = text.replace(/ - < - (?:mailto:)? - ( - [-.\w]+ - \@ - [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ - ) - > - /gi, _DoAutoLinks_callback()); - */ - text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, - function(wholeMatch,m1) { - return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); - } - ); - - return text; -} - - -var _EncodeEmailAddress = function(addr) { -// -// Input: an email address, e.g. "foo@example.com" -// -// Output: the email address as a mailto link, with each character -// of the address encoded as either a decimal or hex entity, in -// the hopes of foiling most address harvesting spam bots. E.g.: -// -// foo -// @example.com -// -// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk -// mailing list: -// - - // attacklab: why can't javascript speak hex? - function char2hex(ch) { - var hexDigits = '0123456789ABCDEF'; - var dec = ch.charCodeAt(0); - return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15)); - } - - var encode = [ - function(ch){return "&#"+ch.charCodeAt(0)+";";}, - function(ch){return "&#x"+char2hex(ch)+";";}, - function(ch){return ch;} - ]; - - addr = "mailto:" + addr; - - addr = addr.replace(/./g, function(ch) { - if (ch == "@") { - // this *must* be encoded. I insist. - ch = encode[Math.floor(Math.random()*2)](ch); - } else if (ch !=":") { - // leave ':' alone (to spot mailto: later) - var r = Math.random(); - // roughly 10% raw, 45% hex, 45% dec - ch = ( - r > .9 ? encode[2](ch) : - r > .45 ? encode[1](ch) : - encode[0](ch) - ); - } - return ch; - }); - - addr = "" + addr + ""; - addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part - - return addr; -} - - -var _UnescapeSpecialChars = function(text) { -// -// Swap back in all the special characters we've hidden. -// - text = text.replace(/~E(\d+)E/g, - function(wholeMatch,m1) { - var charCodeToReplace = parseInt(m1); - return String.fromCharCode(charCodeToReplace); - } - ); - return text; -} - - -var _Outdent = function(text) { -// -// Remove one level of line-leading tabs or spaces -// - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - - text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width - - // attacklab: clean up hack - text = text.replace(/~0/g,"") - - return text; -} - -var _Detab = function(text) { -// attacklab: Detab's completely rewritten for speed. -// In perl we could fix it by anchoring the regexp with \G. -// In javascript we're less fortunate. - - // expand first n-1 tabs - text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width - - // replace the nth with two sentinels - text = text.replace(/\t/g,"~A~B"); - - // use the sentinel to anchor our regex so it doesn't explode - text = text.replace(/~B(.+?)~A/g, - function(wholeMatch,m1,m2) { - var leadingText = m1; - var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width - - // there *must* be a better way to do this: - for (var i=0; i/gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
    ")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.bash=function(a){var g="true false";var e="if then else elif fi for break continue while in do done echo exit return set declare";var c={cN:"variable",b:"\\$[a-zA-Z0-9_#]+"};var b={cN:"variable",b:"\\${([^}]|\\\\})+}"};var h={cN:"string",b:'"',e:'"',i:"\\n",c:[a.BE,c,b],r:0};var d={cN:"string",b:"'",e:"'",c:[{b:"''"}],r:0};var f={cN:"test_condition",b:"",e:"",c:[h,d,c,b],k:{literal:g},r:0};return{k:{keyword:e,literal:g},c:[{cN:"shebang",b:"(#!\\/bin\\/bash)|(#!\\/bin\\/sh)",r:10},c,b,a.HCM,h,d,a.inherit(f,{b:"\\[ ",e:" \\]",r:0}),a.inherit(f,{b:"\\[\\[ ",e:" \\]\\]"})]}}(hljs);hljs.LANGUAGES.cs=function(a){return{k:"abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while ascending descending from get group into join let orderby partial select set value var where yield",c:[{cN:"comment",b:"///",e:"$",rB:true,c:[{cN:"xmlDocTag",b:"///|"},{cN:"xmlDocTag",b:""}]},a.CLCM,a.CBLCLM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},a.ASM,a.QSM,a.CNM]}}(hljs);hljs.LANGUAGES.ruby=function(e){var a="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?";var j="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var g={keyword:"and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include"};var c={cN:"yardoctag",b:"@[A-Za-z]+"};var k=[{cN:"comment",b:"#",e:"$",c:[c]},{cN:"comment",b:"^\\=begin",e:"^\\=end",c:[c],r:10},{cN:"comment",b:"^__END__",e:"\\n$"}];var d={cN:"subst",b:"#\\{",e:"}",l:a,k:g};var i=[e.BE,d];var b=[{cN:"string",b:"'",e:"'",c:i,r:0},{cN:"string",b:'"',e:'"',c:i,r:0},{cN:"string",b:"%[qw]?\\(",e:"\\)",c:i},{cN:"string",b:"%[qw]?\\[",e:"\\]",c:i},{cN:"string",b:"%[qw]?{",e:"}",c:i},{cN:"string",b:"%[qw]?<",e:">",c:i,r:10},{cN:"string",b:"%[qw]?/",e:"/",c:i,r:10},{cN:"string",b:"%[qw]?%",e:"%",c:i,r:10},{cN:"string",b:"%[qw]?-",e:"-",c:i,r:10},{cN:"string",b:"%[qw]?\\|",e:"\\|",c:i,r:10}];var h={cN:"function",bWK:true,e:" |$|;",k:"def",c:[{cN:"title",b:j,l:a,k:g},{cN:"params",b:"\\(",e:"\\)",l:a,k:g}].concat(k)};var f=k.concat(b.concat([{cN:"class",bWK:true,e:"$|;",k:"class module",c:[{cN:"title",b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?",r:0},{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]}].concat(k)},h,{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:":",c:b.concat([{b:j}]),r:0},{cN:"symbol",b:a+":",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"number",b:"\\?\\w"},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:k.concat([{cN:"regexp",b:"/",e:"/[a-z]*",i:"\\n",c:[e.BE,d]}]),r:0}]));d.c=f;h.c[1].c=f;return{l:a,k:g,c:f}}(hljs);hljs.LANGUAGES.diff=function(a){return{c:[{cN:"chunk",b:"^\\@\\@ +\\-\\d+,\\d+ +\\+\\d+,\\d+ +\\@\\@$",r:10},{cN:"chunk",b:"^\\*\\*\\* +\\d+,\\d+ +\\*\\*\\*\\*$",r:10},{cN:"chunk",b:"^\\-\\-\\- +\\d+,\\d+ +\\-\\-\\-\\-$",r:10},{cN:"header",b:"Index: ",e:"$"},{cN:"header",b:"=====",e:"=====$"},{cN:"header",b:"^\\-\\-\\-",e:"$"},{cN:"header",b:"^\\*{3} ",e:"$"},{cN:"header",b:"^\\+\\+\\+",e:"$"},{cN:"header",b:"\\*{5}",e:"\\*{5}$"},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}}(hljs);hljs.LANGUAGES.javascript=function(a){return{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",i:"\\n",c:[{b:"\\\\/"}]},{b:"<",e:">;",sL:"xml"}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}(hljs);hljs.LANGUAGES.css=function(a){var b={cN:"function",b:a.IR+"\\(",e:"\\)",c:[a.NM,a.ASM,a.QSM]};return{cI:true,i:"[=/|']",c:[a.CBLCLM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",eE:true,k:"import page media charset",c:[b,a.ASM,a.QSM,a.NM]},{cN:"tag",b:a.IR,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[a.CBLCLM,{cN:"rule",b:"[^\\s]",rB:true,e:";",eW:true,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:true,i:"[^\\s]",starts:{cN:"value",eW:true,eE:true,c:[b,a.NM,a.QSM,a.ASM,a.CBLCLM,{cN:"hexcolor",b:"\\#[0-9A-F]+"},{cN:"important",b:"!important"}]}}]}]}]}}(hljs);hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[b],starts:{e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.http=function(a){return{i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:true,e:"$",c:[{cN:"string",b:" ",e:" ",eB:true,eE:true}]},{cN:"attribute",b:"^\\w",e:": ",eE:true,i:"\\n|\\s|=",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:"",eW:true}}]}}(hljs);hljs.LANGUAGES.java=function(a){return{k:"false synchronized int abstract float private char boolean static null if const for true while long throw strictfp finally protected import native final return void enum else break transient new catch instanceof byte super volatile case assert short package default double public try this switch continue throws",c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",c:[{cN:"javadoctag",b:"@[A-Za-z]+"}],r:10},a.CLCM,a.CBLCLM,a.ASM,a.QSM,{cN:"class",bWK:true,e:"{",k:"class interface",i:":",c:[{bWK:true,k:"extends implements",r:10},{cN:"title",b:a.UIR}]},a.CNM,{cN:"annotation",b:"@[A-Za-z]+"}]}}(hljs);hljs.LANGUAGES.php=function(a){var e={cN:"variable",b:"\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*"};var b=[a.inherit(a.ASM,{i:null}),a.inherit(a.QSM,{i:null}),{cN:"string",b:'b"',e:'"',c:[a.BE]},{cN:"string",b:"b'",e:"'",c:[a.BE]}];var c=[a.BNM,a.CNM];var d={cN:"title",b:a.UIR};return{cI:true,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return implements parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception php_user_filter default die require __FUNCTION__ enddeclare final try this switch continue endfor endif declare unset true false namespace trait goto instanceof insteadof __DIR__ __NAMESPACE__ __halt_compiler",c:[a.CLCM,a.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"}]},{cN:"comment",eB:true,b:"__halt_compiler.+?;",eW:true},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[a.BE]},{cN:"preprocessor",b:"<\\?php",r:10},{cN:"preprocessor",b:"\\?>"},e,{cN:"function",bWK:true,e:"{",k:"function",i:"\\$|\\[|%",c:[d,{cN:"params",b:"\\(",e:"\\)",c:["self",e,a.CBLCLM].concat(b).concat(c)}]},{cN:"class",bWK:true,e:"{",k:"class",i:"[:\\(\\$]",c:[{bWK:true,eW:true,k:"extends",c:[d]},d]},{b:"=>"}].concat(b).concat(c)}}(hljs);hljs.LANGUAGES.python=function(a){var f={cN:"prompt",b:"^(>>>|\\.\\.\\.) "};var c=[{cN:"string",b:"(u|b)?r?'''",e:"'''",c:[f],r:10},{cN:"string",b:'(u|b)?r?"""',e:'"""',c:[f],r:10},{cN:"string",b:"(u|r|ur)'",e:"'",c:[a.BE],r:10},{cN:"string",b:'(u|r|ur)"',e:'"',c:[a.BE],r:10},{cN:"string",b:"(b|br)'",e:"'",c:[a.BE]},{cN:"string",b:'(b|br)"',e:'"',c:[a.BE]}].concat([a.ASM,a.QSM]);var e={cN:"title",b:a.UIR};var d={cN:"params",b:"\\(",e:"\\)",c:["self",a.CNM,f].concat(c)};var b={bWK:true,e:":",i:"[${=;\\n]",c:[e,d],r:10};return{k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10",built_in:"None True False Ellipsis NotImplemented"},i:"(|\\?)",c:c.concat([f,a.HCM,a.inherit(b,{cN:"function",k:"def"}),a.inherit(b,{cN:"class",k:"class"}),a.CNM,{cN:"decorator",b:"@",e:"$"},{b:"\\b(print|exec)\\("}])}}(hljs);hljs.LANGUAGES.sql=function(a){return{cI:true,c:[{cN:"operator",b:"(begin|start|commit|rollback|savepoint|lock|alter|create|drop|rename|call|delete|do|handler|insert|load|replace|select|truncate|update|set|show|pragma|grant)\\b(?!:)",e:";",eW:true,k:{keyword:"all partial global month current_timestamp using go revoke smallint indicator end-exec disconnect zone with character assertion to add current_user usage input local alter match collate real then rollback get read timestamp session_user not integer bit unique day minute desc insert execute like ilike|2 level decimal drop continue isolation found where constraints domain right national some module transaction relative second connect escape close system_user for deferred section cast current sqlstate allocate intersect deallocate numeric public preserve full goto initially asc no key output collation group by union session both last language constraint column of space foreign deferrable prior connection unknown action commit view or first into float year primary cascaded except restrict set references names table outer open select size are rows from prepare distinct leading create only next inner authorization schema corresponding option declare precision immediate else timezone_minute external varying translation true case exception join hour default double scroll value cursor descriptor values dec fetch procedure delete and false int is describe char as at in varchar null trailing any absolute current_time end grant privileges when cross check write current_date pad begin temporary exec time update catalog user sql date on identity timezone_hour natural whenever interval work order cascade diagnostics nchar having left call do handler load replace truncate start lock show pragma exists number",aggregate:"count sum min max avg"},c:[{cN:"string",b:"'",e:"'",c:[a.BE,{b:"''"}],r:0},{cN:"string",b:'"',e:'"',c:[a.BE,{b:'""'}],r:0},{cN:"string",b:"`",e:"`",c:[a.BE]},a.CNM]},a.CBLCLM,{cN:"comment",b:"--",e:"$"}]}}(hljs);hljs.LANGUAGES.ini=function(a){return{cI:true,i:"[^\\s]",c:[{cN:"comment",b:";",e:"$"},{cN:"title",b:"^\\[",e:"\\]"},{cN:"setting",b:"^[a-z0-9\\[\\]_-]+[ \\t]*=[ \\t]*",e:"$",c:[{cN:"value",eW:true,k:"on off true false yes no",c:[a.QSM,a.NM]}]}]}}(hljs);hljs.LANGUAGES.perl=function(e){var a="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when";var d={cN:"subst",b:"[$@]\\{",e:"\\}",k:a,r:10};var b={cN:"variable",b:"\\$\\d"};var i={cN:"variable",b:"[\\$\\%\\@\\*](\\^\\w\\b|#\\w+(\\:\\:\\w+)*|[^\\s\\w{]|{\\w+}|\\w+(\\:\\:\\w*)*)"};var f=[e.BE,d,b,i];var h={b:"->",c:[{b:e.IR},{b:"{",e:"}"}]};var g={cN:"comment",b:"^(__END__|__DATA__)",e:"\\n$",r:5};var c=[b,i,e.HCM,g,{cN:"comment",b:"^\\=\\w",e:"\\=cut",eW:true},h,{cN:"string",b:"q[qwxr]?\\s*\\(",e:"\\)",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\[",e:"\\]",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\{",e:"\\}",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\|",e:"\\|",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\<",e:"\\>",c:f,r:5},{cN:"string",b:"qw\\s+q",e:"q",c:f,r:5},{cN:"string",b:"'",e:"'",c:[e.BE],r:0},{cN:"string",b:'"',e:'"',c:f,r:0},{cN:"string",b:"`",e:"`",c:[e.BE]},{cN:"string",b:"{\\w+}",r:0},{cN:"string",b:"-?\\w+\\s*\\=\\>",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"("+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,g,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bWK:true,e:"(\\s*\\(.*?\\))?[;{]",k:"sub",r:5},{cN:"operator",b:"-\\w\\b",r:0}];d.c=c;h.c[1].c=c;return{k:a,c:c}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs);hljs.LANGUAGES.cpp=function(a){var b={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long throw volatile static protected bool template mutable if public friend do return goto auto void enum else break new extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue wchar_t inline delete alignof char16_t char32_t constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr"};return{k:b,i:"",k:b,r:10,c:["self"]}]}}(hljs);hljs.LANGUAGES.clojure=function(l){var e={built_in:"def cond apply if-not if-let if not not= = < < > <= <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for doseq dosync dotimes and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import intern refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! import use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if throw printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time ns assert re-find re-groups rand-int rand mod locking assert-valid-fdecl alias namespace resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! memfn to-array future future-call into-array aset gen-class reduce merge map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"};var f="[a-zA-Z_0-9\\!\\.\\?\\-\\+\\*\\/\\<\\=\\>\\&\\#\\$';]+";var a="[\\s:\\(\\{]+\\d+(\\.\\d+)?";var d={cN:"number",b:a,r:0};var j={cN:"string",b:'"',e:'"',c:[l.BE],r:0};var o={cN:"comment",b:";",e:"$",r:0};var n={cN:"collection",b:"[\\[\\{]",e:"[\\]\\}]"};var c={cN:"comment",b:"\\^"+f};var b={cN:"comment",b:"\\^\\{",e:"\\}"};var h={cN:"attribute",b:"[:]"+f};var m={cN:"list",b:"\\(",e:"\\)",r:0};var g={eW:true,eE:true,k:{literal:"true false nil"},r:0};var i={k:e,l:f,cN:"title",b:f,starts:g};m.c=[{cN:"comment",b:"comment"},i];g.c=[m,j,c,b,o,h,n,d];n.c=[m,j,c,o,h,n,d];return{i:"\\S",c:[o,m]}}(hljs); \ No newline at end of file diff --git a/plugin/markdown/example.html b/plugin/markdown/example.html new file mode 100644 index 0000000..b4e7f91 --- /dev/null +++ b/plugin/markdown/example.html @@ -0,0 +1,97 @@ + + + + + + + reveal.js - Markdown Demo + + + + + + + +
    + +
    + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    +
    + + + + + + + + diff --git a/plugin/markdown/example.md b/plugin/markdown/example.md new file mode 100644 index 0000000..e988dd9 --- /dev/null +++ b/plugin/markdown/example.md @@ -0,0 +1,29 @@ +# Markdown Demo + + + +## External 1.1 + +Content 1.1 + + +## External 1.2 + +Content 1.2 + + + +## External 2 + +Content 2.1 + + + +## External 3.1 + +Content 3.1 + + +## External 3.2 + +Content 3.2 diff --git a/plugin/markdown/markdown.js b/plugin/markdown/markdown.js new file mode 100644 index 0000000..39e1168 --- /dev/null +++ b/plugin/markdown/markdown.js @@ -0,0 +1,147 @@ +// From https://gist.github.com/1343518 +// Modified by Hakim to handle Markdown indented with tabs +(function(){ + + if( typeof Showdown === 'undefined' ) { + throw 'The reveal.js Markdown plugin requires Showdown to be loaded'; + } + + var stripLeadingWhitespace = function(section) { + + var template = section.querySelector( 'script' ); + + // strip leading whitespace so it isn't evaluated as code + var text = ( template || section ).textContent; + + var leadingWs = text.match(/^\n?(\s*)/)[1].length, + leadingTabs = text.match(/^\n?(\t*)/)[1].length; + + if( leadingTabs > 0 ) { + text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' ); + } + else if( leadingWs > 1 ) { + text = text.replace( new RegExp('\\n? {' + leadingWs + '}','g'), '\n' ); + } + + return text; + + }; + + var slidifyMarkdown = function(markdown, separator, vertical) { + + separator = separator || '^\n---\n$'; + + var reSeparator = new RegExp(separator + (vertical ? '|' + vertical : ''), 'mg'), + reHorSeparator = new RegExp(separator), + matches, + lastIndex = 0, + isHorizontal, + wasHorizontal = true, + content, + sectionStack = [], + markdownSections = ''; + + // iterate until all blocks between separators are stacked up + while( matches = reSeparator.exec(markdown) ) { + + // determine direction (horizontal by default) + isHorizontal = reHorSeparator.test(matches[0]); + + if( !isHorizontal && wasHorizontal ) { + // create vertical stack + sectionStack.push([]); + } + + // pluck slide content from markdown input + content = markdown.substring(lastIndex, matches.index); + + if( isHorizontal && wasHorizontal ) { + // add to horizontal stack + sectionStack.push(content); + } else { + // add to vertical stack + sectionStack[sectionStack.length-1].push(content); + } + + lastIndex = reSeparator.lastIndex; + wasHorizontal = isHorizontal; + + } + + // add the remaining slide + (wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1]).push(markdown.substring(lastIndex)); + + // flatten the hierarchical stack, and insert
    tags + for( var k = 0, klen = sectionStack.length; k < klen; k++ ) { + markdownSections += typeof sectionStack[k] === 'string' + ? '
    ' + sectionStack[k] + '
    ' + : '
    ' + sectionStack[k].join('
    ') + '
    '; + } + + return markdownSections; + }; + + var querySlidingMarkdown = function() { + + var sections = document.querySelectorAll( '[data-markdown]'), + section; + + for( var j = 0, jlen = sections.length; j < jlen; j++ ) { + + section = sections[j]; + + if( section.getAttribute('data-markdown').length ) { + + var xhr = new XMLHttpRequest(), + url = section.getAttribute('data-markdown'); + + xhr.onreadystatechange = function () { + if( xhr.readyState === 4 ) { + section.outerHTML = slidifyMarkdown( xhr.responseText, section.getAttribute('data-separator'), section.getAttribute('data-vertical') ); + } + }; + + xhr.open('GET', url, false); + xhr.send(); + + } else if( section.getAttribute('data-separator') ) { + + var markdown = stripLeadingWhitespace(section); + section.outerHTML = slidifyMarkdown( markdown, section.getAttribute('data-separator'), section.getAttribute('data-vertical') ); + + } + } + + }; + + var queryMarkdownSlides = function() { + + var sections = document.querySelectorAll( '[data-markdown]'); + + for( var j = 0, jlen = sections.length; j < jlen; j++ ) { + + makeHtml(sections[j]); + + } + + }; + + var makeHtml = function(section) { + + var notes = section.querySelector( 'aside.notes' ); + + var markdown = stripLeadingWhitespace(section); + + section.innerHTML = (new Showdown.converter()).makeHtml(markdown); + + if( notes ) { + section.appendChild( notes ); + } + + }; + + querySlidingMarkdown(); + + queryMarkdownSlides(); + +})(); diff --git a/plugin/markdown/showdown.js b/plugin/markdown/showdown.js new file mode 100644 index 0000000..3f280f4 --- /dev/null +++ b/plugin/markdown/showdown.js @@ -0,0 +1,62 @@ +// +// showdown.js -- A javascript port of Markdown. +// +// Copyright (c) 2007 John Fraser. +// +// Original Markdown Copyright (c) 2004-2005 John Gruber +// +// +// Redistributable under a BSD-style open source license. +// See license.txt for more information. +// +// The full source distribution is at: +// +// A A L +// T C A +// T K B +// +// +// +// +// Wherever possible, Showdown is a straight, line-by-line port +// of the Perl version of Markdown. +// +// This is not a normal parser design; it's basically just a +// series of string substitutions. It's hard to read and +// maintain this way, but keeping Showdown close to the original +// design makes it easier to port new features. +// +// More importantly, Showdown behaves like markdown.pl in most +// edge cases. So web applications can do client-side preview +// in Javascript, and then build identical HTML on the server. +// +// This port needs the new RegExp functionality of ECMA 262, +// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers +// should do fine. Even with the new regular expression features, +// We do a lot of work to emulate Perl's regex functionality. +// The tricky changes in this file mostly have the "attacklab:" +// label. Major or self-explanatory changes don't. +// +// Smart diff tools like Araxis Merge will be able to match up +// this file with markdown.pl in a useful way. A little tweaking +// helps: in a copy of markdown.pl, replace "#" with "//" and +// replace "$text" with "text". Be sure to ignore whitespace +// and line endings. +// +// +// Showdown usage: +// +// var text = "Markdown *rocks*."; +// +// var converter = new Showdown.converter(); +// var html = converter.makeHtml(text); +// +// alert(html); +// +// Note: move the sample code to the bottom of this +// file before uncommenting it. +// +// +// Showdown namespace +// +var Showdown={};Showdown.converter=function(){var a,b,c,d=0;this.makeHtml=function(d){return a=new Array,b=new Array,c=new Array,d=d.replace(/~/g,"~T"),d=d.replace(/\$/g,"~D"),d=d.replace(/\r\n/g,"\n"),d=d.replace(/\r/g,"\n"),d="\n\n"+d+"\n\n",d=F(d),d=d.replace(/^[ \t]+$/mg,""),d=f(d),d=e(d),d=h(d),d=D(d),d=d.replace(/~D/g,"$$"),d=d.replace(/~T/g,"~"),d};var e=function(c){var c=c.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,function(c,d,e,f,g){return d=d.toLowerCase(),a[d]=z(e),f?f+g:(g&&(b[d]=g.replace(/"/g,""")),"")});return c},f=function(a){a=a.replace(/\n/g,"\n\n");var b="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del",c="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math";return a=a.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,g),a=a.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,g),a=a.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,g),a=a.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,g),a=a.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,g),a=a.replace(/\n\n/g,"\n"),a},g=function(a,b){var d=b;return d=d.replace(/\n\n/g,"\n"),d=d.replace(/^\n/,""),d=d.replace(/\n+$/g,""),d="\n\n~K"+(c.push(d)-1)+"K\n\n",d},h=function(a){a=o(a);var b=t("
    ");return a=a.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,b),a=a.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,b),a=a.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,b),a=q(a),a=s(a),a=r(a),a=x(a),a=f(a),a=y(a),a},i=function(a){return a=u(a),a=j(a),a=A(a),a=m(a),a=k(a),a=B(a),a=z(a),a=w(a),a=a.replace(/ +\n/g,"
    \n"),a},j=function(a){var b=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi;return a=a.replace(b,function(a){var b=a.replace(/(.)<\/?code>(?=.)/g,"$1`");return b=G(b,"\\`*_"),b}),a},k=function(a){return a=a.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,l),a=a.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,l),a=a.replace(/(\[([^\[\]]+)\])()()()()()/g,l),a},l=function(c,d,e,f,g,h,i,j){j==undefined&&(j="");var k=d,l=e,m=f.toLowerCase(),n=g,o=j;if(n==""){m==""&&(m=l.toLowerCase().replace(/ ?\n/g," ")),n="#"+m;if(a[m]!=undefined)n=a[m],b[m]!=undefined&&(o=b[m]);else{if(!(k.search(/\(\s*\)$/m)>-1))return k;n=""}}n=G(n,"*_");var p='",p},m=function(a){return a=a.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,n),a=a.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,n),a},n=function(c,d,e,f,g,h,i,j){var k=d,l=e,m=f.toLowerCase(),n=g,o=j;o||(o="");if(n==""){m==""&&(m=l.toLowerCase().replace(/ ?\n/g," ")),n="#"+m;if(a[m]==undefined)return k;n=a[m],b[m]!=undefined&&(o=b[m])}l=l.replace(/"/g,"""),n=G(n,"*_");var p=''+l+''+i(c)+"")}),a=a.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(a,c){return t('

    '+i(c)+"

    ")}),a=a.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(a,c,d){var e=c.length;return t("'+i(d)+"")}),a},p,q=function(a){a+="~0";var b=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;return d?a=a.replace(b,function(a,b,c){var d=b,e=c.search(/[*+-]/g)>-1?"ul":"ol";d=d.replace(/\n{2,}/g,"\n\n\n");var f=p(d);return f=f.replace(/\s+$/,""),f="<"+e+">"+f+"\n",f}):(b=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g,a=a.replace(b,function(a,b,c,d){var e=b,f=c,g=d.search(/[*+-]/g)>-1?"ul":"ol",f=f.replace(/\n{2,}/g,"\n\n\n"),h=p(f);return h=e+"<"+g+">\n"+h+"\n",h})),a=a.replace(/~0/,""),a};p=function(a){return d++,a=a.replace(/\n{2,}$/,"\n"),a+="~0",a=a.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,function(a,b,c,d,e){var f=e,g=b,j=c;return g||f.search(/\n{2,}/)>-1?f=h(E(f)):(f=q(E(f)),f=f.replace(/\n$/,""),f=i(f)),"
  • "+f+"
  • \n"}),a=a.replace(/~0/g,""),d--,a};var r=function(a){return a+="~0",a=a.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(a,b,c){var d=b,e=c;return d=v(E(d)),d=F(d),d=d.replace(/^\n+/g,""),d=d.replace(/\n+$/g,""),d="
    "+d+"\n
    ",t(d)+e}),a=a.replace(/~0/,""),a},s=function(a){return a+="~0",a=a.replace(/\n```(.*)\n([^`]+)\n```/g,function(a,b,c){var d=b,e=c;return e=v(e),e=F(e),e=e.replace(/^\n+/g,""),e=e.replace(/\n+$/g,""),e="
    "+e+"\n
    ",t(e)}),a=a.replace(/~0/,""),a},t=function(a){return a=a.replace(/(^\n+|\n+$)/g,""),"\n\n~K"+(c.push(a)-1)+"K\n\n"},u=function(a){return a=a.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(a,b,c,d,e){var f=d;return f=f.replace(/^([ \t]*)/g,""),f=f.replace(/[ \t]*$/g,""),f=v(f),b+""+f+""}),a},v=function(a){return a=a.replace(/&/g,"&"),a=a.replace(//g,">"),a=G(a,"*_{}[]\\",!1),a},w=function(a){return a=a.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,"$2"),a=a.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"$2"),a},x=function(a){return a=a.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(a,b){var c=b;return c=c.replace(/^[ \t]*>[ \t]?/gm,"~0"),c=c.replace(/~0/g,""),c=c.replace(/^[ \t]+$/gm,""),c=h(c),c=c.replace(/(^|\n)/g,"$1 "),c=c.replace(/(\s*
    [^\r]+?<\/pre>)/gm,function(a,b){var c=b;return c=c.replace(/^  /mg,"~0"),c=c.replace(/~0/g,""),c}),t("
    \n"+c+"\n
    ")}),a},y=function(a){a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,"");var b=a.split(/\n{2,}/g),d=new Array,e=b.length;for(var f=0;f=0?d.push(g):g.search(/\S/)>=0&&(g=i(g),g=g.replace(/^([ \t]*)/g,"

    "),g+="

    ",d.push(g))}e=d.length;for(var f=0;f=0){var h=c[RegExp.$1];h=h.replace(/\$/g,"$$$$"),d[f]=d[f].replace(/~K\d+K/,h)}return d.join("\n\n")},z=function(a){return a=a.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"),a=a.replace(/<(?![a-z\/?\$!])/gi,"<"),a},A=function(a){return a=a.replace(/\\(\\)/g,H),a=a.replace(/\\([`*_{}\[\]()>#+-.!])/g,H),a},B=function(a){return a=a.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,'
    $1'),a=a.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,function(a,b){return C(D(b))}),a},C=function(a){function b(a){var b="0123456789ABCDEF",c=a.charCodeAt(0);return b.charAt(c>>4)+b.charAt(c&15)}var c=[function(a){return"&#"+a.charCodeAt(0)+";"},function(a){return"&#x"+b(a)+";"},function(a){return a}];return a="mailto:"+a,a=a.replace(/./g,function(a){if(a=="@")a=c[Math.floor(Math.random()*2)](a);else if(a!=":"){var b=Math.random();a=b>.9?c[2](a):b>.45?c[1](a):c[0](a)}return a}),a=''+a+"",a=a.replace(/">.+:/g,'">'),a},D=function(a){return a=a.replace(/~E(\d+)E/g,function(a,b){var c=parseInt(b);return String.fromCharCode(c)}),a},E=function(a){return a=a.replace(/^(\t|[ ]{1,4})/gm,"~0"),a=a.replace(/~0/g,""),a},F=function(a){return a=a.replace(/\t(?=\t)/g," "),a=a.replace(/\t/g,"~A~B"),a=a.replace(/~B(.+?)~A/g,function(a,b,c){var d=b,e=4-d.length%4;for(var f=0;f
    + diff --git a/plugin/notes/notes.html b/plugin/notes/notes.html new file mode 100644 index 0000000..e14c6ac --- /dev/null +++ b/plugin/notes/notes.html @@ -0,0 +1,252 @@ + + + + + + reveal.js - Slide Notes + + + + + + +
    + +
    + +
    + + UPCOMING: +
    + +
    +
    +

    Time

    + 0:00:00 AM +
    +
    +

    Elapsed

    + 00:00:00 +
    +
    + +
    + + + + + diff --git a/plugin/notes/notes.js b/plugin/notes/notes.js new file mode 100644 index 0000000..63de05a --- /dev/null +++ b/plugin/notes/notes.js @@ -0,0 +1,100 @@ +/** + * Handles opening of and synchronization with the reveal.js + * notes window. + */ +var RevealNotes = (function() { + + function openNotes() { + var jsFileLocation = document.querySelector('script[src$="notes.js"]').src; // this js file path + jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, ''); // the js folder path + var notesPopup = window.open( jsFileLocation + 'notes.html', 'reveal.js - Notes', 'width=1120,height=850' ); + + // Fires when slide is changed + Reveal.addEventListener( 'slidechanged', function( event ) { + post('slidechanged'); + } ); + + // Fires when a fragment is shown + Reveal.addEventListener( 'fragmentshown', function( event ) { + post('fragmentshown'); + } ); + + // Fires when a fragment is hidden + Reveal.addEventListener( 'fragmenthidden', function( event ) { + post('fragmenthidden'); + } ); + + /** + * Posts the current slide data to the notes window + * + * @param {String} eventType Expecting 'slidechanged', 'fragmentshown' + * or 'fragmenthidden' set in the events above to define the needed + * slideDate. + */ + function post( eventType ) { + var slideElement = Reveal.getCurrentSlide(), + messageData; + + if( eventType === 'slidechanged' ) { + var notes = slideElement.querySelector( 'aside.notes' ), + indexh = Reveal.getIndices().h, + indexv = Reveal.getIndices().v, + nextindexh, + nextindexv; + + if( slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION' ) { + nextindexh = indexh; + nextindexv = indexv + 1; + } else { + nextindexh = indexh + 1; + nextindexv = 0; + } + + messageData = { + notes : notes ? notes.innerHTML : '', + indexh : indexh, + indexv : indexv, + nextindexh : nextindexh, + nextindexv : nextindexv, + markdown : notes ? typeof notes.getAttribute( 'data-markdown' ) === 'string' : false + }; + } + else if( eventType === 'fragmentshown' ) { + messageData = { + fragment : 'next' + }; + } + else if( eventType === 'fragmenthidden' ) { + messageData = { + fragment : 'prev' + }; + } + + notesPopup.postMessage( JSON.stringify( messageData ), '*' ); + } + + // Navigate to the current slide when the notes are loaded + notesPopup.addEventListener( 'load', function( event ) { + post('slidechanged'); + }, false ); + } + + // If the there's a 'notes' query set, open directly + if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) { + openNotes(); + } + + // Open the notes when the 's' key is hit + document.addEventListener( 'keydown', function( event ) { + // Disregard the event if the target is editable or a + // modifier is present + if ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return; + + if( event.keyCode === 83 ) { + event.preventDefault(); + openNotes(); + } + }, false ); + + return { open: openNotes }; +})(); diff --git a/plugin/postmessage/example.html b/plugin/postmessage/example.html new file mode 100644 index 0000000..cc57a7b --- /dev/null +++ b/plugin/postmessage/example.html @@ -0,0 +1,39 @@ + + + + + +
    + + + +
    + + + + + diff --git a/plugin/postmessage/postmessage.js b/plugin/postmessage/postmessage.js new file mode 100644 index 0000000..d0f4140 --- /dev/null +++ b/plugin/postmessage/postmessage.js @@ -0,0 +1,42 @@ +/* + + simple postmessage plugin + + Useful when a reveal slideshow is inside an iframe. + It allows to call reveal methods from outside. + + Example: + var reveal = window.frames[0]; + + // Reveal.prev(); + reveal.postMessage(JSON.stringify({method: 'prev', args: []}), '*'); + // Reveal.next(); + reveal.postMessage(JSON.stringify({method: 'next', args: []}), '*'); + // Reveal.slide(2, 2); + reveal.postMessage(JSON.stringify({method: 'slide', args: [2,2]}), '*'); + + Add to the slideshow: + + dependencies: [ + ... + { src: 'plugin/postmessage/postmessage.js', async: true, condition: function() { return !!document.body.classList; } } + ] + +*/ + +(function (){ + + window.addEventListener( "message", function ( event ) { + var data = JSON.parse( event.data ), + method = data.method, + args = data.args; + + if( typeof Reveal[method] === 'function' ) { + Reveal[method].apply( Reveal, data.args ); + } + }, false); + +}()); + + + diff --git a/plugin/print-pdf/print-pdf.js b/plugin/print-pdf/print-pdf.js new file mode 100644 index 0000000..2b1d691 --- /dev/null +++ b/plugin/print-pdf/print-pdf.js @@ -0,0 +1,39 @@ +/** + * phantomjs script for printing presentations to PDF. + * + * Example: + * phantomjs print-pdf.js "http://lab.hakim.se/reveal-js?print-pdf" reveal-demo.pdf + * + * By Manuel Bieh (https://github.com/manuelbieh) + */ + +// html2pdf.js +var page = new WebPage(); +var system = require( 'system' ); + +page.paperSize = { + format: 'A4', + orientation: 'landscape', + margin: { + left: '0', + right: '0', + top: '0', + bottom: '0' + } +}; +page.zoomFactor = 1.5; + +var revealFile = system.args[1] || 'index.html?print-pdf'; +var slideFile = system.args[2] || 'slides.pdf'; + +if( slideFile.match( /\.pdf$/gi ) === null ) { + slideFile += '.pdf'; +} + +console.log( 'Printing PDF...' ); + +page.open( revealFile, function( status ) { + console.log( 'Printed succesfully' ); + page.render( slideFile ); + phantom.exit(); +} ); \ No newline at end of file diff --git a/plugin/remotes/remotes.js b/plugin/remotes/remotes.js new file mode 100644 index 0000000..a1f10b8 --- /dev/null +++ b/plugin/remotes/remotes.js @@ -0,0 +1,30 @@ +/** + * Touch-based remote controller for your presentation courtesy + * of the folks at http://remotes.io + */ + +(function(window){ + + /** + * Detects if we are dealing with a touch enabled device (with some false positives) + * Borrowed from modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touch.js + */ + var hasTouch = (function(){ + return ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; + })(); + + if(!hasTouch){ + head.ready( 'remotes.ne.min.js', function() { + new Remotes("preview") + .on("swipe-left", function(e){ Reveal.right(); }) + .on("swipe-right", function(e){ Reveal.left(); }) + .on("swipe-up", function(e){ Reveal.down(); }) + .on("swipe-down", function(e){ Reveal.up(); }) + .on("tap", function(e){ + Reveal.toggleOverview(); + }); + } ); + + head.js('https://raw.github.com/Remotes/Remotes/master/dist/remotes.ne.min.js'); + } +})(window); \ No newline at end of file diff --git a/plugin/zoom-js/zoom.js b/plugin/zoom-js/zoom.js new file mode 100644 index 0000000..b67ae16 --- /dev/null +++ b/plugin/zoom-js/zoom.js @@ -0,0 +1,256 @@ +// Custom reveal.js integration +(function(){ + var isEnabled = true; + + document.querySelector( '.reveal' ).addEventListener( 'mousedown', function( event ) { + if( event.altKey && isEnabled ) { + event.preventDefault(); + zoom.to({ element: event.target, pan: false }); + } + } ); + + Reveal.addEventListener( 'overviewshown', function() { isEnabled = false; } ); + Reveal.addEventListener( 'overviewhidden', function() { isEnabled = true; } ); +})(); + +/*! + * zoom.js 0.2 (modified version for use with reveal.js) + * http://lab.hakim.se/zoom-js + * MIT licensed + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +var zoom = (function(){ + + // The current zoom level (scale) + var level = 1; + + // The current mouse position, used for panning + var mouseX = 0, + mouseY = 0; + + // Timeout before pan is activated + var panEngageTimeout = -1, + panUpdateInterval = -1; + + var currentOptions = null; + + // Check for transform support so that we can fallback otherwise + var supportsTransforms = '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; + + if( supportsTransforms ) { + // The easing that will be applied when we zoom in/out + document.body.style.transition = 'transform 0.8s ease'; + document.body.style.OTransition = '-o-transform 0.8s ease'; + document.body.style.msTransition = '-ms-transform 0.8s ease'; + document.body.style.MozTransition = '-moz-transform 0.8s ease'; + document.body.style.WebkitTransition = '-webkit-transform 0.8s ease'; + } + + // Zoom out if the user hits escape + document.addEventListener( 'keyup', function( event ) { + if( level !== 1 && event.keyCode === 27 ) { + zoom.out(); + } + }, false ); + + // Monitor mouse movement for panning + document.addEventListener( 'mousemove', function( event ) { + if( level !== 1 ) { + mouseX = event.clientX; + mouseY = event.clientY; + } + }, false ); + + /** + * Applies the CSS required to zoom in, prioritizes use of CSS3 + * transforms but falls back on zoom for IE. + * + * @param {Number} pageOffsetX + * @param {Number} pageOffsetY + * @param {Number} elementOffsetX + * @param {Number} elementOffsetY + * @param {Number} scale + */ + function magnify( pageOffsetX, pageOffsetY, elementOffsetX, elementOffsetY, scale ) { + + if( supportsTransforms ) { + var origin = pageOffsetX +'px '+ pageOffsetY +'px', + transform = 'translate('+ -elementOffsetX +'px,'+ -elementOffsetY +'px) scale('+ scale +')'; + + document.body.style.transformOrigin = origin; + document.body.style.OTransformOrigin = origin; + document.body.style.msTransformOrigin = origin; + document.body.style.MozTransformOrigin = origin; + document.body.style.WebkitTransformOrigin = origin; + + document.body.style.transform = transform; + document.body.style.OTransform = transform; + document.body.style.msTransform = transform; + document.body.style.MozTransform = transform; + document.body.style.WebkitTransform = transform; + } + else { + // Reset all values + if( scale === 1 ) { + document.body.style.position = ''; + document.body.style.left = ''; + document.body.style.top = ''; + document.body.style.width = ''; + document.body.style.height = ''; + document.body.style.zoom = ''; + } + // Apply scale + else { + document.body.style.position = 'relative'; + document.body.style.left = ( - ( pageOffsetX + elementOffsetX ) / scale ) + 'px'; + document.body.style.top = ( - ( pageOffsetY + elementOffsetY ) / scale ) + 'px'; + document.body.style.width = ( scale * 100 ) + '%'; + document.body.style.height = ( scale * 100 ) + '%'; + document.body.style.zoom = scale; + } + } + + level = scale; + + if( level !== 1 && document.documentElement.classList ) { + document.documentElement.classList.add( 'zoomed' ); + } + else { + document.documentElement.classList.remove( 'zoomed' ); + } + } + + /** + * Pan the document when the mosue cursor approaches the edges + * of the window. + */ + function pan() { + var range = 0.12, + rangeX = window.innerWidth * range, + rangeY = window.innerHeight * range, + scrollOffset = getScrollOffset(); + + // Up + if( mouseY < rangeY ) { + window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) ); + } + // Down + else if( mouseY > window.innerHeight - rangeY ) { + window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) ); + } + + // Left + if( mouseX < rangeX ) { + window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y ); + } + // Right + else if( mouseX > window.innerWidth - rangeX ) { + window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y ); + } + } + + function getScrollOffset() { + return { + x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset, + y: window.scrollY !== undefined ? window.scrollY : window.pageXYffset + } + } + + return { + /** + * Zooms in on either a rectangle or HTML element. + * + * @param {Object} options + * - element: HTML element to zoom in on + * OR + * - x/y: coordinates in non-transformed space to zoom in on + * - width/height: the portion of the screen to zoom in on + * - scale: can be used instead of width/height to explicitly set scale + */ + to: function( options ) { + // Due to an implementation limitation we can't zoom in + // to another element without zooming out first + if( level !== 1 ) { + zoom.out(); + } + else { + options.x = options.x || 0; + options.y = options.y || 0; + + // If an element is set, that takes precedence + if( !!options.element ) { + // Space around the zoomed in element to leave on screen + var padding = 20; + + options.width = options.element.getBoundingClientRect().width + ( padding * 2 ); + options.height = options.element.getBoundingClientRect().height + ( padding * 2 ); + options.x = options.element.getBoundingClientRect().left - padding; + options.y = options.element.getBoundingClientRect().top - padding; + } + + // If width/height values are set, calculate scale from those values + if( options.width !== undefined && options.height !== undefined ) { + options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 ); + } + + if( options.scale > 1 ) { + options.x *= options.scale; + options.y *= options.scale; + + var scrollOffset = getScrollOffset(); + + if( options.element ) { + scrollOffset.x -= ( window.innerWidth - ( options.width * options.scale ) ) / 2; + } + + magnify( scrollOffset.x, scrollOffset.y, options.x, options.y, options.scale ); + + if( options.pan !== false ) { + + // Wait with engaging panning as it may conflict with the + // zoom transition + panEngageTimeout = setTimeout( function() { + panUpdateInterval = setInterval( pan, 1000 / 60 ); + }, 800 ); + + } + } + + currentOptions = options; + } + }, + + /** + * Resets the document zoom state to its default. + */ + out: function() { + clearTimeout( panEngageTimeout ); + clearInterval( panUpdateInterval ); + + var scrollOffset = getScrollOffset(); + + if( currentOptions && currentOptions.element ) { + scrollOffset.x -= ( window.innerWidth - ( currentOptions.width * currentOptions.scale ) ) / 2; + } + + magnify( scrollOffset.x, scrollOffset.y, 0, 0, 1 ); + + level = 1; + }, + + // Alias + magnify: function( options ) { this.to( options ) }, + reset: function() { this.out() }, + + zoomLevel: function() { + return level; + } + } + +})(); +