Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
| Download
Project: KOB1
Views: 16973/**1* The reveal.js markdown plugin. Handles parsing of2* markdown inside of presentations as well as loading3* of external markdown documents.4*/5(function( root, factory ) {6if (typeof define === 'function' && define.amd) {7root.marked = require( './marked' );8root.RevealMarkdown = factory( root.marked );9root.RevealMarkdown.initialize();10} else if( typeof exports === 'object' ) {11module.exports = factory( require( './marked' ) );12} else {13// Browser globals (root is window)14root.RevealMarkdown = factory( root.marked );15root.RevealMarkdown.initialize();16}17}( this, function( marked ) {1819var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$',20DEFAULT_NOTES_SEPARATOR = 'notes?:',21DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',22DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';2324var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__';252627/**28* Retrieves the markdown contents of a slide section29* element. Normalizes leading tabs/whitespace.30*/31function getMarkdownFromSlide( section ) {3233// look for a <script> or <textarea data-template> wrapper34var template = section.querySelector( '[data-template]' ) || section.querySelector( 'script' );3536// strip leading whitespace so it isn't evaluated as code37var text = ( template || section ).textContent;3839// restore script end tags40text = text.replace( new RegExp( SCRIPT_END_PLACEHOLDER, 'g' ), '</script>' );4142var leadingWs = text.match( /^\n?(\s*)/ )[1].length,43leadingTabs = text.match( /^\n?(\t*)/ )[1].length;4445if( leadingTabs > 0 ) {46text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );47}48else if( leadingWs > 1 ) {49text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' );50}5152return text;5354}5556/**57* Given a markdown slide section element, this will58* return all arguments that aren't related to markdown59* parsing. Used to forward any other user-defined arguments60* to the output markdown slide.61*/62function getForwardedAttributes( section ) {6364var attributes = section.attributes;65var result = [];6667for( var i = 0, len = attributes.length; i < len; i++ ) {68var name = attributes[i].name,69value = attributes[i].value;7071// disregard attributes that are used for markdown loading/parsing72if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;7374if( value ) {75result.push( name + '="' + value + '"' );76}77else {78result.push( name );79}80}8182return result.join( ' ' );8384}8586/**87* Inspects the given options and fills out default88* values for what's not defined.89*/90function getSlidifyOptions( options ) {9192options = options || {};93options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;94options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;95options.attributes = options.attributes || '';9697return options;9899}100101/**102* Helper function for constructing a markdown slide.103*/104function createMarkdownSlide( content, options ) {105106options = getSlidifyOptions( options );107108var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );109110if( notesMatch.length === 2 ) {111content = notesMatch[0] + '<aside class="notes">' + marked(notesMatch[1].trim()) + '</aside>';112}113114// prevent script end tags in the content from interfering115// with parsing116content = content.replace( /<\/script>/g, SCRIPT_END_PLACEHOLDER );117118return '<script type="text/template">' + content + '</script>';119120}121122/**123* Parses a data string into multiple slides based124* on the passed in separator arguments.125*/126function slidify( markdown, options ) {127128options = getSlidifyOptions( options );129130var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),131horizontalSeparatorRegex = new RegExp( options.separator );132133var matches,134lastIndex = 0,135isHorizontal,136wasHorizontal = true,137content,138sectionStack = [];139140// iterate until all blocks between separators are stacked up141while( matches = separatorRegex.exec( markdown ) ) {142notes = null;143144// determine direction (horizontal by default)145isHorizontal = horizontalSeparatorRegex.test( matches[0] );146147if( !isHorizontal && wasHorizontal ) {148// create vertical stack149sectionStack.push( [] );150}151152// pluck slide content from markdown input153content = markdown.substring( lastIndex, matches.index );154155if( isHorizontal && wasHorizontal ) {156// add to horizontal stack157sectionStack.push( content );158}159else {160// add to vertical stack161sectionStack[sectionStack.length-1].push( content );162}163164lastIndex = separatorRegex.lastIndex;165wasHorizontal = isHorizontal;166}167168// add the remaining slide169( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );170171var markdownSections = '';172173// flatten the hierarchical stack, and insert <section data-markdown> tags174for( var i = 0, len = sectionStack.length; i < len; i++ ) {175// vertical176if( sectionStack[i] instanceof Array ) {177markdownSections += '<section '+ options.attributes +'>';178179sectionStack[i].forEach( function( child ) {180markdownSections += '<section data-markdown>' + createMarkdownSlide( child, options ) + '</section>';181} );182183markdownSections += '</section>';184}185else {186markdownSections += '<section '+ options.attributes +' data-markdown>' + createMarkdownSlide( sectionStack[i], options ) + '</section>';187}188}189190return markdownSections;191192}193194/**195* Parses any current data-markdown slides, splits196* multi-slide markdown into separate sections and197* handles loading of external markdown.198*/199function processSlides() {200201var sections = document.querySelectorAll( '[data-markdown]'),202section;203204for( var i = 0, len = sections.length; i < len; i++ ) {205206section = sections[i];207208if( section.getAttribute( 'data-markdown' ).length ) {209210var xhr = new XMLHttpRequest(),211url = section.getAttribute( 'data-markdown' );212213datacharset = section.getAttribute( 'data-charset' );214215// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes216if( datacharset != null && datacharset != '' ) {217xhr.overrideMimeType( 'text/html; charset=' + datacharset );218}219220xhr.onreadystatechange = function() {221if( xhr.readyState === 4 ) {222// file protocol yields status code 0 (useful for local debug, mobile applications etc.)223if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {224225section.outerHTML = slidify( xhr.responseText, {226separator: section.getAttribute( 'data-separator' ),227verticalSeparator: section.getAttribute( 'data-separator-vertical' ),228notesSeparator: section.getAttribute( 'data-separator-notes' ),229attributes: getForwardedAttributes( section )230});231232}233else {234235section.outerHTML = '<section data-state="alert">' +236'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +237'Check your browser\'s JavaScript console for more details.' +238'<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +239'</section>';240241}242}243};244245xhr.open( 'GET', url, false );246247try {248xhr.send();249}250catch ( e ) {251alert( '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 );252}253254}255else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {256257section.outerHTML = slidify( getMarkdownFromSlide( section ), {258separator: section.getAttribute( 'data-separator' ),259verticalSeparator: section.getAttribute( 'data-separator-vertical' ),260notesSeparator: section.getAttribute( 'data-separator-notes' ),261attributes: getForwardedAttributes( section )262});263264}265else {266section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );267}268}269270}271272/**273* Check if a node value has the attributes pattern.274* If yes, extract it and add that value as one or several attributes275* the the terget element.276*277* You need Cache Killer on Chrome to see the effect on any FOM transformation278* directly on refresh (F5)279* http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277280*/281function addAttributeInElement( node, elementTarget, separator ) {282283var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );284var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' );285var nodeValue = node.nodeValue;286if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {287288var classes = matches[1];289nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );290node.nodeValue = nodeValue;291while( matchesClass = mardownClassRegex.exec( classes ) ) {292elementTarget.setAttribute( matchesClass[1], matchesClass[2] );293}294return true;295}296return false;297}298299/**300* Add attributes to the parent element of a text node,301* or the element of an attribute node.302*/303function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {304305if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {306previousParentElement = element;307for( var i = 0; i < element.childNodes.length; i++ ) {308childElement = element.childNodes[i];309if ( i > 0 ) {310j = i - 1;311while ( j >= 0 ) {312aPreviousChildElement = element.childNodes[j];313if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {314previousParentElement = aPreviousChildElement;315break;316}317j = j - 1;318}319}320parentSection = section;321if( childElement.nodeName == "section" ) {322parentSection = childElement ;323previousParentElement = childElement ;324}325if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {326addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );327}328}329}330331if ( element.nodeType == Node.COMMENT_NODE ) {332if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {333addAttributeInElement( element, section, separatorSectionAttributes );334}335}336}337338/**339* Converts any current data-markdown slides in the340* DOM to HTML.341*/342function convertSlides() {343344var sections = document.querySelectorAll( '[data-markdown]');345346for( var i = 0, len = sections.length; i < len; i++ ) {347348var section = sections[i];349350// Only parse the same slide once351if( !section.getAttribute( 'data-markdown-parsed' ) ) {352353section.setAttribute( 'data-markdown-parsed', true )354355var notes = section.querySelector( 'aside.notes' );356var markdown = getMarkdownFromSlide( section );357358section.innerHTML = marked( markdown );359addAttributes( section, section, null, section.getAttribute( 'data-element-attributes' ) ||360section.parentNode.getAttribute( 'data-element-attributes' ) ||361DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,362section.getAttribute( 'data-attributes' ) ||363section.parentNode.getAttribute( 'data-attributes' ) ||364DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);365366// If there were notes, we need to re-add them after367// having overwritten the section's HTML368if( notes ) {369section.appendChild( notes );370}371372}373374}375376}377378// API379return {380381initialize: function() {382if( typeof marked === 'undefined' ) {383throw 'The reveal.js Markdown plugin requires marked to be loaded';384}385386if( typeof hljs !== 'undefined' ) {387marked.setOptions({388highlight: function( code, lang ) {389return hljs.highlightAuto( code, [lang] ).value;390}391});392}393394var options = Reveal.getConfig().markdown;395396if ( options ) {397marked.setOptions( options );398}399400processSlides();401convertSlides();402},403404// TODO: Do these belong in the API?405processSlides: processSlides,406convertSlides: convertSlides,407slidify: slidify408409};410411}));412413414