' +
"ERROR: The attempt to fetch " +
url +
" failed with HTTP status " +
xhr.status +
"." +
"Check your browser's JavaScript console for more details." +
"Remember that you need to serve the presentation HTML from a HTTP server.
" +
"";
}
)
);
} else {
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),
});
}
});
Promise.all(externalPromises).then(resolve);
});
}
function loadExternalMarkdown(section) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest(),
url = section.getAttribute("data-markdown");
var 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 (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) {
resolve(xhr, url);
} else {
reject(xhr, url);
}
}
}.bind(this, section, xhr);
xhr.open("GET", url, true);
try {
xhr.send();
} catch (e) {
console.warn(
"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
);
resolve(xhr, url);
}
});
}
/**
* Check if a node value has the attributes pattern.
* If yes, extract it and add that value as one or several attributes
* to the target 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(
'([^"= ]+?)="([^"]+?)"|(data-[^"= ]+?)(?=[" ])',
"mg"
);
var nodeValue = node.nodeValue;
var matches, matchesClass;
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))) {
if (matchesClass[2]) {
elementTarget.setAttribute(matchesClass[1], matchesClass[2]);
} else {
elementTarget.setAttribute(matchesClass[3], "");
}
}
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
) {
var previousParentElement = element;
for (var i = 0; i < element.childNodes.length; i++) {
var childElement = element.childNodes[i];
if (i > 0) {
var j = i - 1;
while (j >= 0) {
var aPreviousChildElement = element.childNodes[j];
if (
typeof aPreviousChildElement.setAttribute == "function" &&
aPreviousChildElement.tagName != "BR"
) {
previousParentElement = aPreviousChildElement;
break;
}
j = j - 1;
}
}
var 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 = deck
.getRevealElement()
.querySelectorAll("[data-markdown]:not([data-markdown-parsed])");
[].slice.call(sections).forEach(function (section) {
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);
}
});
return Promise.resolve();
}
function escapeForHTML(input) {
return input.replace(/([&<>'"])/g, (char) => HTML_ESCAPE_MAP[char]);
}
return {
id: "markdown",
/**
* Starts processing and converting Markdown within the
* current reveal.js deck.
*/
init: function (reveal) {
deck = reveal;
let { renderer, animateLists, ...markedOptions } =
deck.getConfig().markdown || {};
if (!renderer) {
renderer = new marked.Renderer();
renderer.code = (code, language) => {
// Off by default
let lineNumbers = "";
// Users can opt in to show line numbers and highlight
// specific lines.
// ```javascript [] show line numbers
// ```javascript [1,4-8] highlights lines 1 and 4-8
if (CODE_LINE_NUMBER_REGEX.test(language)) {
lineNumbers = language.match(CODE_LINE_NUMBER_REGEX)[1].trim();
lineNumbers = `data-line-numbers="${lineNumbers}"`;
language = language.replace(CODE_LINE_NUMBER_REGEX, "").trim();
}
// Escape before this gets injected into the DOM to
// avoid having the HTML parser alter our code before
// highlight.js is able to read it
code = escapeForHTML(code);
return `