/* Example: absoluteUrl(“../subfolder1/divaspari.md”, “images/forest-fire.jpg”) == “../subfolder1/images/forest-fire.jpg”
*/
function absoluteUrl(base, relative) {
// console.debug(base, relative); if (relative.startsWith("http") || relative.startsWith("file")) { return relative; } if (relative.startsWith("/") && !base.startsWith("http") && !base.startsWith("file")) { return relative; } var stack = base.toString().split("/"), parts = relative.split("/"); stack.pop(); // remove current file name (or empty string) // (omit if "base" is the current folder without trailing slash) for (var i=0; i<parts.length; i++) { if (parts[i] == ".") continue; if (parts[i] == "..") stack.pop(); else stack.push(parts[i]); } return stack.join("/");
}
// WHen you include html from one page within another, you need to fix image urls, anchor urls etc.. function fixIncludedHtml(url, html, newLevelForH1) {
// We want to use jquery to parse html, but without loading images. Hence this. // Tip from: https://stackoverflow.com/questions/15113910/jquery-parse-html-without-loading-images var virtualDocument = document.implementation.createHTMLDocument('virtual'); var jqueryElement = $(html, virtualDocument); // console.debug(jqueryElement.html()); // Remove some tags. jqueryElement.find("script").remove(); jqueryElement.find("footer").remove(); jqueryElement.find("#disqus_thread").remove(); jqueryElement.find("#toc").remove(); jqueryElement.find("#toc_header").remove(); jqueryElement.find(".back-to-top").remove(); // console.debug(jqueryElement.html()); // Deal with includes within includes. Do this before fixing images urls etc.. because there may be images within the newly included html. jqueryElement.find('.js_include').each(function() { if (newLevelForH1 < 1) { console.error("Ignoring invalid newLevelForH1: %d, using 6", newLevelForH1); newLevelForH1 = 6; } var jsIncludeElement = $(this); var includedPageNewLevelForH2 = parseInt(jsIncludeJqueryElement.attr("newLevelForH1")); if (includedPageNewLevelForH2 == undefined) { includedPageNewLevelForH2 = 6; } includedPageNewLevelForH2 = Math.min(6, ((includedPageNewLevelForH2 - 2) + newLevelForH1)); fillJsInclude($(this), includedPageNewLevelForH2); }); /* Fix headers in the included html so as to not mess up the table of contents of the including page. Adjusting the heading levels to retain substructure seems more complicated - getting the heading "under" which jsIncludeJqueryElement falls seems non-trivial. */ var headers = jqueryElement.find(":header"); if (headers.length > 0) { var id_prefix = headers[0].id; headers.replaceWith(function() { var headerElement = $(this); // console.debug(headerElement); var hLevel = parseInt(headerElement.prop("tagName").substring(1)); var hLevelNew = Math.min(6, newLevelForH1 - 1 + hLevel); var newId = id_prefix + "_" + headerElement[0].id; return $("<h" + hLevelNew +" id='" + newId + "'/>").append(headerElement.contents()); }); } // Fix image urls. jqueryElement.find("img").each(function() { // console.log(absoluteUrl(url, $(this).attr("src"))); // console.log($(this).attr("src")) $(this).attr("src", absoluteUrl(url, $(this).attr("src"))); // console.log($(this).attr("src")) }); // Fix links. jqueryElement.find("a").each(function() { // console.debug($(this).html()); var href = $(this).attr("href"); if (href.startsWith("#")) { var headers = jqueryElement.find(":header"); var new_href = href; if (headers.length > 0) { var id_prefix = headers[0].id; new_href = id_prefix + "_" + href.substr(1); // console.debug(new_href, id_prefix, href); jqueryElement.find(href).each(function () { $(this).attr("id", new_href.substr(1)); }); } $(this).attr("href", new_href); } else { $(this).attr("href", absoluteUrl(url, href)); } }); return jqueryElement;
}
function fillJsInclude(jsIncludeJqueryElement, includedPageNewLevelForH1) {
var includedPageUrl = jsIncludeJqueryElement.attr("url").replace(".md", ".html"); if (includedPageNewLevelForH1 == undefined) { includedPageNewLevelForH1 = parseInt(jsIncludeJqueryElement.attr("newLevelForH1")); } if (includedPageNewLevelForH1 == undefined) { includedPageNewLevelForH1 = 6; } $.ajax(includedPageUrl,{ success: function(responseHtml) { // We want to use jquery to parse html, but without loading images. Hence this. // Tip from: https://stackoverflow.com/questions/15113910/jquery-parse-html-without-loading-images var virtualDocument = document.implementation.createHTMLDocument('virtual'); var titleElements = $(responseHtml, virtualDocument).find(".post-title-main"); var title = ""; if (titleElements.length > 0) { // console.debug(titleElements[0]); title = titleElements[0].textContent; } var contentElements = $(responseHtml, virtualDocument).find(".post-content"); // console.log(contentElements); if (contentElements.length == 0) { console.warn("Could not get \"post-content\" class element."); console.log(responseHtml); } else { // We don't want multiple post-content divs, hence we replace with an included-post-content div. var elementToInclude = $("<div class='included-post-content'/>") var titleHtml = ""; if (jsIncludeJqueryElement.attr("includeTitle")) { titleHtml = "<h1 id='" + title + "'>" + title + "</h1>" + "<a class='btn btn-default' href='" + absoluteUrl(document.location, includedPageUrl) + "'>See separately</a>"; } elementToInclude.html(titleHtml + contentElements[0].innerHTML); var contentElement = fixIncludedHtml(includedPageUrl, elementToInclude, includedPageNewLevelForH1); jsIncludeJqueryElement.html(contentElement); fillAudioEmbeds(); fillVideoEmbeds(); updateToc(); } }, error: function(xhr, error){ var titleHtml = ""; var title = "Missing page."; if (jsIncludeJqueryElement.attr("includeTitle")) { titleHtml = "<h1 id='" + title + "'>" + title + "</h1>"; } jsIncludeJqueryElement.html(titleHtml + "Could not get: " + includedPageUrl + " See debug messages in console for details."); console.debug(xhr); console.debug(error); } });
}
// Process includes of the form: // <div class=“js_include” url=“index.md”/> $( document ).ready(function() {
$('.js_include').each(function() { console.debug("Inserting include for " + $(this).html()); var jsIncludeJqueryElement = $(this); // The actual filling happens in a separate thread! fillJsInclude(jsIncludeJqueryElement); });
});