async loading of external markdown, add Reveal.registerPlugin()
This commit is contained in:
		
							
								
								
									
										104
									
								
								js/reveal.js
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								js/reveal.js
									
									
									
									
									
								
							| @@ -319,6 +319,12 @@ | ||||
| 		// Cached references to DOM elements | ||||
| 		dom = {}, | ||||
|  | ||||
| 		// A list of registered reveal.js plugins | ||||
| 		plugins = {}, | ||||
|  | ||||
| 		// List of asynchronously loaded reveal.js dependencies | ||||
| 		asyncDependencies = [], | ||||
|  | ||||
| 		// Features supported by the browser, see #checkCapabilities() | ||||
| 		features = {}, | ||||
|  | ||||
| @@ -434,7 +440,7 @@ | ||||
| 		// Hide the address bar in mobile browsers | ||||
| 		hideAddressBar(); | ||||
|  | ||||
| 		// Loads the dependencies and continues to #start() once done | ||||
| 		// Loads dependencies and continues to #start() once done | ||||
| 		load(); | ||||
|  | ||||
| 	} | ||||
| @@ -489,37 +495,22 @@ | ||||
| 	function load() { | ||||
|  | ||||
| 		var scripts = [], | ||||
| 			scriptsAsync = [], | ||||
| 			scriptsToPreload = 0; | ||||
|  | ||||
| 		// Called once synchronous scripts finish loading | ||||
| 		function afterSynchronousScriptsLoaded() { | ||||
| 			// Load asynchronous scripts | ||||
| 			if( scriptsAsync.length ) { | ||||
| 				scriptsAsync.forEach( function( s ) { | ||||
| 					loadScript( s.src, s.callback ); | ||||
| 				} ); | ||||
| 			} | ||||
|  | ||||
| 			start(); | ||||
| 		} | ||||
|  | ||||
| 		for( var i = 0, len = config.dependencies.length; i < len; i++ ) { | ||||
| 			var s = config.dependencies[i]; | ||||
| 			scriptsToLoad = 0; | ||||
|  | ||||
| 		config.dependencies.forEach( function( s ) { | ||||
| 			// Load if there's no condition or the condition is truthy | ||||
| 			if( !s.condition || s.condition() ) { | ||||
| 				if( s.async ) { | ||||
| 					scriptsAsync.push( s ); | ||||
| 					asyncDependencies.push( s ); | ||||
| 				} | ||||
| 				else { | ||||
| 					scripts.push( s ); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		} ); | ||||
|  | ||||
| 		if( scripts.length ) { | ||||
| 			scriptsToPreload = scripts.length; | ||||
| 			scriptsToLoad = scripts.length; | ||||
|  | ||||
| 			// Load synchronous scripts | ||||
| 			scripts.forEach( function( s ) { | ||||
| @@ -527,21 +518,66 @@ | ||||
|  | ||||
| 					if( typeof s.callback === 'function' ) s.callback(); | ||||
|  | ||||
| 					if( --scriptsToPreload === 0 ) { | ||||
|  | ||||
| 						afterSynchronousScriptsLoaded(); | ||||
|  | ||||
| 					if( --scriptsToLoad === 0 ) { | ||||
| 						loadPlugins(); | ||||
| 					} | ||||
|  | ||||
| 				} ); | ||||
| 			} ); | ||||
| 		} | ||||
| 		else { | ||||
| 			afterSynchronousScriptsLoaded(); | ||||
| 			loadPlugins(); | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Loads all plugins that require preloading. | ||||
| 	 */ | ||||
| 	function loadPlugins() { | ||||
|  | ||||
| 		var pluginsToLoad = Object.keys( plugins ).length; | ||||
|  | ||||
| 		for( var i in plugins ) { | ||||
|  | ||||
| 			var plugin = plugins[i]; | ||||
|  | ||||
| 			// If the plugin has an 'init' method, initialize and | ||||
| 			// wait for the callback | ||||
| 			if( typeof plugin.init === 'function' ) { | ||||
| 				plugin.init( function() { | ||||
| 					if( --pluginsToLoad === 0 ) { | ||||
| 						loadAsyncDependencies(); | ||||
| 					} | ||||
| 				} ); | ||||
| 			} | ||||
| 			else { | ||||
| 				pluginsToLoad -= 1; | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if( pluginsToLoad === 0 ) { | ||||
| 			loadAsyncDependencies(); | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Loads all async reveal.js dependencies. | ||||
| 	 */ | ||||
| 	function loadAsyncDependencies() { | ||||
|  | ||||
| 		if( asyncDependencies.length ) { | ||||
| 			asyncDependencies.forEach( function( s ) { | ||||
| 				loadScript( s.src, s.callback ); | ||||
| 			} ); | ||||
| 		} | ||||
|  | ||||
| 		start(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Loads a JavaScript file from the given URL and executes it. | ||||
| 	 * | ||||
| @@ -1512,6 +1548,15 @@ | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Registers a new plugin with this reveal.js instance. | ||||
| 	 */ | ||||
| 	function registerPlugin( id, plugin ) { | ||||
|  | ||||
| 		plugins[id] = plugin; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Add a custom key binding with optional description to | ||||
| 	 * be added to the help screen. | ||||
| @@ -5845,12 +5890,13 @@ | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		// Adds a custom key binding | ||||
| 		// Adds/remvoes a custom key binding | ||||
| 		addKeyBinding: addKeyBinding, | ||||
|  | ||||
| 		// Removes a custom key binding | ||||
| 		removeKeyBinding: removeKeyBinding, | ||||
|  | ||||
| 		// Called by plugins to register/unregister themselves | ||||
| 		registerPlugin: registerPlugin, | ||||
|  | ||||
| 		// Programatically triggers a keyboard event | ||||
| 		triggerKey: function( keyCode ) { | ||||
| 			onDocumentKeyDown( { keyCode: keyCode } ); | ||||
|   | ||||
| @@ -7,13 +7,11 @@ | ||||
| 	if (typeof define === 'function' && define.amd) { | ||||
| 		root.marked = require( './marked' ); | ||||
| 		root.RevealMarkdown = factory( root.marked ); | ||||
| 		root.RevealMarkdown.initialize(); | ||||
| 	} else if( typeof exports === 'object' ) { | ||||
| 		module.exports = factory( require( './marked' ) ); | ||||
| 	} else { | ||||
| 		// Browser globals (root is window) | ||||
| 		root.RevealMarkdown = factory( root.marked ); | ||||
| 		root.RevealMarkdown.initialize(); | ||||
| 	} | ||||
| }( this, function( marked ) { | ||||
|  | ||||
| @@ -24,6 +22,10 @@ | ||||
|  | ||||
| 	var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__'; | ||||
|  | ||||
| 	var markdownFilesToLoad = 0; | ||||
|  | ||||
| 	var loadCallback; | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 	 * Retrieves the markdown contents of a slide section | ||||
| @@ -199,15 +201,37 @@ | ||||
| 	 */ | ||||
| 	function processSlides() { | ||||
|  | ||||
| 		var sections = document.querySelectorAll( '[data-markdown]'), | ||||
| 			section; | ||||
|  | ||||
| 		for( var i = 0, len = sections.length; i < len; i++ ) { | ||||
|  | ||||
| 			section = sections[i]; | ||||
| 		[].slice.call( document.querySelectorAll( '[data-markdown]') ).forEach( function( section, i ) { | ||||
|  | ||||
| 			if( section.getAttribute( 'data-markdown' ).length ) { | ||||
|  | ||||
| 				loadExternalMarkdown( section ); | ||||
|  | ||||
| 			} | ||||
| 			else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) { | ||||
|  | ||||
| 				section.outerHTML = slidify( getMarkdownFromSlide( section ), { | ||||
| 					separator: section.getAttribute( 'data-separator' ), | ||||
| 					verticalSeparator: section.getAttribute( 'data-separator-vertical' ), | ||||
| 					notesSeparator: section.getAttribute( 'data-separator-notes' ), | ||||
| 					attributes: getForwardedAttributes( section ) | ||||
| 				}); | ||||
|  | ||||
| 			} | ||||
| 			else { | ||||
| 				section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) ); | ||||
| 			} | ||||
|  | ||||
| 		}); | ||||
|  | ||||
| 		checkIfLoaded(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	function loadExternalMarkdown( section ) { | ||||
|  | ||||
| 		markdownFilesToLoad += 1; | ||||
|  | ||||
| 		var xhr = new XMLHttpRequest(), | ||||
| 			url = section.getAttribute( 'data-markdown' ); | ||||
|  | ||||
| @@ -218,7 +242,7 @@ | ||||
| 			xhr.overrideMimeType( 'text/html; charset=' + datacharset ); | ||||
| 		} | ||||
|  | ||||
| 				xhr.onreadystatechange = function() { | ||||
| 		xhr.onreadystatechange = function( section, xhr ) { | ||||
| 			if( xhr.readyState === 4 ) { | ||||
| 				// file protocol yields status code 0 (useful for local debug, mobile applications etc.) | ||||
| 				if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) { | ||||
| @@ -240,10 +264,16 @@ | ||||
| 						'</section>'; | ||||
|  | ||||
| 				} | ||||
| 					} | ||||
| 				}; | ||||
|  | ||||
| 				xhr.open( 'GET', url, false ); | ||||
| 				convertSlides(); | ||||
|  | ||||
| 				markdownFilesToLoad -= 1; | ||||
|  | ||||
| 				checkIfLoaded(); | ||||
| 			} | ||||
| 		}.bind( this, section, xhr ); | ||||
|  | ||||
| 		xhr.open( 'GET', url, true ); | ||||
|  | ||||
| 		try { | ||||
| 			xhr.send(); | ||||
| @@ -252,22 +282,6 @@ | ||||
| 			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-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) { | ||||
|  | ||||
| 				section.outerHTML = slidify( getMarkdownFromSlide( section ), { | ||||
| 					separator: section.getAttribute( 'data-separator' ), | ||||
| 					verticalSeparator: section.getAttribute( 'data-separator-vertical' ), | ||||
| 					notesSeparator: section.getAttribute( 'data-separator-notes' ), | ||||
| 					attributes: getForwardedAttributes( section ) | ||||
| 				}); | ||||
|  | ||||
| 			} | ||||
| 			else { | ||||
| 				section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) ); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -342,14 +356,9 @@ | ||||
| 	 */ | ||||
| 	function convertSlides() { | ||||
|  | ||||
| 		var sections = document.querySelectorAll( '[data-markdown]'); | ||||
| 		var sections = document.querySelectorAll( '[data-markdown]:not([data-markdown-parsed])'); | ||||
|  | ||||
| 		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' ) ) { | ||||
| 		[].slice.call( sections ).forEach( function( section ) { | ||||
|  | ||||
| 			section.setAttribute( 'data-markdown-parsed', true ) | ||||
|  | ||||
| @@ -370,16 +379,33 @@ | ||||
| 				section.appendChild( notes ); | ||||
| 			} | ||||
|  | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	function checkIfLoaded() { | ||||
|  | ||||
| 		if( markdownFilesToLoad === 0 ) { | ||||
| 			if( loadCallback ) { | ||||
| 				loadCallback(); | ||||
| 				loadCallback = null; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// API | ||||
| 	return { | ||||
| 	var RevealMarkdown = { | ||||
|  | ||||
| 		/** | ||||
| 		 * Starts processing and converting Markdown within the | ||||
| 		 * current reveal.js deck. | ||||
| 		 * | ||||
| 		 * @param {function} callback function to invoke once | ||||
| 		 * we've finished loading and parsing Markdown | ||||
| 		 */ | ||||
| 		init: function( callback ) { | ||||
|  | ||||
| 		initialize: function() { | ||||
| 			if( typeof marked === 'undefined' ) { | ||||
| 				throw 'The reveal.js Markdown plugin requires marked to be loaded'; | ||||
| 			} | ||||
| @@ -392,14 +418,17 @@ | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			// marked can be configured via reveal.js config options | ||||
| 			var options = Reveal.getConfig().markdown; | ||||
|  | ||||
| 			if ( options ) { | ||||
| 			if( options ) { | ||||
| 				marked.setOptions( options ); | ||||
| 			} | ||||
|  | ||||
| 			loadCallback = callback; | ||||
|  | ||||
| 			processSlides(); | ||||
| 			convertSlides(); | ||||
|  | ||||
| 		}, | ||||
|  | ||||
| 		// TODO: Do these belong in the API? | ||||
| @@ -409,4 +438,10 @@ | ||||
|  | ||||
| 	}; | ||||
|  | ||||
| 	// Register our plugin so that reveal.js will call our | ||||
| 	// plugin 'init' method as part of the initialization | ||||
| 	Reveal.registerPlugin( 'markdown', RevealMarkdown ); | ||||
|  | ||||
| 	return RevealMarkdown; | ||||
|  | ||||
| })); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user