2011-04-28 18:34:19 +00:00
|
|
|
// vim: et:ts=4:sw=4:sts=4:ft=js
|
|
|
|
|
|
|
|
/*@
|
2011-05-08 20:38:51 +00:00
|
|
|
Ox.SyntaxHighlighter <function> Syntax Highlighter
|
|
|
|
(options[, self]) -> <o> Syntax Highlighter
|
|
|
|
options <o> Options
|
2011-05-08 21:06:48 +00:00
|
|
|
lineLength <n|0> If larger than zero, show edge of page
|
2011-05-08 20:38:51 +00:00
|
|
|
offset <n|1> First line number
|
2011-05-08 21:14:56 +00:00
|
|
|
showLinebreaks <b|false> If true, show linebreaks
|
2011-05-08 20:38:51 +00:00
|
|
|
showLineNumbers <b|false> If true, show line numbers
|
2011-05-08 21:14:56 +00:00
|
|
|
showWhitespace <b|false> If true, show whitespace
|
2011-05-08 21:06:48 +00:00
|
|
|
showTabs <b|false> If true, show tabs
|
|
|
|
source <s|''> JavaScript source
|
2011-05-08 20:38:51 +00:00
|
|
|
stripComments <b|false> If true, strip comments
|
2011-05-08 21:06:48 +00:00
|
|
|
tabSize <n|4> Number of spaces per tab
|
2011-05-08 21:14:56 +00:00
|
|
|
self <o> Shared private variable
|
2011-04-28 18:34:19 +00:00
|
|
|
@*/
|
|
|
|
|
|
|
|
Ox.SyntaxHighlighter = function(options, self) {
|
|
|
|
|
2011-05-08 20:38:51 +00:00
|
|
|
self = self || {};
|
2011-05-08 18:22:43 +00:00
|
|
|
var that = Ox.Element({}, self)
|
2011-04-28 18:34:19 +00:00
|
|
|
.defaults({
|
2011-05-08 21:06:48 +00:00
|
|
|
lineLength: 0,
|
|
|
|
offset: 1,
|
2011-05-08 21:14:56 +00:00
|
|
|
showLinebreaks: false,
|
2011-05-08 18:22:43 +00:00
|
|
|
showLineNumbers: false,
|
2011-05-08 21:06:48 +00:00
|
|
|
showTabs: false,
|
2011-05-08 21:14:56 +00:00
|
|
|
showWhitespace: false,
|
2011-05-08 21:06:48 +00:00
|
|
|
source: '',
|
|
|
|
stripComments: false,
|
|
|
|
tabSize: 4,
|
2011-04-28 18:34:19 +00:00
|
|
|
})
|
|
|
|
.options(options || {})
|
|
|
|
.addClass('OxSyntaxHighlighter');
|
|
|
|
|
2011-05-08 18:22:43 +00:00
|
|
|
renderSource();
|
2011-04-28 18:34:19 +00:00
|
|
|
|
2011-05-08 18:22:43 +00:00
|
|
|
function renderSource() {
|
2011-05-08 21:06:48 +00:00
|
|
|
var $lineNumbers, $line, $source, width,
|
|
|
|
lines, source = '', tokens,
|
2011-05-08 20:38:51 +00:00
|
|
|
linebreak = (
|
2011-05-08 21:14:56 +00:00
|
|
|
self.options.showLinebreaks ?
|
2011-05-08 20:38:51 +00:00
|
|
|
'<span class="OxLinebreak">\u21A9</span>' : ''
|
|
|
|
) + '<br/>',
|
|
|
|
tab = (
|
|
|
|
self.options.showTabs ?
|
|
|
|
'<span class="OxTab">\u2192</span>' : ''
|
2011-05-08 21:06:48 +00:00
|
|
|
) + Ox.repeat(' ', self.options.tabSize - self.options.showTabs),
|
2011-05-08 21:14:56 +00:00
|
|
|
whitespace = self.options.showWhitespace ? '\u00B7' : ' ';
|
2011-05-08 18:22:43 +00:00
|
|
|
self.options.source = self.options.source
|
|
|
|
.replace(/\r\n/g, '\n')
|
|
|
|
.replace(/\r/g, '\n');
|
2011-05-08 20:38:51 +00:00
|
|
|
tokens = Ox.tokenize(self.options.source);
|
|
|
|
tokens.forEach(function(token, i) {
|
2011-05-08 18:22:43 +00:00
|
|
|
var classNames,
|
2011-05-08 20:38:51 +00:00
|
|
|
substr = self.options.source.substr(token.offset, token.length);
|
2011-05-08 18:22:43 +00:00
|
|
|
if (
|
|
|
|
!(self.options.stripComments && token.type == 'comment')
|
|
|
|
) {
|
|
|
|
classNames = 'Ox' + Ox.toTitleCase(token.type);
|
2011-05-08 21:14:56 +00:00
|
|
|
if (self.options.showWhitespace && token.type == 'whitespace') {
|
2011-05-08 18:22:43 +00:00
|
|
|
if (isAfterLinebreak() && hasIrregularSpaces()) {
|
|
|
|
classNames += ' OxLeading'
|
|
|
|
} else if (isBeforeLinebreak()) {
|
|
|
|
classNames += ' OxTrailing'
|
|
|
|
}
|
|
|
|
}
|
2011-05-08 20:38:51 +00:00
|
|
|
source += '<span class="' + classNames + '">' +
|
|
|
|
Ox.encodeHTML(substr)
|
|
|
|
.replace(/ /g, whitespace)
|
|
|
|
.replace(/\t/g, tab)
|
|
|
|
.replace(/\n/g, linebreak) + '</span>';
|
2011-05-08 18:22:43 +00:00
|
|
|
}
|
|
|
|
function isAfterLinebreak() {
|
|
|
|
return i == 0 ||
|
2011-05-08 20:38:51 +00:00
|
|
|
tokens[i - 1].type == 'linebreak';
|
2011-05-08 18:22:43 +00:00
|
|
|
}
|
|
|
|
function isBeforeLinebreak() {
|
2011-05-08 20:38:51 +00:00
|
|
|
return i == tokens.length - 1 ||
|
|
|
|
tokens[i + 1].type == 'linebreak';
|
2011-05-08 18:22:43 +00:00
|
|
|
}
|
|
|
|
function hasIrregularSpaces() {
|
2011-05-08 20:38:51 +00:00
|
|
|
return substr.split('').reduce(function(prev, curr) {
|
2011-05-08 18:22:43 +00:00
|
|
|
return prev + (curr == ' ' ? 1 : 0);
|
2011-05-08 21:06:48 +00:00
|
|
|
}, 0) % self.options.tabSize;
|
2011-05-08 18:22:43 +00:00
|
|
|
}
|
|
|
|
});
|
2011-05-08 20:38:51 +00:00
|
|
|
lines = source.split('<br/>');
|
2011-05-08 18:22:43 +00:00
|
|
|
that.empty();
|
2011-05-08 20:38:51 +00:00
|
|
|
if (self.options.showLineNumbers) {
|
2011-05-08 21:06:48 +00:00
|
|
|
$lineNumbers = new Ox.Element()
|
2011-05-08 20:38:51 +00:00
|
|
|
.addClass('OxLineNumbers')
|
|
|
|
.html(
|
|
|
|
Ox.range(lines.length).map(function(line) {
|
|
|
|
return (line + self.options.offset);
|
|
|
|
}).join('<br/>')
|
|
|
|
)
|
|
|
|
.appendTo(that);
|
2011-05-08 18:22:43 +00:00
|
|
|
}
|
2011-05-08 21:06:48 +00:00
|
|
|
$source = new Ox.Element()
|
2011-05-08 18:22:43 +00:00
|
|
|
.addClass('OxSourceCode')
|
2011-05-08 20:38:51 +00:00
|
|
|
.html(source)
|
2011-05-08 18:22:43 +00:00
|
|
|
.appendTo(that);
|
2011-05-08 21:06:48 +00:00
|
|
|
if (self.options.lineLength) {
|
|
|
|
$line = new Ox.Element()
|
|
|
|
.css({
|
|
|
|
position: 'absolute',
|
|
|
|
top: '-1000px'
|
|
|
|
})
|
|
|
|
.html(Ox.repeat(' ', self.options.lineLength))
|
|
|
|
.appendTo(that),
|
|
|
|
width = $line.width() + 4; // add padding
|
|
|
|
$line.removeElement();
|
|
|
|
['moz', 'webkit'].forEach(function(browser) {
|
|
|
|
$source.css({
|
|
|
|
background: '-' + browser +
|
|
|
|
'-linear-gradient(left, rgb(255, 255, 255) ' +
|
|
|
|
width + 'px, rgb(192, 192, 192) ' + width +
|
|
|
|
'px, rgb(255, 255, 255) ' + (width + 1) + 'px)'
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2011-05-08 18:22:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.setOption = function(key, value) {
|
|
|
|
renderSource();
|
2011-04-28 18:34:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return that;
|
|
|
|
|
|
|
|
};
|