Browse Source

Modules: Add code highlighting using hooks.

Achieved using Prism.js (MIT licensed) library and its language addons.
Website: http://prismjs.com/
master
Maciej Smolinski 10 years ago
parent
commit
9690f717fe
  1. 1
      resources/edit.html
  2. 157
      resources/public/js/highlight.js
  3. 1
      resources/template.html

1
resources/edit.html

@ -10,6 +10,7 @@
<script src="//cdnjs.cloudflare.com/ajax/libs/blueimp-md5/1.0.1/js/md5.min.js" type="text/javascript"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/blueimp-md5/1.0.1/js/md5.min.js" type="text/javascript"></script>
<script src="/js/events.js" type="text/javascript"></script> <script src="/js/events.js" type="text/javascript"></script>
<script src="/js/publishing.js" type="text/javascript"></script> <script src="/js/publishing.js" type="text/javascript"></script>
<script src="/js/highlight.js" type="text/javascript"></script>
<base target="_blank"> <base target="_blank">
</head> </head>

157
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));

1
resources/template.html

@ -14,6 +14,7 @@
%FOOTER% %FOOTER%
<script src="/js/events.js"></script> <script src="/js/events.js"></script>
<script src="/js/highlight.js"></script>
<script> <script>
events.publish('document:loaded'); events.publish('document:loaded');
</script> </script>

Loading…
Cancel
Save