diff --git a/.travis.yml b/.travis.yml index baa0031d..2d6cd8f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +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/grunt.js b/Gruntfile.js similarity index 53% rename from grunt.js rename to Gruntfile.js index 00f43502..22502f61 100644 --- a/grunt.js +++ b/Gruntfile.js @@ -3,19 +3,11 @@ module.exports = function(grunt) { // Project configuration grunt.initConfig({ - pkg: '', - - inputJS: 'js/reveal.js', - inputCSS: 'css/reveal.css', - - outputJS: 'js/reveal.min.js', - outputCSS: 'css/reveal.min.css', - + pkg: grunt.file.readJSON('package.json'), meta: { - version: '2.3', - banner: + banner: '/*!\n' + - ' * reveal.js <%= meta.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\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' + @@ -23,8 +15,8 @@ module.exports = function(grunt) { ' */' }, - lint: { - files: [ 'grunt.js', '<%= inputJS %>' ] + jshint: { + files: [ 'Gruntfile.js', 'js/reveal.js' ] }, // Tests will be added soon @@ -32,17 +24,20 @@ module.exports = function(grunt) { files: [ 'test/**/*.html' ] }, - min: { - dist: { - src: [ '', '<%= inputJS %>' ], - dest: '<%= outputJS %>' + uglify: { + options: { + banner: '<%= meta.banner %>\n' + }, + build: { + src: 'js/reveal.js', + dest: 'js/reveal.min.js' } }, - mincss: { + cssmin: { compress: { files: { - '<%= outputCSS %>': [ '<%= inputCSS %>' ] + 'css/reveal.min.css': [ 'css/reveal.css' ] } } }, @@ -69,16 +64,19 @@ module.exports = function(grunt) { }, watch: { - files: [ 'grunt.js', '<%= inputJS %>', '<%= inputCSS %>' ], + files: [ 'Gruntfile.js', 'js/reveal.js', 'css/reveal.css' ], tasks: 'default' } }); // Dependencies - grunt.loadNpmTasks( 'grunt-contrib-mincss' ); + 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', [ 'lint', 'mincss', 'min' ] ); + grunt.registerTask( 'default', [ 'jshint', 'cssmin', 'uglify' ] ); }; diff --git a/README.md b/README.md index adf7a3a8..f4b5e6ec 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Markup heirarchy needs to be ``
``` elements and wrap the contents in a ``` + + + +
+ +
+ + +
+ +
+ +
+
+ + + + + + + + diff --git a/plugin/markdown/example.md b/plugin/markdown/example.md new file mode 100644 index 00000000..e988dd9c --- /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 index b1660a1b..39e11687 100644 --- a/plugin/markdown/markdown.js +++ b/plugin/markdown/markdown.js @@ -6,16 +6,12 @@ throw 'The reveal.js Markdown plugin requires Showdown to be loaded'; } - var sections = document.querySelectorAll( '[data-markdown]' ); - - for( var i = 0, len = sections.length; i < len; i++ ) { - var section = sections[i]; - var notes = section.querySelector( 'aside.notes' ); + var stripLeadingWhitespace = function(section) { var template = section.querySelector( 'script' ); // strip leading whitespace so it isn't evaluated as code - var text = ( template || section ).innerHTML; + var text = ( template || section ).textContent; var leadingWs = text.match(/^\n?(\s*)/)[1].length, leadingTabs = text.match(/^\n?(\t*)/)[1].length; @@ -27,11 +23,125 @@ text = text.replace( new RegExp('\\n? {' + leadingWs + '}','g'), '\n' ); } - section.innerHTML = (new Showdown.converter()).makeHtml(text); + 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 ); } - } -})(); \ No newline at end of file + }; + + querySlidingMarkdown(); + + queryMarkdownSlides(); + +})(); diff --git a/plugin/notes/notes.html b/plugin/notes/notes.html index 50d172a9..e14c6ac7 100644 --- a/plugin/notes/notes.html +++ b/plugin/notes/notes.html @@ -225,6 +225,13 @@ }, 1000 ); + // Navigate the main window when the notes slide changes + currentSlide.contentWindow.Reveal.addEventListener( 'slidechanged', function( event ) { + + window.opener.Reveal.slide( event.indexh, event.indexv ); + + } ); + } else {