windhamdavid 8 years ago
parent
commit
ae2d3536c2

File diff suppressed because it is too large
+ 27 - 9
public/js/plugin/highlight/highlight.js


File diff suppressed because it is too large
+ 14 - 0
public/js/plugin/leap/leap.js


+ 129 - 0
public/js/plugin/markdown/example.html

@@ -0,0 +1,129 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Markdown Demo</title>
+
+		<link rel="stylesheet" href="../../css/reveal.css">
+		<link rel="stylesheet" href="../../css/theme/default.css" id="theme">
+
+        <link rel="stylesheet" href="../../lib/css/zenburn.css">
+	</head>
+
+	<body>
+
+		<div class="reveal">
+
+			<div class="slides">
+
+                <!-- Use external markdown resource, separate slides by three newlines; vertical slides by two newlines -->
+                <section data-markdown="example.md" data-separator="^\n\n\n" data-vertical="^\n\n"></section>
+
+                <!-- Slides are separated by three dashes (quick 'n dirty regular expression) -->
+                <section data-markdown data-separator="---">
+                    <script type="text/template">
+                        ## Demo 1
+                        Slide 1
+                        ---
+                        ## Demo 1
+                        Slide 2
+                        ---
+                        ## Demo 1
+                        Slide 3
+                    </script>
+                </section>
+
+                <!-- Slides are separated by newline + three dashes + newline, vertical slides identical but two dashes -->
+                <section data-markdown data-separator="^\n---\n$" data-vertical="^\n--\n$">
+                    <script type="text/template">
+                        ## Demo 2
+                        Slide 1.1
+
+                        --
+
+                        ## Demo 2
+                        Slide 1.2
+
+                        ---
+
+                        ## Demo 2
+                        Slide 2
+                    </script>
+                </section>
+
+                <!-- No "extra" slides, since there are no separators defined (so they'll become horizontal rulers) -->
+                <section data-markdown>
+                    <script type="text/template">
+                        A
+
+                        ---
+
+                        B
+
+                        ---
+
+                        C
+                    </script>
+                </section>
+
+                <!-- Slide attributes -->
+                <section data-markdown>
+                    <script type="text/template">
+                        <!-- .slide: data-background="#000000" -->
+                        ## Slide attributes
+                    </script>
+                </section>
+
+                <!-- Element attributes -->
+                <section data-markdown>
+                    <script type="text/template">
+                        ## Element attributes
+                        - Item 1 <!-- .element: class="fragment" data-fragment-index="2" -->
+                        - Item 2 <!-- .element: class="fragment" data-fragment-index="1" -->
+                    </script>
+                </section>
+
+                <!-- Code -->
+                <section data-markdown>
+                    <script type="text/template">
+                        ```php
+                        public function foo()
+                        {
+                            $foo = array(
+                                'bar' => 'bar'
+                            )
+                        }
+                        ```
+                    </script>
+                </section>
+
+            </div>
+		</div>
+
+		<script src="../../lib/js/head.min.js"></script>
+		<script src="../../js/reveal.js"></script>
+
+		<script>
+
+			Reveal.initialize({
+				controls: true,
+				progress: true,
+				history: true,
+				center: true,
+
+				// Optional libraries used to extend on reveal.js
+				dependencies: [
+					{ src: '../../lib/js/classList.js', condition: function() { return !document.body.classList; } },
+					{ src: 'marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
+                    { src: 'markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
+                    { src: '../highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
+					{ src: '../notes/notes.js' }
+				]
+			});
+
+		</script>
+
+	</body>
+</html>

+ 31 - 0
public/js/plugin/markdown/example.md

@@ -0,0 +1,31 @@
+# Markdown Demo
+
+
+
+## External 1.1
+
+Content 1.1
+
+Note: This will only appear in the speaker notes window.
+
+
+## External 1.2
+
+Content 1.2
+
+
+
+## External 2
+
+Content 2.1
+
+
+
+## External 3.1
+
+Content 3.1
+
+
+## External 3.2
+
+Content 3.2

+ 382 - 27
public/js/plugin/markdown/markdown.js

@@ -1,37 +1,392 @@
-// From https://gist.github.com/1343518
-// Modified by Hakim to handle Markdown indented with tabs
-(function(){
+/**
+ * The reveal.js markdown plugin. Handles parsing of
+ * markdown inside of presentations as well as loading
+ * of external markdown documents.
+ */
+(function( root, factory ) {
+	if( typeof exports === 'object' ) {
+		module.exports = factory( require( './marked' ) );
+	}
+	else {
+		// Browser globals (root is window)
+		root.RevealMarkdown = factory( root.marked );
+		root.RevealMarkdown.initialize();
+	}
+}( this, function( marked ) {
 
-    if( typeof Showdown === 'undefined' ) {
-        throw 'The reveal.js Markdown plugin requires Showdown to be loaded';
-    }
+	if( typeof marked === 'undefined' ) {
+		throw 'The reveal.js Markdown plugin requires marked to be loaded';
+	}
 
-    var sections = document.querySelectorAll( '[data-markdown]' );
+	if( typeof hljs !== 'undefined' ) {
+		marked.setOptions({
+			highlight: function( lang, code ) {
+				return hljs.highlightAuto( lang, code ).value;
+			}
+		});
+	}
 
-    for( var i = 0, len = sections.length; i < len; i++ ) {
-        var section = sections[i];
-        var notes = section.querySelector( 'aside.notes' );
+	var DEFAULT_SLIDE_SEPARATOR = '^\n---\n$',
+		DEFAULT_NOTES_SEPARATOR = 'note:',
+		DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
+		DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';
 
-        var template = section.querySelector( 'script' );
 
-        // strip leading whitespace so it isn't evaluated as code
-        var text = ( template || section ).innerHTML;
+	/**
+	 * Retrieves the markdown contents of a slide section
+	 * element. Normalizes leading tabs/whitespace.
+	 */
+	function getMarkdownFromSlide( section ) {
 
-        var leadingWs = text.match(/^\n?(\s*)/)[1].length,
-            leadingTabs = text.match(/^\n?(\t*)/)[1].length;
+		var template = section.querySelector( 'script' );
 
-        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' );
-        }
+		// strip leading whitespace so it isn't evaluated as code
+		var text = ( template || section ).textContent;
 
-        section.innerHTML = (new Showdown.converter()).makeHtml(text);
+		var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
+			leadingTabs = text.match( /^\n?(\t*)/ )[1].length;
 
-        if( notes ) {
-            section.appendChild( notes );
-        }
-    }
+		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;
+
+	}
+
+	/**
+	 * Given a markdown slide section element, this will
+	 * return all arguments that aren't related to markdown
+	 * parsing. Used to forward any other user-defined arguments
+	 * to the output markdown slide.
+	 */
+	function getForwardedAttributes( section ) {
+
+		var attributes = section.attributes;
+		var result = [];
+
+		for( var i = 0, len = attributes.length; i < len; i++ ) {
+			var name = attributes[i].name,
+				value = attributes[i].value;
+
+			// disregard attributes that are used for markdown loading/parsing
+			if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;
+
+			if( value ) {
+				result.push( name + '=' + value );
+			}
+			else {
+				result.push( name );
+			}
+		}
+
+		return result.join( ' ' );
+
+	}
+
+	/**
+	 * Inspects the given options and fills out default
+	 * values for what's not defined.
+	 */
+	function getSlidifyOptions( options ) {
+
+		options = options || {};
+		options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
+		options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
+		options.attributes = options.attributes || '';
+
+		return options;
+
+	}
+
+	/**
+	 * Helper function for constructing a markdown slide.
+	 */
+	function createMarkdownSlide( content, options ) {
+
+		options = getSlidifyOptions( options );
+
+		var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );
+
+		if( notesMatch.length === 2 ) {
+			content = notesMatch[0] + '<aside class="notes" data-markdown>' + notesMatch[1].trim() + '</aside>';
+		}
+
+		return '<script type="text/template">' + content + '</script>';
+
+	}
+
+	/**
+	 * Parses a data string into multiple slides based
+	 * on the passed in separator arguments.
+	 */
+	function slidify( markdown, options ) {
+
+		options = getSlidifyOptions( options );
+
+		var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
+			horizontalSeparatorRegex = new RegExp( options.separator );
+
+		var matches,
+			lastIndex = 0,
+			isHorizontal,
+			wasHorizontal = true,
+			content,
+			sectionStack = [];
+
+		// iterate until all blocks between separators are stacked up
+		while( matches = separatorRegex.exec( markdown ) ) {
+			notes = null;
+
+			// determine direction (horizontal by default)
+			isHorizontal = horizontalSeparatorRegex.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 = separatorRegex.lastIndex;
+			wasHorizontal = isHorizontal;
+		}
+
+		// add the remaining slide
+		( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );
+
+		var markdownSections = '';
+
+		// flatten the hierarchical stack, and insert <section data-markdown> tags
+		for( var i = 0, len = sectionStack.length; i < len; i++ ) {
+			// vertical
+			if( sectionStack[i] instanceof Array ) {
+				markdownSections += '<section '+ options.attributes +'>';
+
+				sectionStack[i].forEach( function( child ) {
+					markdownSections += '<section data-markdown>' +  createMarkdownSlide( child, options ) + '</section>';
+				} );
+
+				markdownSections += '</section>';
+			}
+			else {
+				markdownSections += '<section '+ options.attributes +' data-markdown>' + createMarkdownSlide( sectionStack[i], options ) + '</section>';
+			}
+		}
+
+		return markdownSections;
+
+	}
+
+	/**
+	 * Parses any current data-markdown slides, splits
+	 * multi-slide markdown into separate sections and
+	 * handles loading of external markdown.
+	 */
+	function processSlides() {
+
+		var sections = document.querySelectorAll( '[data-markdown]'),
+			section;
+
+		for( var i = 0, len = sections.length; i < len; i++ ) {
+
+			section = sections[i];
+
+			if( section.getAttribute( 'data-markdown' ).length ) {
+
+				var xhr = new XMLHttpRequest(),
+					url = section.getAttribute( 'data-markdown' );
+
+				datacharset = section.getAttribute( 'data-charset' );
+
+				// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
+				if( datacharset != null && datacharset != '' ) {
+					xhr.overrideMimeType( 'text/html; charset=' + datacharset );
+				}
+
+				xhr.onreadystatechange = function() {
+					if( xhr.readyState === 4 ) {
+						if ( xhr.status >= 200 && xhr.status < 300 ) {
+
+							section.outerHTML = slidify( xhr.responseText, {
+								separator: section.getAttribute( 'data-separator' ),
+								verticalSeparator: section.getAttribute( 'data-vertical' ),
+								notesSeparator: section.getAttribute( 'data-notes' ),
+								attributes: getForwardedAttributes( section )
+							});
+
+						}
+						else {
+
+							section.outerHTML = '<section data-state="alert">' +
+								'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
+								'Check your browser\'s JavaScript console for more details.' +
+								'<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
+								'</section>';
+
+						}
+					}
+				};
+
+				xhr.open( 'GET', url, false );
+
+				try {
+					xhr.send();
+				}
+				catch ( e ) {
+					alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
+				}
+
+			}
+			else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-vertical' ) || section.getAttribute( 'data-notes' ) ) {
+
+				section.outerHTML = slidify( getMarkdownFromSlide( section ), {
+					separator: section.getAttribute( 'data-separator' ),
+					verticalSeparator: section.getAttribute( 'data-vertical' ),
+					notesSeparator: section.getAttribute( 'data-notes' ),
+					attributes: getForwardedAttributes( section )
+				});
+
+			}
+			else {
+				section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
+			}
+		}
+
+	}
+
+	/**
+	 * Check if a node value has the attributes pattern.
+	 * If yes, extract it and add that value as one or several attributes
+	 * the the terget element.
+	 *
+	 * You need Cache Killer on Chrome to see the effect on any FOM transformation
+	 * directly on refresh (F5)
+	 * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
+	 */
+	function addAttributeInElement( node, elementTarget, separator ) {
+
+		var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
+		var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' );
+		var nodeValue = node.nodeValue;
+		if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {
+
+			var classes = matches[1];
+			nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
+			node.nodeValue = nodeValue;
+			while( matchesClass = mardownClassRegex.exec( classes ) ) {
+				elementTarget.setAttribute( matchesClass[1], matchesClass[2] );
+			}
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Add attributes to the parent element of a text node,
+	 * or the element of an attribute node.
+	 */
+	function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {
+
+		if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
+			previousParentElement = element;
+			for( var i = 0; i < element.childNodes.length; i++ ) {
+				childElement = element.childNodes[i];
+				if ( i > 0 ) {
+					j = i - 1;
+					while ( j >= 0 ) {
+						aPreviousChildElement = element.childNodes[j];
+						if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
+							previousParentElement = aPreviousChildElement;
+							break;
+						}
+						j = j - 1;
+					}
+				}
+				parentSection = section;
+				if( childElement.nodeName ==  "section" ) {
+					parentSection = childElement ;
+					previousParentElement = childElement ;
+				}
+				if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
+					addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
+				}
+			}
+		}
+
+		if ( element.nodeType == Node.COMMENT_NODE ) {
+			if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
+				addAttributeInElement( element, section, separatorSectionAttributes );
+			}
+		}
+	}
+
+	/**
+	 * Converts any current data-markdown slides in the
+	 * DOM to HTML.
+	 */
+	function convertSlides() {
+
+		var sections = document.querySelectorAll( '[data-markdown]');
+
+		for( var i = 0, len = sections.length; i < len; i++ ) {
+
+			var section = sections[i];
+
+			// Only parse the same slide once
+			if( !section.getAttribute( 'data-markdown-parsed' ) ) {
+
+				section.setAttribute( 'data-markdown-parsed', true )
+
+				var notes = section.querySelector( 'aside.notes' );
+				var markdown = getMarkdownFromSlide( section );
+
+				section.innerHTML = marked( markdown );
+				addAttributes( 	section, section, null, section.getAttribute( 'data-element-attributes' ) ||
+								section.parentNode.getAttribute( 'data-element-attributes' ) ||
+								DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
+								section.getAttribute( 'data-attributes' ) ||
+								section.parentNode.getAttribute( 'data-attributes' ) ||
+								DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);
+
+				// If there were notes, we need to re-add them after
+				// having overwritten the section's HTML
+				if( notes ) {
+					section.appendChild( notes );
+				}
+
+			}
+
+		}
+
+	}
+
+	// API
+	return {
+
+		initialize: function() {
+			processSlides();
+			convertSlides();
+		},
+
+		// TODO: Do these belong in the API?
+		processSlides: processSlides,
+		convertSlides: convertSlides,
+		slidify: slidify
+
+	};
+
+}));

+ 37 - 0
public/js/plugin/markdown/marked.js

@@ -0,0 +1,37 @@
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/chjj/marked
+ */
+
+(function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){3,} *\n*/,blockquote:/^( *>[^\n]+(\n[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
+text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr",/\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b";block.html=replace(block.html)("comment",/\x3c!--[\s\S]*?--\x3e/)("closed",
+/<(tag)[\s\S]+?<\/\1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,paragraph:/^/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1",
+"\\2")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm)if(this.options.tables)this.rules=block.tables;else this.rules=block.gfm}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};
+Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g,"    ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1)this.tokens.push({type:"space"})}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,
+"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,
+"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i<item.align.length;i++)if(/^ *-+: *$/.test(item.align[i]))item.align[i]="right";else if(/^ *:-+: *$/.test(item.align[i]))item.align[i]="center";else if(/^ *:-+ *$/.test(item.align[i]))item.align[i]="left";else item.align[i]=null;for(i=0;i<item.cells.length;i++)item.cells[i]=item.cells[i].split(/ *\| */);this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=
+src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);
+bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|\d+\.) +/,"");if(~item.indexOf("\n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+
+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item[item.length-1]==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:cap[1]==="pre"||cap[1]==="script",text:cap[0]});continue}if(top&&
+(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i<item.align.length;i++)if(/^ *-+: *$/.test(item.align[i]))item.align[i]="right";else if(/^ *:-+: *$/.test(item.align[i]))item.align[i]=
+"center";else if(/^ *:-+ *$/.test(item.align[i]))item.align[i]="left";else item.align[i]=null;for(i=0;i<item.cells.length;i++)item.cells[i]=item.cells[i].replace(/^ *\| *| *\| *$/g,"").split(/ *\| */);this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1][cap[1].length-1]==="\n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",
+text:cap[0]});continue}if(src)throw new Error("Infinite loop on byte: "+src.charCodeAt(0));}return this.tokens};var inline={escape:/^\\([\\`*{}\[\]()#+\-.!_>])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^\x3c!--[\s\S]*?--\x3e|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/};inline._inside=/(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;inline._href=/\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;
+if(!this.links)throw new Error("Tokens array requires a `links` property.");if(this.options.gfm)if(this.options.breaks)this.rules=inline.breaks;else this.rules=inline.gfm;else if(this.options.pedantic)this.rules=inline.pedantic}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);
+out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1][6]===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+='<a href="'+href+'">'+text+"</a>";continue}if(cap=this.rules.url.exec(src)){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+='<a href="'+href+'">'+text+"</a>";continue}if(cap=this.rules.tag.exec(src)){src=src.substring(cap[0].length);
+out+=this.options.sanitize?escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);out+=this.outputLink(cap,{href:cap[2],title:cap[3]});continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0][0];src=cap[0].substring(1)+src;continue}out+=this.outputLink(cap,link);continue}if(cap=this.rules.strong.exec(src)){src=
+src.substring(cap[0].length);out+="<strong>"+this.output(cap[2]||cap[1])+"</strong>";continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+="<em>"+this.output(cap[2]||cap[1])+"</em>";continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+="<code>"+escape(cap[2],true)+"</code>";continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+="<br>";continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+="<del>"+
+this.output(cap[1])+"</del>";continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=escape(cap[0]);continue}if(src)throw new Error("Infinite loop on byte: "+src.charCodeAt(0));}return out};InlineLexer.prototype.outputLink=function(cap,link){if(cap[0][0]!=="!")return'<a href="'+escape(link.href)+'"'+(link.title?' title="'+escape(link.title)+'"':"")+">"+this.output(cap[1])+"</a>";else return'<img src="'+escape(link.href)+'" alt="'+escape(cap[1])+'"'+(link.title?' title="'+
+escape(link.title)+'"':"")+">"};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/--/g,"\u2014").replace(/'([^']*)'/g,"\u2018$1\u2019").replace(/"([^"]*)"/g,"\u201c$1\u201d").replace(/\.{3}/g,"\u2026")};InlineLexer.prototype.mangle=function(text){var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>0.5)ch="x"+ch.toString(16);out+="&#"+ch+";"}return out};function Parser(options){this.tokens=[];this.token=null;
+this.options=options||marked.defaults}Parser.parse=function(src,options){var parser=new Parser(options);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options);this.tokens=src.reverse();var out="";while(this.next())out+=this.tok();return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;
+while(this.peek().type==="text")body+="\n"+this.next().text;return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case "space":return"";case "hr":return"<hr>\n";case "heading":return"<h"+this.token.depth+">"+this.inline.output(this.token.text)+"</h"+this.token.depth+">\n";case "code":if(this.options.highlight){var code=this.options.highlight(this.token.text,this.token.lang);if(code!=null&&code!==this.token.text){this.token.escaped=true;this.token.text=code}}if(!this.token.escaped)this.token.text=
+escape(this.token.text,true);return"<pre><code"+(this.token.lang?' class="'+this.options.langPrefix+this.token.lang+'"':"")+">"+this.token.text+"</code></pre>\n";case "table":var body="",heading,i,row,cell,j;body+="<thead>\n<tr>\n";for(i=0;i<this.token.header.length;i++){heading=this.inline.output(this.token.header[i]);body+=this.token.align[i]?'<th align="'+this.token.align[i]+'">'+heading+"</th>\n":"<th>"+heading+"</th>\n"}body+="</tr>\n</thead>\n";body+="<tbody>\n";for(i=0;i<this.token.cells.length;i++){row=
+this.token.cells[i];body+="<tr>\n";for(j=0;j<row.length;j++){cell=this.inline.output(row[j]);body+=this.token.align[j]?'<td align="'+this.token.align[j]+'">'+cell+"</td>\n":"<td>"+cell+"</td>\n"}body+="</tr>\n"}body+="</tbody>\n";return"<table>\n"+body+"</table>\n";case "blockquote_start":var body="";while(this.next().type!=="blockquote_end")body+=this.tok();return"<blockquote>\n"+body+"</blockquote>\n";case "list_start":var type=this.token.ordered?"ol":"ul",body="";while(this.next().type!=="list_end")body+=
+this.tok();return"<"+type+">\n"+body+"</"+type+">\n";case "list_item_start":var body="";while(this.next().type!=="list_item_end")body+=this.token.type==="text"?this.parseText():this.tok();return"<li>"+body+"</li>\n";case "loose_item_start":var body="";while(this.next().type!=="list_item_end")body+=this.tok();return"<li>"+body+"</li>\n";case "html":return!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;case "paragraph":return"<p>"+this.inline.output(this.token.text)+
+"</p>\n";case "text":return"<p>"+this.parseText()+"</p>\n"}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=
+1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target)if(Object.prototype.hasOwnProperty.call(target,key))obj[key]=target[key]}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}if(opt)opt=merge({},marked.defaults,opt);var tokens=Lexer.lex(tokens,opt),highlight=opt.highlight,pending=0,l=tokens.length,i=0;if(!highlight||highlight.length<3)return callback(null,Parser.parse(tokens,opt));var done=function(){delete opt.highlight;
+var out=Parser.parse(tokens,opt);opt.highlight=highlight;return callback(null,out)};for(;i<l;i++)(function(token){if(token.type!=="code")return;pending++;return highlight(token.text,token.lang,function(err,code){if(code==null||code===token.text)return--pending||done();token.text=code;token.escaped=true;--pending||done()})})(tokens[i]);return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="\nPlease report this to https://github.com/chjj/marked.";
+if((opt||marked.defaults).silent)return"<p>An error occured:</p><pre>"+escape(e.message+"",true)+"</pre>";throw e;}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:""};marked.Parser=Parser;marked.parser=Parser.parse;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;
+marked.parse=marked;if(typeof exports==="object")module.exports=marked;else if(typeof define==="function"&&define.amd)define(function(){return marked});else this.marked=marked}).call(function(){return this||(typeof window!=="undefined"?window:global)}());

File diff suppressed because it is too large
+ 0 - 61
public/js/plugin/markdown/showdown.js


+ 64 - 0
public/js/plugin/math/math.js

@@ -0,0 +1,64 @@
+/**
+ * A plugin which enables rendering of math equations inside
+ * of reveal.js slides. Essentially a thin wrapper for MathJax.
+ *
+ * @author Hakim El Hattab
+ */
+var RevealMath = window.RevealMath || (function(){
+
+	var options = Reveal.getConfig().math || {};
+	options.mathjax = options.mathjax || 'http://cdn.mathjax.org/mathjax/latest/MathJax.js';
+	options.config = options.config || 'TeX-AMS_HTML-full';
+
+	loadScript( options.mathjax + '?config=' + options.config, function() {
+
+		MathJax.Hub.Config({
+			messageStyle: 'none',
+			tex2jax: { inlineMath: [['$','$'],['\\(','\\)']] },
+			skipStartupTypeset: true
+		});
+
+		// Typeset followed by an immediate reveal.js layout since
+		// the typesetting process could affect slide height
+		MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] );
+		MathJax.Hub.Queue( Reveal.layout );
+
+		// Reprocess equations in slides when they turn visible
+		Reveal.addEventListener( 'slidechanged', function( event ) {
+
+			MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] );
+
+		} );
+
+	} );
+
+	function loadScript( url, callback ) {
+
+		var head = document.querySelector( 'head' );
+		var script = document.createElement( 'script' );
+		script.type = 'text/javascript';
+		script.src = url;
+
+		// Wrapper for callback to make sure it only fires once
+		var finish = function() {
+			if( typeof callback === 'function' ) {
+				callback.call();
+				callback = null;
+			}
+		}
+
+		script.onload = finish;
+
+		// IE
+		script.onreadystatechange = function() {
+			if ( this.readyState === 'loaded' ) {
+				finish();
+			}
+		}
+
+		// Normal browsers
+		head.appendChild( script );
+
+	}
+
+})();

+ 13 - 0
public/js/plugin/multiplex/client.js

@@ -0,0 +1,13 @@
+(function() {
+	var multiplex = Reveal.getConfig().multiplex;
+	var socketId = multiplex.id;
+	var socket = io.connect(multiplex.url);
+
+	socket.on(multiplex.id, function(data) {
+		// ignore data from sockets that aren't ours
+		if (data.socketId !== socketId) { return; }
+		if( window.location.host === 'localhost:1947' ) return;
+
+		Reveal.slide(data.indexh, data.indexv, data.indexf, 'remote');
+	});
+}());

+ 56 - 0
public/js/plugin/multiplex/index.js

@@ -0,0 +1,56 @@
+var express		= require('express');
+var fs			= require('fs');
+var io			= require('socket.io');
+var crypto		= require('crypto');
+
+var app			= express.createServer();
+var staticDir	= express.static;
+
+io				= io.listen(app);
+
+var opts = {
+	port: 1948,
+	baseDir : __dirname + '/../../'
+};
+
+io.sockets.on('connection', function(socket) {
+	socket.on('slidechanged', function(slideData) {
+		if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return;
+		if (createHash(slideData.secret) === slideData.socketId) {
+			slideData.secret = null;
+			socket.broadcast.emit(slideData.socketId, slideData);
+		};
+	});
+});
+
+app.configure(function() {
+	[ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
+		app.use('/' + dir, staticDir(opts.baseDir + dir));
+	});
+});
+
+app.get("/", function(req, res) {
+	res.writeHead(200, {'Content-Type': 'text/html'});
+	fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
+});
+
+app.get("/token", function(req,res) {
+	var ts = new Date().getTime();
+	var rand = Math.floor(Math.random()*9999999);
+	var secret = ts.toString() + rand.toString();
+	res.send({secret: secret, socketId: createHash(secret)});
+});
+
+var createHash = function(secret) {
+	var cipher = crypto.createCipher('blowfish', secret);
+	return(cipher.final('hex'));
+};
+
+// Actually listen
+app.listen(opts.port || null);
+
+var brown = '\033[33m',
+	green = '\033[32m',
+	reset = '\033[0m';
+
+console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset );

+ 51 - 0
public/js/plugin/multiplex/master.js

@@ -0,0 +1,51 @@
+(function() {
+	// Don't emit events from inside of notes windows
+	if ( window.location.search.match( /receiver/gi ) ) { return; }
+
+	var multiplex = Reveal.getConfig().multiplex;
+
+	var socket = io.connect(multiplex.url);
+
+	var notify = function( slideElement, indexh, indexv, origin ) {
+		if( typeof origin === 'undefined' && origin !== 'remote' ) {
+			var nextindexh;
+			var nextindexv;
+
+			var fragmentindex = Reveal.getIndices().f;
+			if (typeof fragmentindex == 'undefined') {
+				fragmentindex = 0;
+			}
+
+			if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') {
+				nextindexh = indexh;
+				nextindexv = indexv + 1;
+			} else {
+				nextindexh = indexh + 1;
+				nextindexv = 0;
+			}
+
+			var slideData = {
+				indexh : indexh,
+				indexv : indexv,
+				indexf : fragmentindex,
+				nextindexh : nextindexh,
+				nextindexv : nextindexv,
+				secret: multiplex.secret,
+				socketId : multiplex.id
+			};
+
+			socket.emit('slidechanged', slideData);
+		}
+	}
+
+	Reveal.addEventListener( 'slidechanged', function( event ) {
+		notify( event.currentSlide, event.indexh, event.indexv, event.origin );
+	} );
+
+	var fragmentNotify = function( event ) {
+		notify( Reveal.getCurrentSlide(), Reveal.getIndices().h, Reveal.getIndices().v, event.origin );
+	};
+
+	Reveal.addEventListener( 'fragmentshown', fragmentNotify );
+	Reveal.addEventListener( 'fragmenthidden', fragmentNotify );
+}());

+ 1 - 0
public/js/plugin/notes-server/index.js

@@ -30,6 +30,7 @@ app.configure(function() {
 });
 
 app.get("/", function(req, res) {
+	res.writeHead(200, {'Content-Type': 'text/html'});
 	fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
 });
 

+ 8 - 5
public/js/plugin/notes-server/notes.html

@@ -3,6 +3,8 @@
 	<head>
 		<meta charset="utf-8">
 
+		<meta name="viewport" content="width=1150">
+
 		<title>reveal.js - Slide Notes</title>
 
 		<style>
@@ -14,6 +16,7 @@
 				font-size: 24px;
 				width: 640px;
 				margin-top: 5px;
+				clear: left;
 			}
 
 			#wrap-current-slide {
@@ -27,13 +30,13 @@
 				width: 1280px;
 				height: 1024px;
 				border: none;
-				
+
 				-webkit-transform-origin: 0 0;
 				   -moz-transform-origin: 0 0;
 				    -ms-transform-origin: 0 0;
 				     -o-transform-origin: 0 0;
 				        transform-origin: 0 0;
-				 
+
 				-webkit-transform: scale(0.5);
 				   -moz-transform: scale(0.5);
 				    -ms-transform: scale(0.5);
@@ -53,7 +56,7 @@
 				width: 1280px;
 				height: 1024px;
 				border: none;
-				
+
 				-webkit-transform-origin: 0 0;
 				   -moz-transform-origin: 0 0;
 				    -ms-transform-origin: 0 0;
@@ -99,7 +102,7 @@
 		<div id="notes"></div>
 
 		<script src="/socket.io/socket.io.js"></script>
-		<script src="/plugin/markdown/showdown.js"></script>
+		<script src="/plugin/markdown/marked.js"></script>
 
 		<script>
 		var socketId = '{{socketId}}';
@@ -113,7 +116,7 @@
 			if (data.socketId !== socketId) { return; }
 
 			if (data.markdown) {
-				notes.innerHTML = (new Showdown.converter()).makeHtml(data.notes);
+				notes.innerHTML = marked(data.notes);
 			}
 			else {
 				notes.innerHTML = data.notes;

+ 159 - 35
public/js/plugin/notes/notes.html

@@ -14,6 +14,7 @@
 				font-size: 24px;
 				width: 640px;
 				margin-top: 5px;
+				clear: left;
 			}
 
 			#wrap-current-slide {
@@ -29,16 +30,16 @@
 				border: none;
 
 				-webkit-transform-origin: 0 0;
-					 -moz-transform-origin: 0 0;
-						-ms-transform-origin: 0 0;
-						 -o-transform-origin: 0 0;
-								transform-origin: 0 0;
+				   -moz-transform-origin: 0 0;
+					-ms-transform-origin: 0 0;
+					 -o-transform-origin: 0 0;
+						transform-origin: 0 0;
 
 				-webkit-transform: scale(0.5);
-					 -moz-transform: scale(0.5);
-						-ms-transform: scale(0.5);
-						 -o-transform: scale(0.5);
-								transform: scale(0.5);
+				   -moz-transform: scale(0.5);
+					-ms-transform: scale(0.5);
+					 -o-transform: scale(0.5);
+						transform: scale(0.5);
 			}
 
 			#wrap-next-slide {
@@ -55,16 +56,16 @@
 				border: none;
 
 				-webkit-transform-origin: 0 0;
-					 -moz-transform-origin: 0 0;
-						-ms-transform-origin: 0 0;
-						 -o-transform-origin: 0 0;
-								transform-origin: 0 0;
+				   -moz-transform-origin: 0 0;
+					-ms-transform-origin: 0 0;
+					 -o-transform-origin: 0 0;
+						transform-origin: 0 0;
 
 				-webkit-transform: scale(0.35);
-					 -moz-transform: scale(0.35);
-						-ms-transform: scale(0.35);
-						 -o-transform: scale(0.35);
-								transform: scale(0.35);
+				   -moz-transform: scale(0.35);
+					-ms-transform: scale(0.35);
+					 -o-transform: scale(0.35);
+						transform: scale(0.35);
 			}
 
 			.slides {
@@ -83,61 +84,184 @@
 				font-size: 14px;
 				color: rgba( 255, 255, 255, 0.9 );
 			}
+
+			.error {
+				font-weight: bold;
+				color: red;
+				font-size: 1.5em;
+				text-align: center;
+				margin-top: 10%;
+			}
+
+			.error code {
+				font-family: monospace;
+			}
+
+			.time {
+				width: 448px;
+				margin: 30px 0 0 10px;
+				float: left;
+				text-align: center;
+				opacity: 0;
+
+				-webkit-transition: opacity 0.4s;
+				   -moz-transition: opacity 0.4s;
+				     -o-transition: opacity 0.4s;
+				        transition: opacity 0.4s;
+			}
+
+			.elapsed,
+			.clock {
+				color: #333;
+				font-size: 2em;
+				text-align: center;
+				display: inline-block;
+				padding: 0.5em;
+				background-color: #eee;
+				border-radius: 10px;
+			}
+
+			.elapsed h2,
+			.clock h2 {
+				font-size: 0.8em;
+				line-height: 100%;
+				margin: 0;
+				color: #aaa;
+			}
+
+			.elapsed .mute {
+				color: #ddd;
+			}
+
 		</style>
 	</head>
 
 	<body>
 
+		<script>
+			function getNotesURL( controls ) {
+				return window.opener.location.protocol + '//' + window.opener.location.host + window.opener.location.pathname + '?receiver&controls='+ ( controls || 'false' ) +'&progress=false&overview=false' + window.opener.location.hash;
+			}
+			var notesCurrentSlideURL = getNotesURL( true );
+			var notesNextSlideURL = getNotesURL( false );
+		</script>
+
 		<div id="wrap-current-slide" class="slides">
-			<iframe src="../../index.html" width="1280" height="1024" id="current-slide"></iframe>
+			<script>document.write( '<iframe width="1280" height="1024" id="current-slide" src="'+ notesCurrentSlideURL +'"></iframe>' );</script>
 		</div>
 
 		<div id="wrap-next-slide" class="slides">
-			<iframe src="../../index.html" width="640" height="512" id="next-slide"></iframe>
+			<script>document.write( '<iframe width="640" height="512" id="next-slide" src="'+ notesNextSlideURL +'"></iframe>' );</script>
 			<span>UPCOMING:</span>
 		</div>
+
+		<div class="time">
+			<div class="clock">
+				<h2>Time</h2>
+				<span id="clock">0:00:00 AM</span>
+			</div>
+			<div class="elapsed">
+				<h2>Elapsed</h2>
+				<span id="hours">00</span><span id="minutes">:00</span><span id="seconds">:00</span>
+			</div>
+		</div>
+
 		<div id="notes"></div>
 
-		<script src="../../plugin/markdown/showdown.js"></script>
+		<script src="../../plugin/markdown/marked.js"></script>
 		<script>
+
 			window.addEventListener( 'load', function() {
 
-				(function( window, undefined ) {
+				if( window.opener && window.opener.location && window.opener.location.href ) {
+
 					var notes = document.getElementById( 'notes' ),
 						currentSlide = document.getElementById( 'current-slide' ),
-						nextSlide = document.getElementById( 'next-slide' );
+						nextSlide = document.getElementById( 'next-slide' ),
+						silenced = false;
 
 					window.addEventListener( 'message', function( event ) {
 						var data = JSON.parse( event.data );
+
 						// No need for updating the notes in case of fragment changes
 						if ( data.notes !== undefined) {
 							if( data.markdown ) {
-								notes.innerHTML = (new Showdown.converter()).makeHtml( data.notes );
+								notes.innerHTML = marked( data.notes );
 							}
 							else {
 								notes.innerHTML = data.notes;
 							}
 						}
 
-						// Showing and hiding fragments
-						if( data.fragment === 'next' ) {
-							currentSlide.contentWindow.Reveal.nextFragment();
-						}
-						else if( data.fragment === 'prev' ) {
-							currentSlide.contentWindow.Reveal.prevFragment();
-						}
-						else {
-							// Update the note slides
-							currentSlide.contentWindow.Reveal.slide( data.indexh, data.indexv );
-							nextSlide.contentWindow.Reveal.slide( data.nextindexh, data.nextindexv );
-						}
+						silenced = true;
+
+						// Update the note slides
+						currentSlide.contentWindow.Reveal.slide( data.indexh, data.indexv, data.indexf );
+						nextSlide.contentWindow.Reveal.slide( data.nextindexh, data.nextindexv );
+
+						silenced = false;
 
 					}, false );
 
-				})( window );
+					var start = new Date(),
+						timeEl = document.querySelector( '.time' ),
+						clockEl = document.getElementById( 'clock' ),
+						hoursEl = document.getElementById( 'hours' ),
+						minutesEl = document.getElementById( 'minutes' ),
+						secondsEl = document.getElementById( 'seconds' );
+
+					setInterval( function() {
+
+						timeEl.style.opacity = 1;
+
+						var diff, hours, minutes, seconds,
+							now = new Date();
+
+						diff = now.getTime() - start.getTime();
+						hours = Math.floor( diff / ( 1000 * 60 * 60 ) );
+						minutes = Math.floor( ( diff / ( 1000 * 60 ) ) % 60 );
+						seconds = Math.floor( ( diff / 1000 ) % 60 );
+
+						clockEl.innerHTML = now.toLocaleTimeString();
+						hoursEl.innerHTML = zeroPadInteger( hours );
+						hoursEl.className = hours > 0 ? "" : "mute";
+						minutesEl.innerHTML = ":" + zeroPadInteger( minutes );
+						minutesEl.className = minutes > 0 ? "" : "mute";
+						secondsEl.innerHTML = ":" + zeroPadInteger( seconds );
+
+					}, 1000 );
+
+					// Broadcasts the state of the notes window to synchronize
+					// the main window
+					function synchronizeMainWindow() {
+
+						if( !silenced ) {
+							var indices = currentSlide.contentWindow.Reveal.getIndices();
+							window.opener.Reveal.slide( indices.h, indices.v, indices.f );
+						}
+
+					}
+
+					// Navigate the main window when the notes slide changes
+					currentSlide.contentWindow.Reveal.addEventListener( 'slidechanged', synchronizeMainWindow );
+					currentSlide.contentWindow.Reveal.addEventListener( 'fragmentshown', synchronizeMainWindow );
+					currentSlide.contentWindow.Reveal.addEventListener( 'fragmenthidden', synchronizeMainWindow );
+
+				}
+				else {
+
+					document.body.innerHTML =  '<p class="error">Unable to access <code>window.opener.location</code>.<br>Make sure the presentation is running on a web server.</p>';
+
+				}
+
 
 			}, false );
 
+			function zeroPadInteger( num ) {
+				var str = "00" + parseInt( num );
+				return str.substring( str.length - 2 );
+			}
+
 		</script>
 	</body>
 </html>

+ 28 - 48
public/js/plugin/notes/notes.js

@@ -5,75 +5,55 @@
 var RevealNotes = (function() {
 
 	function openNotes() {
-		var notesPopup = window.open( 'plugin/notes/notes.html', 'reveal.js - Notes', 'width=1120,height=850' );
+		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');
-		} );
+		Reveal.addEventListener( 'slidechanged', post );
 
 		// Fires when a fragment is shown
-		Reveal.addEventListener( 'fragmentshown', function( event ) {
-			post('fragmentshown');
-		} );
+		Reveal.addEventListener( 'fragmentshown', post );
 
 		// Fires when a fragment is hidden
-		Reveal.addEventListener( 'fragmenthidden', function( event ) {
-			post('fragmenthidden');
-		} );
+		Reveal.addEventListener( 'fragmenthidden', post );
 
 		/**
 		 * 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 ) {
+		function post() {
 			var slideElement = Reveal.getCurrentSlide(),
+				slideIndices = Reveal.getIndices(),
 				messageData;
 
-			if( eventType === 'slidechanged' ) {
-				var notes = slideElement.querySelector( 'aside.notes' ),
-					indexh = Reveal.getIndices().h,
-					indexv = Reveal.getIndices().v,
-					nextindexh,
-					nextindexv;
+			var notes = slideElement.querySelector( 'aside.notes' ),
+				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'
-				};
+			if( slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION' ) {
+				nextindexh = slideIndices.h;
+				nextindexv = slideIndices.v + 1;
+			} else {
+				nextindexh = slideIndices.h + 1;
+				nextindexv = 0;
 			}
 
+			messageData = {
+				notes : notes ? notes.innerHTML : '',
+				indexh : slideIndices.h,
+				indexv : slideIndices.v,
+				indexf : slideIndices.f,
+				nextindexh : nextindexh,
+				nextindexv : nextindexv,
+				markdown : notes ? typeof notes.getAttribute( 'data-markdown' ) === 'string' : false
+			};
+
 			notesPopup.postMessage( JSON.stringify( messageData ), '*' );
 		}
 
 		// Navigate to the current slide when the notes are loaded
 		notesPopup.addEventListener( 'load', function( event ) {
-			post('slidechanged');
+			post();
 		}, false );
 	}
 

+ 44 - 0
public/js/plugin/print-pdf/print-pdf.js

@@ -0,0 +1,44 @@
+/**
+ * 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.viewportSize  = {
+	width: 1024,
+	height: 768
+};
+
+page.paperSize = {
+	format: 'letter',
+	orientation: 'landscape',
+	margin: {
+		left: '0',
+		right: '0',
+		top: '0',
+		bottom: '0'
+	}
+};
+
+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();
+} );
+

+ 32 - 12
public/js/plugin/remotes/remotes.js

@@ -3,17 +3,37 @@
  * of the folks at http://remotes.io
  */
 
-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(); 
-		});
+(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;
+    })();
 
-head.js( 'https://raw.github.com/Remotes/Remotes/master/dist/remotes.ne.min.js' );
+    /**
+     * Detects if notes are enable and the current page is opened inside an /iframe
+     * this prevents loading Remotes.io several times
+     */
+    var isNotesAndIframe = (function(){
+        return window.RevealNotes && !(self == top);
+    })();
+
+    if(!hasTouch && !isNotesAndIframe){
+        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.next(); })
+                .on("zoom-out", function(e){ Reveal.toggleOverview(true); })
+                .on("zoom-in", function(e){ Reveal.toggleOverview(false); })
+            ;
+        } );
+
+        head.js('https://hakim-static.s3.amazonaws.com/reveal-js/remotes.ne.min.js');
+    }
+})(window);

+ 196 - 0
public/js/plugin/search/search.js

@@ -0,0 +1,196 @@
+/*
+ * Handles finding a text string anywhere in the slides and showing the next occurrence to the user
+ * by navigatating to that slide and highlighting it.
+ *
+ * By Jon Snyder <snyder.jon@gmail.com>, February 2013
+ */
+
+var RevealSearch = (function() {
+
+	var matchedSlides;
+	var currentMatchedIndex;
+	var searchboxDirty;
+	var myHilitor;
+
+// Original JavaScript code by Chirp Internet: www.chirp.com.au
+// Please acknowledge use of this code by including this header.
+// 2/2013 jon: modified regex to display any match, not restricted to word boundaries.
+
+function Hilitor(id, tag)
+{
+
+  var targetNode = document.getElementById(id) || document.body;
+  var hiliteTag = tag || "EM";
+  var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM|SPAN)$");
+  var colors = ["#ff6", "#a0ffff", "#9f9", "#f99", "#f6f"];
+  var wordColor = [];
+  var colorIdx = 0;
+  var matchRegex = "";
+  var matchingSlides = [];
+
+  this.setRegex = function(input)
+  {
+    input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|");
+    matchRegex = new RegExp("(" + input + ")","i");
+  }
+
+  this.getRegex = function()
+  {
+    return matchRegex.toString().replace(/^\/\\b\(|\)\\b\/i$/g, "").replace(/\|/g, " ");
+  }
+
+  // recursively apply word highlighting
+  this.hiliteWords = function(node)
+  {
+    if(node == undefined || !node) return;
+    if(!matchRegex) return;
+    if(skipTags.test(node.nodeName)) return;
+
+    if(node.hasChildNodes()) {
+      for(var i=0; i < node.childNodes.length; i++)
+        this.hiliteWords(node.childNodes[i]);
+    }
+    if(node.nodeType == 3) { // NODE_TEXT
+      if((nv = node.nodeValue) && (regs = matchRegex.exec(nv))) {
+      	//find the slide's section element and save it in our list of matching slides
+      	var secnode = node.parentNode;
+      	while (secnode.nodeName != 'SECTION') {
+      		secnode = secnode.parentNode;
+      	}
+      	
+      	var slideIndex = Reveal.getIndices(secnode);
+      	var slidelen = matchingSlides.length;
+      	var alreadyAdded = false;
+      	for (var i=0; i < slidelen; i++) {
+      		if ( (matchingSlides[i].h === slideIndex.h) && (matchingSlides[i].v === slideIndex.v) ) {
+      			alreadyAdded = true;
+      		}
+      	}
+      	if (! alreadyAdded) {
+      		matchingSlides.push(slideIndex);
+      	}
+      	
+        if(!wordColor[regs[0].toLowerCase()]) {
+          wordColor[regs[0].toLowerCase()] = colors[colorIdx++ % colors.length];
+        }
+
+        var match = document.createElement(hiliteTag);
+        match.appendChild(document.createTextNode(regs[0]));
+        match.style.backgroundColor = wordColor[regs[0].toLowerCase()];
+        match.style.fontStyle = "inherit";
+        match.style.color = "#000";
+
+        var after = node.splitText(regs.index);
+        after.nodeValue = after.nodeValue.substring(regs[0].length);
+        node.parentNode.insertBefore(match, after);
+      }
+    }
+  };
+
+  // remove highlighting
+  this.remove = function()
+  {
+    var arr = document.getElementsByTagName(hiliteTag);
+    while(arr.length && (el = arr[0])) {
+      el.parentNode.replaceChild(el.firstChild, el);
+    }
+  };
+
+  // start highlighting at target node
+  this.apply = function(input)
+  {
+    if(input == undefined || !input) return;
+    this.remove();
+    this.setRegex(input);
+    this.hiliteWords(targetNode);
+    return matchingSlides;
+  };
+
+}
+
+	function openSearch() {
+		//ensure the search term input dialog is visible and has focus:
+		var inputbox = document.getElementById("searchinput");
+		inputbox.style.display = "inline";
+		inputbox.focus();
+		inputbox.select();
+	}
+
+	function toggleSearch() {
+		var inputbox = document.getElementById("searchinput");
+		if (inputbox.style.display !== "inline") {
+			openSearch();
+		}
+		else {
+			inputbox.style.display = "none";
+			myHilitor.remove();
+		}
+	}
+
+	function doSearch() {
+		//if there's been a change in the search term, perform a new search:
+		if (searchboxDirty) {
+			var searchstring = document.getElementById("searchinput").value;
+
+			//find the keyword amongst the slides
+			myHilitor = new Hilitor("slidecontent");
+			matchedSlides = myHilitor.apply(searchstring);
+			currentMatchedIndex = 0;
+		}
+
+		//navigate to the next slide that has the keyword, wrapping to the first if necessary
+		if (matchedSlides.length && (matchedSlides.length <= currentMatchedIndex)) {
+			currentMatchedIndex = 0;
+		}
+		if (matchedSlides.length > currentMatchedIndex) {
+			Reveal.slide(matchedSlides[currentMatchedIndex].h, matchedSlides[currentMatchedIndex].v);
+			currentMatchedIndex++;
+		}
+	}
+
+	var dom = {};
+	dom.wrapper = document.querySelector( '.reveal' );
+
+	if( !dom.wrapper.querySelector( '.searchbox' ) ) {
+			var searchElement = document.createElement( 'div' );
+			searchElement.id = "searchinputdiv";
+			searchElement.classList.add( 'searchdiv' );
+      searchElement.style.position = 'absolute';
+      searchElement.style.top = '10px';
+      searchElement.style.left = '10px';
+      //embedded base64 search icon Designed by Sketchdock - http://www.sketchdock.com/:
+			searchElement.innerHTML = '<span><input type="search" id="searchinput" class="searchinput" style="vertical-align: top;"/><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJiSURBVHjatFZNaxNBGH5md+Mmu92NVdKDRipSAyqCghgQD4L4cRe86UUtAQ+eFCxoa4/25EXBFi8eBE+eRPoDhB6KgiiixdAPCEkx2pjvTXadd9yNsflwuyUDD/O+u8PzzDPvzOwyx3EwyCZhwG3gAkp7MnpjgbopjsltcD4gjuXZZKeAR348MYLYTm3LzOs/y3j3JTfZxgXWXmTuwPHIc4VmoOmv5IrI53+AO2DdHLjkDWQ3GoEEVFXtXQOvkSnPWcyUceviLhwbDYv8/XIVj97kse7TodLvZXxYxrPUHkQ1ufXs3FEdybEIxucySOesoNvUgWU1cP3MkCBfTFdw9fGaAMVmRELq7LBw2Q3/FaAxxWIRpw+ZIr/7IouPqzUBiqmdHAv7EuhRAwf1er2Vy4x1jW3b2d5Jfvu5IPp7l2LYbcgCFFNb+FoJ7oBqEAqFMPNqFcmEgVMJDfMT+1tvN0pNjERlMS6QA5pFOKxiKVPFhakPeL3It+WGJUDxt2wFR+JhzI7v5ctkd8DXOZAkCYYxhO+lKm4+Xfqz/rIixBuNBl7eOYzkQQNzqX249mRl6zUgEcYkaJrGhUwBinVdh6IouPzwE6/DL5w4oLkH8y981aDf+uq6hlKpJESiUdNfDZi7/ehG9K6KfiA3pml0PLcsq+cSMTj2NL9ukc4UOmz7AZ3+crkC4mHujFvXNaMFB3bEr8xPS6p5O+jXxq4VZtaen7/PwzrntjcLUE0iHPS1Ud1cdiEJl/8WivZk0wXd7zWOMkeF8s0CcAmkNrC2nvXZDbbbN73ccYnZoH9bfgswAFzAe9/h3dbKAAAAAElFTkSuQmCC" id="searchbutton" class="searchicon" style="vertical-align: top; margin-top: -1px;"/></span>';
+			dom.wrapper.appendChild( searchElement );
+	}
+
+	document.getElementById("searchbutton").addEventListener( 'click', function(event) {
+		doSearch();
+	}, false );
+
+	document.getElementById("searchinput").addEventListener( 'keyup', function( event ) {
+		switch (event.keyCode) {
+			case 13:
+				event.preventDefault();
+				doSearch();
+				searchboxDirty = false;
+				break;
+			default:
+				searchboxDirty = true;
+		}
+	}, false );
+
+	// Open the search when the 's' key is hit (yes, this conflicts with the notes plugin, disabling for now)
+	/*
+	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();
+			openSearch();
+		}
+	}, false );
+*/
+	return { open: openSearch };
+})();

+ 28 - 21
public/js/plugin/zoom-js/zoom.js

@@ -1,29 +1,36 @@
 // Custom reveal.js integration
 (function(){
-	document.querySelector( '.reveal' ).addEventListener( 'click', function( event ) {
-		if( event.altKey ) {
+	var isEnabled = true;
+
+	document.querySelector( '.reveal' ).addEventListener( 'mousedown', function( event ) {
+		var modifier = ( Reveal.getConfig().zoomKey ? Reveal.getConfig().zoomKey : 'alt' ) + 'Key';
+
+		if( event[ modifier ] && 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;
@@ -36,7 +43,7 @@ var zoom = (function(){
 								'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';
@@ -45,7 +52,7 @@ var zoom = (function(){
 		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 ) {
@@ -62,21 +69,21 @@ var zoom = (function(){
 	}, false );
 
 	/**
-	 * Applies the CSS required to zoom in, prioritizes use of CSS3 
+	 * 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 
+	 *
+	 * @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;
@@ -121,7 +128,7 @@ var zoom = (function(){
 	}
 
 	/**
-	 * Pan the document when the mosue cursor approaches the edges 
+	 * Pan the document when the mosue cursor approaches the edges
 	 * of the window.
 	 */
 	function pan() {
@@ -129,7 +136,7 @@ var zoom = (function(){
 			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 ) );
@@ -159,7 +166,7 @@ var zoom = (function(){
 	return {
 		/**
 		 * Zooms in on either a rectangle or HTML element.
-		 * 
+		 *
 		 * @param {Object} options
 		 *   - element: HTML element to zoom in on
 		 *   OR
@@ -232,7 +239,7 @@ var zoom = (function(){
 			if( currentOptions && currentOptions.element ) {
 				scrollOffset.x -= ( window.innerWidth - ( currentOptions.width * currentOptions.scale ) ) / 2;
 			}
-			
+
 			magnify( scrollOffset.x, scrollOffset.y, 0, 0, 1 );
 
 			level = 1;
@@ -241,11 +248,11 @@ var zoom = (function(){
 		// Alias
 		magnify: function( options ) { this.to( options ) },
 		reset: function() { this.out() },
-		
+
 		zoomLevel: function() {
 			return level;
 		}
 	}
-	
+
 })();
 

+ 82 - 0
public/node/index.html

@@ -0,0 +1,82 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<meta name="robots" content="noindex,nofollow">
+		<title>Node.js | Presentations | David A. Windham</title>
+		<meta name="description" content="HTML Presentation">
+		<meta name="author" content="David A. Windham">
+		<meta name="apple-mobile-web-app-capable" content="yes" />
+		<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
+		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+		<link rel="stylesheet" href="../css/reveal.min.css">
+		<link rel="stylesheet" href="../css/daw.css" id="theme">
+		<!--[if lt IE 9]>
+		<script src="js/html5shiv.js"></script>
+		<![endif]-->
+	</head>
+
+	<body>
+
+		<div class="reveal">
+
+			<div class="slides">
+				<section>
+					<section data-background="#ffffff">
+					<div id="logo">
+					<h3>David A. Windham</h3>
+					</div>	
+					<p><small>This is just the place I keep presentations.</small></p>					
+					<p class="footnotes"><a href="https://davidawindham.com">David A. Windham</a> / <a href="http://twitter.com/windhamd">@windhamdavid</a></p>
+					<br />
+					<p class="footnotes">keys ( arrows | n:next | p:previous | esc:all )</p>
+					</section>		
+				</section>
+				<section>
+					<h4><span class="raised">Credits</span></h4>
+
+						<p class="footnotes">Made with: HTML, CSS, JavaScript, Ubuntu, Nginx, Node.js,<br /> TextMate, LiceCap, Illustrator/Photoshop, Apple OSX.<br />
+						Hakim El Hattab &amp; John Polocek who wrote 95% of the Javascript.<br />
+						Open Sans &amp; Quicksand by Steve Matteson &amp; Andres Paglinawan.</p>
+						</small>
+						<p class="footnotes"><a rel="license" href="http://creativecommons.org/licenses/by/4.0/deed.en_US"><img alt="Creative Commons License" style="margin:0; border-width:0" src="http://i.creativecommons.org/l/by/4.0/80x15.png" /></a><br />This <span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/InteractiveResource" rel="dct:type">work</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/deed.en_US">Creative Commons Attribution 4.0 International License</a>.<br />
+						by <a xmlns:cc="http://creativecommons.org/ns#" href="https://davidawindham.com/" property="cc:attributionName" rel="cc:attributionURL">David A. Windham</a> / <a href="http://twitter.com/windhamd_gwd">@windhamdavid</a><br /><br /></p>
+				</section>
+			</div>
+		</div>
+		<script src="../js/head.min.js"></script>
+		<script src="../js/reveal.min.js"></script>
+		<script>
+			Reveal.initialize({
+				controls: true,
+				progress: true,
+				history: true,
+				center: true,
+				rollingLinks: true,
+				theme: Reveal.getQueryHash().theme,
+				transition: Reveal.getQueryHash().transition || 'default', //cube/page/concave/zoom/linear/fade/none
+				dependencies: [
+					{ src: '../js/classList.js', condition: function() { return !document.body.classList; } },
+					{ src: '../js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
+					{ src: '../js/plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
+					{ src: '../js/plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
+				]
+			});
+		</script>
+		<script src="/socket.io/socket.io.js"></script>
+		<script src="/controldeck-slides.js"></script>
+		<script>
+		  var _paq = _paq || [];
+		  _paq.push(['trackPageView']);
+		  _paq.push(['enableLinkTracking']);
+		  (function() {
+		    var u="//davidawindham.com/wik/";
+		    _paq.push(['setTrackerUrl', u+'piwik.php']);
+		    _paq.push(['setSiteId', 1]);
+		    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
+		    g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
+		  })();
+		</script>
+		<noscript><p><img src="//davidawindham.com/wik/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
+	</body>
+</html>

Some files were not shown because too many files changed in this diff