diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-03-22 01:14:35 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-03-22 01:14:35 +0100 |
commit | 93119091893c2f2bf396785e6bd7dcbb9b4f207d (patch) | |
tree | 408e37ec0bf4ebb1e5427f37b1e12344f70eae9c /script | |
parent | f6fc9fa292e8da56fe7078f9ac5bdfefa55abe94 (diff) |
* Implement code highlighting using Prism
* Automatically build ousia.js
Diffstat (limited to 'script')
-rw-r--r-- | script/highlight.js | 29 | ||||
-rw-r--r-- | script/prism.js | 428 | ||||
-rw-r--r-- | script/prism_bash.js (renamed from script/ousia.js) | 19 |
3 files changed, 466 insertions, 10 deletions
diff --git a/script/highlight.js b/script/highlight.js new file mode 100644 index 0000000..c50c172 --- /dev/null +++ b/script/highlight.js @@ -0,0 +1,29 @@ +// @license magnet:?xt=urn:btih:5305d91886084f776adcf57509a648432709a7c7&dn=x11.txt +/* + * Ousía Website JS + * + * (c) Andreas Stöckel, 2015 + * + * This work is licensed under the X11 (MIT) license. + * http://www.opensource.org/licenses/mit-license.php + */ +(function () { + "use strict"; + + /* Enable code highlighting (this will eventually be natively supported by + Ousía) */ + document.addEventListener("DOMContentLoaded", function(event) { + var codeBlocks = document.querySelectorAll("pre.code[data-lang]"); + for (var i = 0; i < codeBlocks.length; i++) { + var block = codeBlocks[i]; + var lang = block.getAttribute("data-lang"); + var text = block.textContent; + if (lang in Prism.languages) { + var res = Prism.highlight(text, Prism.languages[lang]); + block.innerHTML = res; + } + } + }); +})(); +// @license-end + diff --git a/script/prism.js b/script/prism.js new file mode 100644 index 0000000..fc62daa --- /dev/null +++ b/script/prism.js @@ -0,0 +1,428 @@ +/* http://prismjs.com/download.html?themes=prism */ +self = (typeof window !== 'undefined') + ? window // if in browser + : ( + (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) + ? self // if in worker + : {} // if in node js + ); + +/** + * Prism: Lightweight, robust, elegant syntax highlighting + * MIT license http://www.opensource.org/licenses/mit-license.php/ + * @author Lea Verou http://lea.verou.me + */ + +var Prism = (function(){ + +// Private helper vars +var lang = /\blang(?:uage)?-(?!\*)(\w+)\b/i; + +var _ = self.Prism = { + util: { + encode: function (tokens) { + if (tokens instanceof Token) { + return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias); + } else if (_.util.type(tokens) === 'Array') { + return tokens.map(_.util.encode); + } else { + return tokens.replace(/&/g, '&').replace(/</g, '<').replace(/\u00a0/g, ' '); + } + }, + + type: function (o) { + return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1]; + }, + + // Deep clone a language definition (e.g. to extend it) + clone: function (o) { + var type = _.util.type(o); + + switch (type) { + case 'Object': + var clone = {}; + + for (var key in o) { + if (o.hasOwnProperty(key)) { + clone[key] = _.util.clone(o[key]); + } + } + + return clone; + + case 'Array': + return o.map(function(v) { return _.util.clone(v); }); + } + + return o; + } + }, + + languages: { + extend: function (id, redef) { + var lang = _.util.clone(_.languages[id]); + + for (var key in redef) { + lang[key] = redef[key]; + } + + return lang; + }, + + /** + * Insert a token before another token in a language literal + * As this needs to recreate the object (we cannot actually insert before keys in object literals), + * we cannot just provide an object, we need anobject and a key. + * @param inside The key (or language id) of the parent + * @param before The key to insert before. If not provided, the function appends instead. + * @param insert Object with the key/value pairs to insert + * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted. + */ + insertBefore: function (inside, before, insert, root) { + root = root || _.languages; + var grammar = root[inside]; + + if (arguments.length == 2) { + insert = arguments[1]; + + for (var newToken in insert) { + if (insert.hasOwnProperty(newToken)) { + grammar[newToken] = insert[newToken]; + } + } + + return grammar; + } + + var ret = {}; + + for (var token in grammar) { + + if (grammar.hasOwnProperty(token)) { + + if (token == before) { + + for (var newToken in insert) { + + if (insert.hasOwnProperty(newToken)) { + ret[newToken] = insert[newToken]; + } + } + } + + ret[token] = grammar[token]; + } + } + + // Update references in other language definitions + _.languages.DFS(_.languages, function(key, value) { + if (value === root[inside] && key != inside) { + this[key] = ret; + } + }); + + return root[inside] = ret; + }, + + // Traverse a language definition with Depth First Search + DFS: function(o, callback, type) { + for (var i in o) { + if (o.hasOwnProperty(i)) { + callback.call(o, i, o[i], type || i); + + if (_.util.type(o[i]) === 'Object') { + _.languages.DFS(o[i], callback); + } + else if (_.util.type(o[i]) === 'Array') { + _.languages.DFS(o[i], callback, i); + } + } + } + } + }, + + highlightAll: function(async, callback) { + var elements = document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'); + + for (var i=0, element; element = elements[i++];) { + _.highlightElement(element, async === true, callback); + } + }, + + highlightElement: function(element, async, callback) { + // Find language + var language, grammar, parent = element; + + while (parent && !lang.test(parent.className)) { + parent = parent.parentNode; + } + + if (parent) { + language = (parent.className.match(lang) || [,''])[1]; + grammar = _.languages[language]; + } + + if (!grammar) { + return; + } + + // Set language on the element, if not present + element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language; + + // Set language on the parent, for styling + parent = element.parentNode; + + if (/pre/i.test(parent.nodeName)) { + parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language; + } + + var code = element.textContent; + + if(!code) { + return; + } + + code = code.replace(/^(?:\r?\n|\r)/,''); + + var env = { + element: element, + language: language, + grammar: grammar, + code: code + }; + + _.hooks.run('before-highlight', env); + + if (async && self.Worker) { + var worker = new Worker(_.filename); + + worker.onmessage = function(evt) { + env.highlightedCode = Token.stringify(JSON.parse(evt.data), language); + + _.hooks.run('before-insert', env); + + env.element.innerHTML = env.highlightedCode; + + callback && callback.call(env.element); + _.hooks.run('after-highlight', env); + }; + + worker.postMessage(JSON.stringify({ + language: env.language, + code: env.code + })); + } + else { + env.highlightedCode = _.highlight(env.code, env.grammar, env.language); + + _.hooks.run('before-insert', env); + + env.element.innerHTML = env.highlightedCode; + + callback && callback.call(element); + + _.hooks.run('after-highlight', env); + } + }, + + highlight: function (text, grammar, language) { + var tokens = _.tokenize(text, grammar); + return Token.stringify(_.util.encode(tokens), language); + }, + + tokenize: function(text, grammar, language) { + var Token = _.Token; + + var strarr = [text]; + + var rest = grammar.rest; + + if (rest) { + for (var token in rest) { + grammar[token] = rest[token]; + } + + delete grammar.rest; + } + + tokenloop: for (var token in grammar) { + if(!grammar.hasOwnProperty(token) || !grammar[token]) { + continue; + } + + var patterns = grammar[token]; + patterns = (_.util.type(patterns) === "Array") ? patterns : [patterns]; + + for (var j = 0; j < patterns.length; ++j) { + var pattern = patterns[j], + inside = pattern.inside, + lookbehind = !!pattern.lookbehind, + lookbehindLength = 0, + alias = pattern.alias; + + pattern = pattern.pattern || pattern; + + for (var i=0; i<strarr.length; i++) { // Don’t cache length as it changes during the loop + + var str = strarr[i]; + + if (strarr.length > text.length) { + // Something went terribly wrong, ABORT, ABORT! + break tokenloop; + } + + if (str instanceof Token) { + continue; + } + + pattern.lastIndex = 0; + + var match = pattern.exec(str); + + if (match) { + if(lookbehind) { + lookbehindLength = match[1].length; + } + + var from = match.index - 1 + lookbehindLength, + match = match[0].slice(lookbehindLength), + len = match.length, + to = from + len, + before = str.slice(0, from + 1), + after = str.slice(to + 1); + + var args = [i, 1]; + + if (before) { + args.push(before); + } + + var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias); + + args.push(wrapped); + + if (after) { + args.push(after); + } + + Array.prototype.splice.apply(strarr, args); + } + } + } + } + + return strarr; + }, + + hooks: { + all: {}, + + add: function (name, callback) { + var hooks = _.hooks.all; + + hooks[name] = hooks[name] || []; + + hooks[name].push(callback); + }, + + run: function (name, env) { + var callbacks = _.hooks.all[name]; + + if (!callbacks || !callbacks.length) { + return; + } + + for (var i=0, callback; callback = callbacks[i++];) { + callback(env); + } + } + } +}; + +var Token = _.Token = function(type, content, alias) { + this.type = type; + this.content = content; + this.alias = alias; +}; + +Token.stringify = function(o, language, parent) { + if (typeof o == 'string') { + return o; + } + + if (_.util.type(o) === 'Array') { + return o.map(function(element) { + return Token.stringify(element, language, o); + }).join(''); + } + + var env = { + type: o.type, + content: Token.stringify(o.content, language, parent), + tag: 'span', + classes: ['token', o.type], + attributes: {}, + language: language, + parent: parent + }; + + if (env.type == 'comment') { + env.attributes['spellcheck'] = 'true'; + } + + if (o.alias) { + var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias]; + Array.prototype.push.apply(env.classes, aliases); + } + + _.hooks.run('wrap', env); + + var attributes = ''; + + for (var name in env.attributes) { + attributes += name + '="' + (env.attributes[name] || '') + '"'; + } + + return '<' + env.tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + '</' + env.tag + '>'; + +}; + +if (!self.document) { + if (!self.addEventListener) { + // in Node.js + return self.Prism; + } + // In worker + self.addEventListener('message', function(evt) { + var message = JSON.parse(evt.data), + lang = message.language, + code = message.code; + + self.postMessage(JSON.stringify(_.util.encode(_.tokenize(code, _.languages[lang])))); + self.close(); + }, false); + + return self.Prism; +} + +// Get current script and highlight +var script = document.getElementsByTagName('script'); + +script = script[script.length - 1]; + +if (script) { + _.filename = script.src; + + if (document.addEventListener && !script.hasAttribute('data-manual')) { + document.addEventListener('DOMContentLoaded', _.highlightAll); + } +} + +return self.Prism; + +})(); + +if (typeof module !== 'undefined' && module.exports) { + module.exports = Prism; +} +; diff --git a/script/ousia.js b/script/prism_bash.js index 0ef203a..24d5025 100644 --- a/script/ousia.js +++ b/script/prism_bash.js @@ -7,16 +7,15 @@ * This work is licensed under the X11 (MIT) license. * http://www.opensource.org/licenses/mit-license.php */ + (function () { - "use strict"; - var header = document.querySelector("header"); - window.addEventListener("scroll", function () { - if (window.pageYOffset > 40) { - header.className = "scrolled"; - } else { - header.className = ""; - } - }); +"use strict"; +Prism.languages.bash = { + 'comment': /#.*$/, + 'string': /("|')(\\\n|\\?.)*?\1/, + 'url': /http:\/\/[^\s]*/, + 'keyword': /(^| )(cd|mkdir|git|echo|clone|sudo|yum|apt-get|cmake|make|install)( |$)/ +}; })(); -// @license-end +// @license-end |