diff --git a/resources/edit.html b/resources/edit.html index 8c286ef..6325934 100644 --- a/resources/edit.html +++ b/resources/edit.html @@ -10,6 +10,7 @@ + diff --git a/resources/public/js/highlight.js b/resources/public/js/highlight.js new file mode 100644 index 0000000..88bfdca --- /dev/null +++ b/resources/public/js/highlight.js @@ -0,0 +1,157 @@ +/** + * Highlight Module + * + * + * High Level API: + * + * // Load basic dependencies and language support for present code tags + * + * highlight.init() + * + * + * // Function that loads additional languages support + * // Useful when markdown has been parsed again + * // and new language occurred in the output for example + * + * highlight.update(); + * + * + * Hooks To: + * + * 'document:loaded' ~> highlight.init(); + * 'content:rendered' ~> highlight.update(); + * + */ +(function (global) { + + // Simplified debounce version (no args support) + function debounce(callback, milliseconds) { + var timeout; + + return function () { + clearTimeout(timeout); + + timeout = setTimeout(function () { + callback(); + }, milliseconds); + }; + } + + // Converts arguments-like / querySelector results data to a simple array + function toArray(data) { + return [].slice.call(data); + } + + // Highlight callback + function highlight() { + if (!('Prism' in global)) { + throw new Error( + '[Highlight] Prism not detected. Please run `highlight.init` to load all dependencies' + ); + } + + global.Prism.highlightAll(); + } + + // Debounced highlight callback + var debouncedHighlight = debounce(highlight, 300); + + // Collect a list of unique langages to be highlighted + function collectLanguages() { + return [] + .concat(toArray(document.querySelectorAll('code[class*="lang-"]'))) + .concat(toArray(document.querySelectorAll('code[class*="language-"]'))) + .map(function (element) { + // Collect languages from code elements, e.g. `lang-css`, `language-javascript` + // and then remove `lang-` and `language-` parts + return (element.className.match(/lang(uage)?\-\w+/g) || []) + .map(function (languageClass) { + return languageClass.replace(/lang(uage)?\-/g, '').trim(); + }) + .map(function (language) { + // Common language abbreviations mapped to full language names + var mappings = { + js: 'javascript', + }; + + return mappings[language] || language; + }); + }) + .reduce(function (uniqueLanguages, elementLanguages) { + elementLanguages.forEach(function (language) { + // Add language to the pool if not detected already + if (uniqueLanguages.indexOf(language) === -1) { + uniqueLanguages.push(language); + } + }); + + return uniqueLanguages; + }, []); + } + + // Load scripts for additional languages support + function loadAdditionalLanguageSupport() { + collectLanguages() + .forEach(function (language) { + var resource = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/prism-%s.min.js' + .replace('%s', language); + + // Escape early if language support is already loaded + if (document.querySelector('script[src="' + resource + '"]')) { + debouncedHighlight(); + return; + } + + // Load language support file otherwise + var script = document.createElement('script'); + + script.src = resource; + + script.addEventListener('load', debouncedHighlight); + script.addEventListener('error', function (event) { + // Remove element that wasn't successful + document.body.removeChild(event.srcElement); + + // Highlight code anyway + debouncedHighlight(); + }); + + document.body.appendChild(script); + }); + } + + // Load minimal requirements + function loadInitialScriptsAndStyles() { + var link = document.createElement('link'); + var script = document.createElement('script'); + + link.rel = 'stylesheet'; + link.href = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css'; + script.src = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js'; + + script.addEventListener('load', global.highlight.update); + + document.head.appendChild(link); + document.body.appendChild(script); + } + + // High Level API + global.highlight = global.highlight || { + + init: function () { + loadInitialScriptsAndStyles(); + }, + + update: function () { + loadAdditionalLanguageSupport(); + }, + + }; + + // Hooks + if ('events' in global) { + events.subscribe('document:loaded', global.highlight.init); + events.subscribe('content:rendered', global.highlight.update); + } + +}(window)); diff --git a/resources/template.html b/resources/template.html index c88ce88..01c8461 100644 --- a/resources/template.html +++ b/resources/template.html @@ -14,6 +14,7 @@ %FOOTER% +