markdown.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // From https://gist.github.com/1343518
  2. // Modified by Hakim to handle Markdown indented with tabs
  3. (function(){
  4. if( typeof marked === 'undefined' ) {
  5. throw 'The reveal.js Markdown plugin requires marked to be loaded';
  6. }
  7. if (typeof hljs !== 'undefined') {
  8. marked.setOptions({
  9. highlight: function (lang, code) {
  10. return hljs.highlightAuto(lang, code).value;
  11. }
  12. });
  13. }
  14. var stripLeadingWhitespace = function(section) {
  15. var template = section.querySelector( 'script' );
  16. // strip leading whitespace so it isn't evaluated as code
  17. var text = ( template || section ).textContent;
  18. var leadingWs = text.match(/^\n?(\s*)/)[1].length,
  19. leadingTabs = text.match(/^\n?(\t*)/)[1].length;
  20. if( leadingTabs > 0 ) {
  21. text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
  22. }
  23. else if( leadingWs > 1 ) {
  24. text = text.replace( new RegExp('\\n? {' + leadingWs + '}','g'), '\n' );
  25. }
  26. return text;
  27. };
  28. var twrap = function(el) {
  29. var content = el.content || el;
  30. content += el.asideContent ? ('<aside class="notes" data-markdown>' + el.asideContent + '</aside>') : '';
  31. return '<script type="text/template">' + content + '</script>';
  32. };
  33. var getForwardedAttributes = function(section) {
  34. var attributes = section.attributes;
  35. var result = [];
  36. for( var i = 0, len = attributes.length; i < len; i++ ) {
  37. var name = attributes[i].name,
  38. value = attributes[i].value;
  39. // disregard attributes that are used for markdown loading/parsing
  40. if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;
  41. if( value ) {
  42. result.push( name + '=' + value );
  43. }
  44. else {
  45. result.push( name );
  46. }
  47. }
  48. return result.join( ' ' );
  49. };
  50. var slidifyMarkdown = function(markdown, separator, vertical, notes, attributes) {
  51. separator = separator || '^\n---\n$';
  52. notes = notes || 'note:';
  53. var separatorRegex = new RegExp( separator + ( vertical ? '|' + vertical : '' ), 'mg' ),
  54. horizontalSeparatorRegex = new RegExp( separator ),
  55. notesSeparatorRegex = new RegExp( notes, 'mgi' ),
  56. matches,
  57. noteMatch,
  58. lastIndex = 0,
  59. isHorizontal,
  60. wasHorizontal = true,
  61. content,
  62. asideContent,
  63. slide,
  64. sectionStack = [],
  65. markdownSections = '';
  66. // iterate until all blocks between separators are stacked up
  67. while( matches = separatorRegex.exec( markdown ) ) {
  68. asideContent = null;
  69. // determine direction (horizontal by default)
  70. isHorizontal = horizontalSeparatorRegex.test( matches[0] );
  71. if( !isHorizontal && wasHorizontal ) {
  72. // create vertical stack
  73. sectionStack.push( [] );
  74. }
  75. // pluck slide content from markdown input
  76. content = markdown.substring( lastIndex, matches.index );
  77. noteMatch = content.split( notesSeparatorRegex );
  78. if( noteMatch.length === 2 ) {
  79. content = noteMatch[0];
  80. asideContent = noteMatch[1].trim();
  81. }
  82. slide = {
  83. content: content,
  84. asideContent: asideContent || ""
  85. };
  86. if( isHorizontal && wasHorizontal ) {
  87. // add to horizontal stack
  88. sectionStack.push(slide);
  89. } else {
  90. // add to vertical stack
  91. sectionStack[sectionStack.length-1].push(slide);
  92. }
  93. lastIndex = separatorRegex.lastIndex;
  94. wasHorizontal = isHorizontal;
  95. }
  96. // add the remaining slide
  97. (wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1]).push(markdown.substring(lastIndex));
  98. // flatten the hierarchical stack, and insert <section data-markdown> tags
  99. for( var k = 0, klen = sectionStack.length; k < klen; k++ ) {
  100. // vertical
  101. if( sectionStack[k].propertyIsEnumerable(length) && typeof sectionStack[k].splice === 'function' ) {
  102. markdownSections += '<section '+ attributes +'>' +
  103. '<section data-markdown>' + sectionStack[k].map(twrap).join('</section><section data-markdown>') + '</section>' +
  104. '</section>';
  105. } else {
  106. markdownSections += '<section '+ attributes +' data-markdown>' + twrap( sectionStack[k] ) + '</section>';
  107. }
  108. }
  109. return markdownSections;
  110. };
  111. var querySlidingMarkdown = function() {
  112. var sections = document.querySelectorAll( '[data-markdown]'),
  113. section;
  114. for( var j = 0, jlen = sections.length; j < jlen; j++ ) {
  115. section = sections[j];
  116. if( section.getAttribute('data-markdown').length ) {
  117. var xhr = new XMLHttpRequest(),
  118. url = section.getAttribute('data-markdown');
  119. datacharset = section.getAttribute('data-charset');
  120. // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
  121. if (datacharset != null && datacharset != '') {
  122. xhr.overrideMimeType('text/html; charset=' + datacharset);
  123. }
  124. xhr.onreadystatechange = function () {
  125. if( xhr.readyState === 4 ) {
  126. if (xhr.status >= 200 && xhr.status < 300) {
  127. section.outerHTML = slidifyMarkdown( xhr.responseText, section.getAttribute('data-separator'), section.getAttribute('data-vertical'), section.getAttribute('data-notes'), getForwardedAttributes(section) );
  128. } else {
  129. section.outerHTML = '<section data-state="alert">ERROR: The attempt to fetch ' + url + ' failed with the HTTP status ' + xhr.status +
  130. '. Check your browser\'s JavaScript console for more details.' +
  131. '<p>Remember that you need to serve the presentation HTML from a HTTP server and the Markdown file must be there too.</p></section>';
  132. }
  133. }
  134. };
  135. xhr.open('GET', url, false);
  136. try {
  137. xhr.send();
  138. } catch (e) {
  139. 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);
  140. }
  141. } else if( section.getAttribute('data-separator') ) {
  142. var markdown = stripLeadingWhitespace(section);
  143. section.outerHTML = slidifyMarkdown( markdown, section.getAttribute('data-separator'), section.getAttribute('data-vertical'), section.getAttribute('data-notes'), getForwardedAttributes(section) );
  144. }
  145. }
  146. };
  147. var queryMarkdownSlides = function() {
  148. var sections = document.querySelectorAll( '[data-markdown]');
  149. for( var j = 0, jlen = sections.length; j < jlen; j++ ) {
  150. makeHtml(sections[j]);
  151. }
  152. };
  153. var makeHtml = function(section) {
  154. var notes = section.querySelector( 'aside.notes' );
  155. var markdown = stripLeadingWhitespace(section);
  156. section.innerHTML = marked(markdown);
  157. if( notes ) {
  158. section.appendChild( notes );
  159. }
  160. };
  161. querySlidingMarkdown();
  162. queryMarkdownSlides();
  163. })();