118 lines
No EOL
3.9 KiB
JavaScript
118 lines
No EOL
3.9 KiB
JavaScript
// Only install the plugin if Rainbow is present and has been loaded
|
|
if (window.Rainbow) window.Rainbow.linenumbers = (function(Rainbow) {
|
|
/**
|
|
* Splits up a single element into individual lines
|
|
*
|
|
* @param {HTMLElement} elem
|
|
* @returns {Array}
|
|
*/
|
|
function splitElement(elem) {
|
|
if (elem.nodeType === 3) {
|
|
// Just split up the text node
|
|
return elem.nodeValue.split('\n');
|
|
}
|
|
|
|
// Otherwise, we need to split up the HTML
|
|
var sourceLines = elem.innerHTML.split('\n');
|
|
var lines = [];
|
|
|
|
// Wraps each chunk in the parent element. For example:
|
|
// <b>foo\nbar</b> -> [<b>foo</b>, <b>bar</b>]
|
|
for (var i = 0; i < sourceLines.length; i++) {
|
|
// Handles <b>\nbar</b> -> [, <b>bar</b>]
|
|
if (sourceLines[i] === '') {
|
|
lines.push('');
|
|
} else {
|
|
var wrapper = elem.cloneNode(true);
|
|
wrapper.innerHTML = sourceLines[i];
|
|
|
|
var div = document.createElement('div');
|
|
div.appendChild(wrapper.cloneNode(true));
|
|
|
|
lines.push(div.innerHTML);
|
|
}
|
|
}
|
|
|
|
return lines;
|
|
};
|
|
|
|
/**
|
|
* Splits up the element containing highlighted source code
|
|
* into an array of lines
|
|
*
|
|
* @param {HTMLElement} block
|
|
* @returns {Array}
|
|
*/
|
|
function splitLines(block) {
|
|
var lines = [''];
|
|
|
|
for (var i = 0; i < block.childNodes.length; i++) {
|
|
var elemLines = splitElement(block.childNodes[i]);
|
|
|
|
// The first element in elemLines is
|
|
// a continuation of the previous line
|
|
lines[lines.length - 1] += elemLines[0];
|
|
|
|
// The remaining elements get their own lines
|
|
for (var j = 1; j < elemLines.length; j++) {
|
|
lines.push(elemLines[j]);
|
|
}
|
|
}
|
|
|
|
// Returns the array of lines
|
|
return lines;
|
|
};
|
|
|
|
// Callback is called when Rainbow has highlighted a block
|
|
Rainbow.onHighlight(function(block) {
|
|
// This addresses an issue when Rainbow.color() is called multiple times.
|
|
// Since code element is replaced with table element below,
|
|
// second pass of Rainbow.color() will result in block.parentNode being null.
|
|
if (!block || !block.parentNode) {
|
|
return;
|
|
}
|
|
|
|
// Create a table wrapper
|
|
var table = document.createElement('table');
|
|
table.className = 'rainbow';
|
|
table.setAttribute('data-language', block.getAttribute('data-language'));
|
|
|
|
// Split up the lines of the block
|
|
var lines = splitLines(block);
|
|
|
|
// For each line
|
|
for (var i = 0; i < lines.length; i++) {
|
|
var line = lines[i];
|
|
var index = i + 1;
|
|
|
|
// Create a row
|
|
var row = table.insertRow(-1);
|
|
row.className = 'line line-' + index;
|
|
|
|
// Create a cell which displays the line number with CSS
|
|
var lineNumber = row.insertCell(-1);
|
|
lineNumber.className = 'line-number';
|
|
lineNumber.setAttribute('data-line-number', index);
|
|
|
|
// Add in the actual line of source code
|
|
var code = row.insertCell(-1);
|
|
code.className = 'line-code';
|
|
|
|
// If the line is blank, add a newline to make it copyable.
|
|
if (line === '') {
|
|
line = '\n';
|
|
}
|
|
|
|
code.innerHTML = line;
|
|
}
|
|
|
|
// If the block is a <pre> element, its parent is not an element
|
|
// generated by Rainbow (i.e. it could be <body>). We don't want
|
|
// to clear this.
|
|
var parent = (block.nodeName.toLowerCase() === 'pre') ? block : block.parentNode;
|
|
|
|
// Clear the parent element and use the table in place of the <code> block
|
|
parent.innerHTML = '';
|
|
parent.appendChild(table);
|
|
});
|
|
})(window.Rainbow); |