基于正则表达式的JavaScript/C++语法高亮(js版)
2011-10-26 13:39
387 查看
JavaScript的语法高亮一直是个难点,因为需要语义分析以确定/是除法操作的开始还是正则表达式的开始。目前关于高亮js的语法高亮都不是很理想,尤其是syntaxhighlighter(你待会看到的代码高亮效果就是它弄的, csdn说, 就用sh了)的效果算是最差的了,根本就不高亮正则、数字等,太不敬业了。假设你写的代码是足够规范的,而且不考虑效率的话,那么你使用我这个高亮js一般不会有问题。
语法高亮最初我是想做但感觉很难,但前阵子偶然看到一个很牛的女程序员Ider(博客地址Ider),绝对很牛,写了一个C++的语法高亮Regular Expression for C++ Code Syntax Coloring
(Version 1.0),我按耐不住了,决心也要写一个。
关于更多的高亮javascript的,请访问Ben Joffe(悉尼的绝绝对对大牛)的JavaScript Syntax Highlighter,这个估计是目前最好的高亮js的,当然最好的高亮js的应该属aptana和netbeans了(但它们都不适用于网页)
以下是一个Hack SH的js高亮示例:
效果图:
js的
C++的
以下是源码(前面说过SH高亮很烂,所以这儿没用高亮,把下面的源码复制到文本编辑器中然后保存为utf8编码的html后用浏览器(推荐opera或chrome浏览器)可以打开测试了,当然不用utf8也不要紧,我没有使用utf8字符,呵呵),你需要一定的正则表达式基础来读懂以下源码,希望哪天我能有时间解释下。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Highlight Code Version 2.0(2011-10-20)</title>
<style type="text/css">
* { padding: 0; margin: 0; }
#main { width: 800px; margin: auto; }
#code { font-size: 0.8em; padding: 0.2em 0; }
#codesettings { border: 1px dotted #888; margin: 0.4em 0; padding: 0.1em 0; }
#codesettings label { display: block; text-indent: 0.2em; }
#codesettings input { width: 2em; }
#settingstitle { font-size: 1.1em; font-weight: bold; }
#code textarea { width: 100%; height: 200px; }
#presentation { line-height: 1.2em; font-size: 0.8em; font-family:monospace; width: 800px; } /* fix IE6, making lineno and code of the same height */
#lineno { float: left; width: 60px; text-align: right; color: #990033; background-color: #cccccc; }
#highlightcode { float: left; width: 734px; margin-left: 6px; white-space: nowrap; overflow-x: auto; }
/* custom code style class defined here */
/* add each language style className with the RELATED option value as prefix in [Select a Programing Language] select list */
/* for C/C++ */
.cppstring { color:#ff0000; }
.cppcomment { color:#008800; }
.cppcharacter { color:#ff00cc; }
.cpppreprocessor { color:#006666; }
.cppidentifier { color:#0000ff; }
.cppkeyword { color:#ff3300; font-weight:bold; }
.cppnumber { color:#990099; }
.cppoperator { color:#000066; }
.cppuserhighlight { color:#0099ff; }
/* for JavaScript */
.jsstring_dq { color:#ff0000; }
.jscomment { color:#008800; }
.jsstring_sq { color:#ff00cc; }
.jsidentifier { color:#0000ff; }
.jskeyword { color:#ff3300; font-weight:bold; }
.jsnumber { color:#990099; }
.jsoperator { color:#000066; }
.jsuserhighlight { color:#0099ff; }
.jsregexp { color: #fe5677; }
.jsloprand { color: #000000; }
/* TODO: other language css style adds here */
</style>
</head>
<body>
<div id="main">
<br />
<div style="font-size:0.9em;"><strong><em>A simple highlight (JavaScript/C/C++) code parser based on regular expression, works fine with IE6 and the latest FF/Chrome/Opera(<span style="color:green; font-size:0.9em;">have a small align bug in Safari between
lineno and highlight code</span>), hope you can enjoy, designed by Wind Fantasy © 2011-10-20</em></strong></div>
<div style="overflow-x: hidden;"> ----------------------------------------------------------------------------------------------------------------------------- </div>
<div style="color: #008800; font-size: 0.8em;">Version 2.0: Now I have partly fixed the <em>JavaScript Highlight bug</em> by supposing that '/' stands for division only if '/' follows <strong style="color: #ff0000;">({number}|{identifier}|[\)\]}])[ \t]*</strong>
directly, as we know that '/' needs a left operand for division</div>
<div style="font-size: 0.8em;">Please visit <a href="http://www.benjoffe.com/code/projects/syntax/" target="_blank">JavaScript Syntax Highlighter</a> for more information about JavaScript Highlight.</div>
<br />
<div id="code">
<div id="action">
<button id="hidecodesettings" type="button">Hide Code Settings</button>
<button id="highlight" type="button">Highlight Textarea Code</button>
<button id="testjsself" type="button">Highlight JavaScript Of Current Document</button>
</div>
<form autocomplete="off">
<div id="codesettings">
<div id="settingstitle">Code Settings</div>
<label>Select a Programing Language:
<select id="language">
<option value="cpp">C/C++</option>
<option value="js">JavaScript</option>
</select>
</label>
<label>Select Formatting Method:
<select id="formattingmethod">
<option value="pre">Use Pre Tag (Fast)</option>
<option value="entity">Use HTML Entity (Slow)</option>
</select>
</label>
<label><em>(Depreciated)</em> Highlight With Line Number:
<input type="checkbox" checked="checked" id="withlineno" disabled="disabled" />
</label>
<label><em>(Depreciated)</em> Tab Width(works only when <strong>[Select Formatting Method] > Use HTML Entity</strong> is Selected):
<input type="text" id="tabwidth" value="4" maxlength="1" />
</label>
<!--
<label>Line Number Min Width(works only when <strong>[Highlight With Line Number]</strong> is Checked):
<input type="text" id="linenominwidth" value="4" />
</label>
-->
<label>
<button id="submitcodesettings" type="button">Save Code Settings & Restart to Hightlight</button>
</label>
</div>
<div style="padding: 3px;">Time used: <span id="time" style="color: #FF0000;">0.000</span> s <span style="color: #008800;">(I think the latest opera browser is the fastest)</span></div>
<div id="codetext">
<textarea id="sourcecode">
// input some codes here to test, please help yourself
#include<cstdio> // work as well
#include <iostream>
#include <string>
using namespace std;
/*
swap int x and int y quickly with ^
we expect x, y is an int variable, must NOT be (const) expression
such as swap(1,y) OR swap(a+b, c), the same as swap_slow
*/
#define swap(x,y) \
x ^= y; \
y ^= x; \
x ^= y;
/*
swap int x and int slowly with temp
*/
#define swap_slow(x,y) \
int temp = x; \
y = x; \
x = temp;
int main() {
string s = "uvwxyz"; // nomal C++ string
/**
line-continuing string
*/
char[] lcs = "ABC\x44D\
EF\
G\
";
/**
special line-continuing string
*/
char[] slcs = "abc\\\"\\\
continue\
end";
/**
invalid line-continuing string 1
no line-continuing character (pattern) /\\(?:\r?\n)/ to be found
*/
char[] ilcs1 = "abc\\\"\\
invalidremain\
invalidend";
/**
invalid line-continuing string 2
*/
char[] ilcs2 = "abc\"\
def\
badend // no quote enclosed to be found
char c = '\x41'; // character 'A'
// line-continuing character
char lcc = '\
\
\x\
42'; // character 'B'
// no enclose double quote " string
char* partly_s = "abcdefg
// no enclose single quote ' character
char partly_c = 'z
float f = 1.2f;
double d = 3.// 3.0
double d2 = .3// 0.3
double e = .1234e-3L; // long double
unsigned int ui = 5678u;
long int li = 9l; // not 91
int i = 100;
double cast = 12345678f; // int to float, float to double
long int lhex = 0x12aBL;
unsigned int uhex = 0XFf23u;
int hex = 0xcD89;
int invalid = 0xZ123; // invalid hex number
cout << s << endl;
return 0;
}
</textarea>
</div>
</form>
</div>
<div id="presentation">
<div id="lineno"></div>
<div id="highlightcode"></div>
</div>
</div>
<script id="jsself" type="text/javascript">
// new bugs: (These are even difficult to be fixed because we need further analysising.)
var hacknum = 0;
{} /re/.test('re'); // {} becomes an empty statement so that /re/ should be highlighted as a R.E.
hacknum++ / 1 / 2; // division not R.E
/[/]/.test('/'); // non-escaped '/' in a character set of R.E. is interpreted as the end of R.E. unfortunately
hacknum = 1
/ // single-line comment does NOT work
0.5
/ 2; // the first '/' is NOT a delimiter of R.E. as you know that hacknum is 1 later;
var hacknull = null / 2; // division not R.E.
// !spam, partly fixed Version 1.0. Oh, JavaScript is NOT that easy!
// the big problem in Highlight JavaScript Code:
// code below is test for hacking, division symbol '/' is treated as a delimiter of the beginning of regular expression
// so far I have NOT (found) any way based on regular expression determining whether '/' stands for division(operator) or not(that is, for delimiter)
// sometimes we need Syntax Analysis, or even Semantic Analysis
// be careful with the code (being or like) below, which will cause confusing representation of the code according to its real meaning
// if we don't analysis R.E.(short for regular expression), quote(s) in R.E. will cause error when try to highlight/analysis quoted string
// else(that is we analysis R.E.) we don't know whether '/' stands for division(operator) or not
// [1]. I have NOT fixed part R.E.(fixed string as you can test, that is, a string defined like 'var s="abc', no enclose ", will be highlighted as string)
// [2]. I always suppose that your js code is clean and well-orgnized and you hardly use '/' as division twice or more OR mixed R.E. and division in the same line
// [3]. I always suppose your js code to be block not inline, you know(that is, multiline, so that an error ruins at most ONE line)
// [4]. The most important is that I suppose you DO according to the three rules above
var hacknum = 1/2, hackre = /"test/, hackcomment = "abc"; // this SHOULD have been single-line comment, but it is NOT as you see
// spam!
var HighlightParser =(function () {
// if you want to use jQuery $, please define a variable alias of $ by assignment, for example: var alias_$ = $, OR use jQuery itself
var $ = { // create a container so that we can access the previous attributes or methods next step by $.attributename or $.methodname
'library': {
// PHP-Partly-Compatible htmlspecialchars and htmlspecialchars_decode
'htmlspecialchars': function (str, encode_sq) { // make html special chars their entities, bool encode_sq means whether we wanna convert single quote to its entity
var ent = { '>': '>', '<': '<', '&': '&', '"': '"' };
ent["'"] = encode_sq ? ''' : "'";
return str.replace(/[\<\>&"']/g, function () { return ent[arguments[0]]; } );
},
'htmlspecialchars_decode': function (str, decode_sq) { // decode of htmlspecialchars
var ent_r = { '>': '>', '<': '<', '&': '&', '"': '"'};
ent_r['''] = decode_sq ? "'" : ''';
return str.replace(/&(?:gt|lt|quot|#039|amp);/g, function () { return ent_r[arguments[0]]; });
},
/*
'htmlspace': function (str) {
var ent = { '\n': '<br \/>', '\r\n': '<br \/>', '\t': $.config.tabreplacement };
//alert('sp:' + str + '*')
return str.replace(/\r?\n|\t|(\s)/g, function () { var $ = arguments; /*alert('1' + $[1] + '1'); if(! prompt('', false)) throw 'hi';* / return $[1] ? ' ' : ent[$[0]]; });
},
*/
'htmlspace': function (str) { // make html spaces(\t \n) its real representation in text mode
var ent = { '\t': $.config.tabreplacement, ' ': ' '}
return str.replace(/[\t ]/g, function () { return ent[arguments[0]]; });
},
'htmlbreakline': function (str, tagname) { // make html break-line character its real representation in text mode
if (! tagname) { // tagname means using tagname instead of tag br to render representation, possiablely use div, but div is block default while span is inline(this will cause problem)
return str.replace(/\r?\n/g, '<br \/>\r\n');
}
var bothtag = [ ['<', '>'].join(tagname), ['</', '>'].join(tagname)];
// return str.replace(/^.*$/mg, function () { return bothtag.join(arguments[0]); });
return str.replace(/(?:^|\r?\n)([^\r\n]*)/g, function () {
var $1 = String(arguments[1]);
//alert($1 + '*' + $1.charCodeAt(0) + '.' + $1.charCodeAt(1));
//alert(bothtag.join(arguments[1] || 'nbsp;'))
return bothtag.join(arguments[1] || ' '); });
},
'htmlpre': function (str, encode_sq) { // make representation of str in html mode the same as pre mode
//alert('pre:' + str + '*')
return $.library.htmlspace( $.library.htmlspecialchars(str, encode_sq) );
},
'objecttoarray': function (obj, includeprototype) { // reuturn an array of each propery VALUE of obj
var a = [];
for (var prop in obj) {
if (includeprototype || obj.hasOwnProperty(prop))
a.push(obj[prop]);
}
return a;
},
'propertytoarray': function (obj, includeprototype) { // return an array of each propery NAME of obj
var a = [];
for (var prop in obj) {
if (includeprototype || obj.hasOwnProperty(prop))
a.push(prop);
}
return a;
},
'repeat': function (repeat, times) { // return a string when variable "repeat" repeats "times" times
return new Array(times + 1).join(repeat);
}
},
'config': {
'language': 'js', // [current/selected] programing language in ['cpp', 'js', ... etc.]
'formattingmethod': 0 ? 'pre' : 'entity', // formatingmethod in ['pre', 'entity'] for [pre tag method(Fast), HTML entity method(Slow)]
'withlineno': false, // highlight with line number
'tabwidth': 4, // works only when formattingmethod is 'entity'
//'linenominwidth': 4, // works only when withlineno is true
'tabreplacement': new Array(4 + 1).join(' '), // but $.library.repeat(' ', $.config.tabwidth) doesn't work // replacement for tab
'updateTabwidth': function (tabwidth) {
$.config.tabwidth = tabwidth;
$.config.tabreplacement = new Array(tabwidth + 1).join(' ');
}
},
'regexp': { // core regular expressions for parser
'cpp': {
// both of 'string'(comment and uncomment) regexp below take the same effect
//'string': '"(?:\\\\(?:\\\\|"|\\r?\\n)|[^"\\n])*"?', // first is ", /"(?:\\(?:\\|"|\r?\n)|[^"\n])*"?/
'string': '"(?:\\\\(?:\\r?\\n|.)|[^"\\n\\\\])*"?', // first is ", /"(?:\\(?:.|\r?\n)|[^"\n\\])*"?/
'character': "'(?:\\\\(?:\\\\|'|\\r?\\n)|[^'\\n])*'?", // first is ', /'(?:\\(?:\\|'|\r?\n)|[^\n])*'?/
'preprocessor': '(?:^|\\r?\\n)[^\\S\\n]*#[^\\S\\n]*[a-zA-Z]+', // first is #, /(?:^|\r?\n)[^\S\n]*#[^\S\n]*[a-zA-Z]+/
'keyword': '(?:asm|auto|bad_cast|bad_typeid|bool|break|case|catch|char|class|const|const_cast|continue|default|delete|do|double|dynamic_cast|else|enum|except|explicit|extern|false|finally|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_cast|struct|switch|template|this|throw|true|try|type_info|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)(?=\\W|$)',
'userhighlight': '(?:assert|cout|cin|endl|std|string)(?=\\W|$)',
'identifier': '[^\\W\\d]\\w*', // first is [^\W\d], /[^\W\d]\w*/
//'number': '(?:0[xX][\\da-fA-F]+|\\d+)(?:[Ll]?[Uu]?|[Uu]?[Ll]?)|(?:\\d+\\.?\\d*|\\d*\\.?\\d+)(?:[Ee][+-]?\\d+)?[LlFf]?', // first is [\d\.], /(?:0[xX][\da-fA-F]+|\d+)(?:[Ll]?[Uu]?|[Uu]?[Ll]?)|(?:\d+\.?\d*|\d*\.?\d+)(?:[Ee][+-]?\d+)?[LlFf]?/
'number': '0[xX][\\da-fA-F]+(?:[Ll]?[Uu]?|[Uu]?[Ll]?)|\\d+(?:[Ll][Uu]?|[Uu][Ll]?)|(?:\\d+\\.?\\d*|\\d*\\.?\\d+)(?:[Ee][+-]?\\d+)?[LlFf]?', // first is [\d\.], /0[xX][\da-fA-F]+(?:[Ll]?[Uu]?|[Uu]?[Ll]?)|\d+(?:[Ll][Uu]?|[Uu][Ll]?)|(?:\d+\.?\d*|\d*\.?\d+)(?:[Ee][+-]?\d+)?[LlFf]?/
//'comment': '//.*|/\\*(?:[^*]|\\*[^\\/])*(?:\\*/)?', // first is /, expect * or / to be followed, /\/\/.*|\/\*(?:[^*]|\*[^\/])*(?:*/)?/
'comment': '//.*|/\\*(?:[^*]|\\*(?!\\/))*(?:\\*/)?', // first is /, expect * or / to be followed, /\/\/.*|\/\*(?:[^*]|\*(?!\/))*(?:\*\/)?/
'operator': '(?:[-!+*/%&^|=><]|>>|<<)=?|[?:]', // /(?:[-!+*/%&^|=><]|>>|<<)=?|[?:]/
'space': '[^\\S\\n]+' // first is [^\\S\\n], /[^\\S\\n]+/
},
'js': {
'string_dq': '"(?:\\\\(?:\\\\|"|\\r?\\n)|[^"\\n])*"?', // first is ", /"(?:\\(?:\\|"|\r?\n)|[^"\n])*"?/, $1
'string_sq': "'(?:\\\\(?:\\\\|'|\\r?\\n)|[^'\\n])*'?", // first is ', /'(?:\\(?:\\|'|\r?\n)|[^\n])*'?/, $2
'keyword': '(?:abstract|boolean|byte|char|class|const|debugger|double|enum|export|extends|final|float|goto|implements|import|int|interface|long|native|package|private|protected|public|short|static|super|synchronized|throws|transient|volatile|break|case|continue|default|do|else|false|function|if|in|instanceof|null|new|return|switch|this|true|typeof|undefined|var|void|while)(?=\\W|$)',
'userhighlight': '(?:Array|Bool|Date|Function|Global|Math|NaN|Number|Object|RegExp|String|apply|call|constructor|document|window)(?=\\W|$)',
'identifier': '[$_a-zA-Z][\\w\\$]*', // first is [$_a-zA-Z], /[$_a-zA-Z][\w\$]*/, $5
'number': '0[xX][\\da-fA-F]+|(?:\\d+\\.?\\d*|\\d*\\.?\\d+)(?:[Ee][+-]?\\d+)?', // first is [\d.], /0[xX][\da-fA-F]+|(?:\d+\.?\d*|\d*\.?\d+)(?:[Ee][+-]?\d+)?/, $6
//'comment': '//.*|/\\*(?:[^*]|\\*[^\\/])*(?:\\*/)?', // first is /, expect * or / to be followed, /\/\/.*|\/\*(?:[^*]|\*[^\/])*(?:*/)?/
'comment': '//.*|/\\*(?:[^*]|\\*(?!\\/))*(?:\\*/)?', // first is /, expect * or / to be followed, /\/\/.*|\/\*(?:[^*]|\*(?!\/))*(?:\*\/)?/
//'loprand': '\\)\\s*/(?![*/])', // first is ), /\)\s*\/(?![*\/])/
'loprand': '(?:[\\])]\\s*|\\}[ \\t]*)/(?![*/])', // first is )]}, /(?:[\])]\s*|\}[ \t]*)\/(?![*\/])/
//'regexp': '/(?:\\\\/|[^/\\n])*/?[gmi]*', // first is /, /\/(?:[^\/\n]|\\\/)+)\//[gmi]*
//'regexp': '/(?:\\\\[/\\\\]|[^/\\n])+/[gmi]*[^\\S\\n]*', // first is /, /\/(?:\\[\/\\]|[^\/\n])+\/[gmi]*[^\S\n]*(?![\w])/
//'regexp': '/(?:\\\\[\\/\\\\]|[^/\\n\\\\])+/[gmi]*', // first is /, /\/(?:\\[\/\\]|[^/\n\\])+\/[gmi]*/
'regexp': '/(?:\\\\.|[^\\\\\\n/])+/[gmi]*', // first is /, /\/(?:\\.|[^\\\n\/])+\/[gmi]*/, $9
'operator': '(?:[-!+*/%&^|=><]|>>|<<)=?|=|[?:]', // /(?:[-!+*/%&^|=><]|>>|<<)=?|[?:]/
'space': '\\s+' // first is \s, /\s+/
},
'html': { // to be implemented
'tag': '<([a-zA-Z])(\\s>)*>.' // /<([a-zA-Z]+)((?:\s+[-\w]+=(?:'[^']*'|"[^"]*"))*)\s*>(.*)(?:<\/\1>)?/
}
},
'getCompiledPattern': function(language) {
var pattern = '(' + $.library.objecttoarray($.regexp[language]).join(')|(') + ')';
return new RegExp(pattern, "g");
},
'getCurrentPattern': function () {
return $.getCompiledPattern($.config.language);
},
'parse': function (sourcecode) {
// first we convert windows break line "\r\n" to unix/linux "\n" to fix opera/safari bug
sourcecode = sourcecode.replace(/\r(?=\n)/g, ''); // fix opera/safari(Opera/Safari will treat '\r' as the same representation of '\n')
var language = $.config.language, classNames = $.library.propertytoarray($.regexp[language]);
var premethod = ($.config.formattingmethod == 'pre');
var htmlformater = premethod ? $.library.htmlspecialchars
: function (str, encode_sq) { return $.library.htmlspace($.library.htmlspecialchars(str, encode_sq));};
if (language == 'js') {
var pattern = new RegExp( '(' + $.library.objecttoarray($.regexp[language]).join(')|(') + ')' );
var isdivision = false, s = [], turnback;
var divisionflag = [null, true, true, false, false, true, true]; // only 1, 2, 5, 6 subpattern can be placed before division
var next = sourcecode, matches, i, len, highlightcode, htmlspace = function (str) { return premethod ? str : $.library.htmlspace(str); };
while ( (matches = pattern.exec(next)) != null) {
for (i = 1, len = matches.length; i < len; ++i) {
if (matches[i]) {
if (i == 9 && isdivision && matches.index == 0) { // when try to match regexp, but '/' turns out to be division, sorry we should turn back
s.push([htmlformater(next.substr(0, matches.index), true), '<span class="jsoperator"\>/\<\/sapn\>'].join(''));
turnback = true;
next = next.substr(matches.index + 1);
} else if (i == len - 1) { // spaces?
s.push ( htmlformater(next.substr(0, matches.index + matches[i].length), true) );
} else if (i == 8) { // loperand before '/'? i.e. '(function () { return 1; })() / 2', matches[0] is ') /'
s.push ( [htmlformater(next.substr(0, matches.index), true), '<span class="js', classNames[i-1], '"\>', htmlspace(matches[i]).replace('/', '\<span class="jsoperator"\>/\<\/sapn\>'), '\<\/span\>'].join('') );
} else {
s.push ( [htmlformater(next.substr(0, matches.index), true), '<span class="js', classNames[i-1], '"\>', htmlformater(matches[i], true), '\<\/span\>'].join('') );
}
if (i == len - 1) {
isdivision = (matches.index == 0 && matches[0].indexOf('\n') == -1) ? isdivision : false;
} else {
isdivision = Boolean(divisionflag[i]);
}
break;
}
}
if (! turnback) {
next = next.substr(matches.index + matches[0].length);
} else {
turnback = false;
}
}
s.push(next);
highlightcode = s.join('');
} else {
var replace_callback = function () {
var matches = arguments;
var reallen = matches.length - 3; // strip last spaces, position where pattern starts to match and the original string to be replaced
for (var i=1; i < reallen; ++i) {
if(matches[i]) {
//alert(['*', matches[i], '*', matches[i].length, '*'].join(''));
return ['<span class="', language, classNames[i-1], '">', htmlformater(matches[i], true), '<\/span\>'].join('');
break;
}
}
return htmlformater(matches[0], true);
}
highlightcode = sourcecode.replace($.getCurrentPattern(), replace_callback);
}
return premethod ? ['<pre>\r\n', '</pre>'].join(highlightcode) : $.library.htmlbreakline(highlightcode, false);
},
'getLineNoHTML': function (sourcecode) {
var linecount = sourcecode.split('\n').length, w = String(linecount).length;
var repeat = $.library.repeat, s, a = [];
for (var i=1; i <= linecount; ++i) {
s = String(i);
a.push( [repeat('0', w - s.length), s, ' '].join('') ); // possiblely use instead of 0
}
if($.config.formattingmethod == 'pre') {
return ['<pre\>', a.join("\r\n"), '\r\n<\/pre>'].join('');
}
return a.join("\r\n<br \/>");
}
};
return $;
})();
function g(id) {
return document.getElementById(id);
}
window.onload = function () {
var elineno = g('lineno');
var ehighlightcode = g('highlightcode');
var esourcecode = g('sourcecode');
var elanguage = g('language');
var ewithlineno = g('withlineno');
var etabwidth = g('tabwidth');
var ecodesettings = g('codesettings');
var eformattingmethod = g('formattingmethod');
var time = g('time');
var cfg = HighlightParser.config;
g('hidecodesettings').onclick = function () {
var style = ecodesettings.style;
if (style.display == 'none') {
style.display = 'block';
this.innerHTML = 'Hide Code Settings';
} else {
style.display = 'none';
this.innerHTML = "Display Code Settings";
}
}
var highlight = function () {
var t = new Date().getTime();
elineno.innerHTML = HighlightParser.getLineNoHTML(esourcecode.value);
ehighlightcode.innerHTML = HighlightParser.parse(esourcecode.value);
time.innerHTML = String( (new Date().getTime() - t) / 1000 );
if (window.ActiveXObject) { // fix ie6
ehighlightcode.style.height = elineno.clientHeight + 20 + 'px';
}
}
var reloadconfig = function () {
cfg.language = elanguage.value;
cfg.withlineno = ewithlineno.checked;
cfg.formattingmethod = eformattingmethod.value;
cfg.updateTabwidth(parseInt(etabwidth.value, 10));
}
g('submitcodesettings').onclick = function () {
reloadconfig();
highlight();
}
g('highlight').onclick = function () {
highlight();
}
g('testjsself').onclick = function () {
esourcecode.value = g('jsself').innerHTML;
/* There is a small difference(innerHTML of html script node) between w3c-compatible browser and IE6, here you can test
var v = esourcecode.value, a = [0, 1, 2, 3], s = [];
for (var i in a) {
s.push(v.charCodeAt(a[i]));
}
alert(s);
*/
elanguage.selectedIndex = 1;
reloadconfig();
highlight();
}
reloadconfig();
highlight();
}
</script>
</body>
</html>
语法高亮最初我是想做但感觉很难,但前阵子偶然看到一个很牛的女程序员Ider(博客地址Ider),绝对很牛,写了一个C++的语法高亮Regular Expression for C++ Code Syntax Coloring
(Version 1.0),我按耐不住了,决心也要写一个。
关于更多的高亮javascript的,请访问Ben Joffe(悉尼的绝绝对对大牛)的JavaScript Syntax Highlighter,这个估计是目前最好的高亮js的,当然最好的高亮js的应该属aptana和netbeans了(但它们都不适用于网页)
以下是一个Hack SH的js高亮示例:
var re = /'abc/g, str = 'str'; // I can't imagine this will DO highlight well
效果图:
js的
C++的
以下是源码(前面说过SH高亮很烂,所以这儿没用高亮,把下面的源码复制到文本编辑器中然后保存为utf8编码的html后用浏览器(推荐opera或chrome浏览器)可以打开测试了,当然不用utf8也不要紧,我没有使用utf8字符,呵呵),你需要一定的正则表达式基础来读懂以下源码,希望哪天我能有时间解释下。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Highlight Code Version 2.0(2011-10-20)</title>
<style type="text/css">
* { padding: 0; margin: 0; }
#main { width: 800px; margin: auto; }
#code { font-size: 0.8em; padding: 0.2em 0; }
#codesettings { border: 1px dotted #888; margin: 0.4em 0; padding: 0.1em 0; }
#codesettings label { display: block; text-indent: 0.2em; }
#codesettings input { width: 2em; }
#settingstitle { font-size: 1.1em; font-weight: bold; }
#code textarea { width: 100%; height: 200px; }
#presentation { line-height: 1.2em; font-size: 0.8em; font-family:monospace; width: 800px; } /* fix IE6, making lineno and code of the same height */
#lineno { float: left; width: 60px; text-align: right; color: #990033; background-color: #cccccc; }
#highlightcode { float: left; width: 734px; margin-left: 6px; white-space: nowrap; overflow-x: auto; }
/* custom code style class defined here */
/* add each language style className with the RELATED option value as prefix in [Select a Programing Language] select list */
/* for C/C++ */
.cppstring { color:#ff0000; }
.cppcomment { color:#008800; }
.cppcharacter { color:#ff00cc; }
.cpppreprocessor { color:#006666; }
.cppidentifier { color:#0000ff; }
.cppkeyword { color:#ff3300; font-weight:bold; }
.cppnumber { color:#990099; }
.cppoperator { color:#000066; }
.cppuserhighlight { color:#0099ff; }
/* for JavaScript */
.jsstring_dq { color:#ff0000; }
.jscomment { color:#008800; }
.jsstring_sq { color:#ff00cc; }
.jsidentifier { color:#0000ff; }
.jskeyword { color:#ff3300; font-weight:bold; }
.jsnumber { color:#990099; }
.jsoperator { color:#000066; }
.jsuserhighlight { color:#0099ff; }
.jsregexp { color: #fe5677; }
.jsloprand { color: #000000; }
/* TODO: other language css style adds here */
</style>
</head>
<body>
<div id="main">
<br />
<div style="font-size:0.9em;"><strong><em>A simple highlight (JavaScript/C/C++) code parser based on regular expression, works fine with IE6 and the latest FF/Chrome/Opera(<span style="color:green; font-size:0.9em;">have a small align bug in Safari between
lineno and highlight code</span>), hope you can enjoy, designed by Wind Fantasy © 2011-10-20</em></strong></div>
<div style="overflow-x: hidden;"> ----------------------------------------------------------------------------------------------------------------------------- </div>
<div style="color: #008800; font-size: 0.8em;">Version 2.0: Now I have partly fixed the <em>JavaScript Highlight bug</em> by supposing that '/' stands for division only if '/' follows <strong style="color: #ff0000;">({number}|{identifier}|[\)\]}])[ \t]*</strong>
directly, as we know that '/' needs a left operand for division</div>
<div style="font-size: 0.8em;">Please visit <a href="http://www.benjoffe.com/code/projects/syntax/" target="_blank">JavaScript Syntax Highlighter</a> for more information about JavaScript Highlight.</div>
<br />
<div id="code">
<div id="action">
<button id="hidecodesettings" type="button">Hide Code Settings</button>
<button id="highlight" type="button">Highlight Textarea Code</button>
<button id="testjsself" type="button">Highlight JavaScript Of Current Document</button>
</div>
<form autocomplete="off">
<div id="codesettings">
<div id="settingstitle">Code Settings</div>
<label>Select a Programing Language:
<select id="language">
<option value="cpp">C/C++</option>
<option value="js">JavaScript</option>
</select>
</label>
<label>Select Formatting Method:
<select id="formattingmethod">
<option value="pre">Use Pre Tag (Fast)</option>
<option value="entity">Use HTML Entity (Slow)</option>
</select>
</label>
<label><em>(Depreciated)</em> Highlight With Line Number:
<input type="checkbox" checked="checked" id="withlineno" disabled="disabled" />
</label>
<label><em>(Depreciated)</em> Tab Width(works only when <strong>[Select Formatting Method] > Use HTML Entity</strong> is Selected):
<input type="text" id="tabwidth" value="4" maxlength="1" />
</label>
<!--
<label>Line Number Min Width(works only when <strong>[Highlight With Line Number]</strong> is Checked):
<input type="text" id="linenominwidth" value="4" />
</label>
-->
<label>
<button id="submitcodesettings" type="button">Save Code Settings & Restart to Hightlight</button>
</label>
</div>
<div style="padding: 3px;">Time used: <span id="time" style="color: #FF0000;">0.000</span> s <span style="color: #008800;">(I think the latest opera browser is the fastest)</span></div>
<div id="codetext">
<textarea id="sourcecode">
// input some codes here to test, please help yourself
#include<cstdio> // work as well
#include <iostream>
#include <string>
using namespace std;
/*
swap int x and int y quickly with ^
we expect x, y is an int variable, must NOT be (const) expression
such as swap(1,y) OR swap(a+b, c), the same as swap_slow
*/
#define swap(x,y) \
x ^= y; \
y ^= x; \
x ^= y;
/*
swap int x and int slowly with temp
*/
#define swap_slow(x,y) \
int temp = x; \
y = x; \
x = temp;
int main() {
string s = "uvwxyz"; // nomal C++ string
/**
line-continuing string
*/
char[] lcs = "ABC\x44D\
EF\
G\
";
/**
special line-continuing string
*/
char[] slcs = "abc\\\"\\\
continue\
end";
/**
invalid line-continuing string 1
no line-continuing character (pattern) /\\(?:\r?\n)/ to be found
*/
char[] ilcs1 = "abc\\\"\\
invalidremain\
invalidend";
/**
invalid line-continuing string 2
*/
char[] ilcs2 = "abc\"\
def\
badend // no quote enclosed to be found
char c = '\x41'; // character 'A'
// line-continuing character
char lcc = '\
\
\x\
42'; // character 'B'
// no enclose double quote " string
char* partly_s = "abcdefg
// no enclose single quote ' character
char partly_c = 'z
float f = 1.2f;
double d = 3.// 3.0
double d2 = .3// 0.3
double e = .1234e-3L; // long double
unsigned int ui = 5678u;
long int li = 9l; // not 91
int i = 100;
double cast = 12345678f; // int to float, float to double
long int lhex = 0x12aBL;
unsigned int uhex = 0XFf23u;
int hex = 0xcD89;
int invalid = 0xZ123; // invalid hex number
cout << s << endl;
return 0;
}
</textarea>
</div>
</form>
</div>
<div id="presentation">
<div id="lineno"></div>
<div id="highlightcode"></div>
</div>
</div>
<script id="jsself" type="text/javascript">
// new bugs: (These are even difficult to be fixed because we need further analysising.)
var hacknum = 0;
{} /re/.test('re'); // {} becomes an empty statement so that /re/ should be highlighted as a R.E.
hacknum++ / 1 / 2; // division not R.E
/[/]/.test('/'); // non-escaped '/' in a character set of R.E. is interpreted as the end of R.E. unfortunately
hacknum = 1
/ // single-line comment does NOT work
0.5
/ 2; // the first '/' is NOT a delimiter of R.E. as you know that hacknum is 1 later;
var hacknull = null / 2; // division not R.E.
// !spam, partly fixed Version 1.0. Oh, JavaScript is NOT that easy!
// the big problem in Highlight JavaScript Code:
// code below is test for hacking, division symbol '/' is treated as a delimiter of the beginning of regular expression
// so far I have NOT (found) any way based on regular expression determining whether '/' stands for division(operator) or not(that is, for delimiter)
// sometimes we need Syntax Analysis, or even Semantic Analysis
// be careful with the code (being or like) below, which will cause confusing representation of the code according to its real meaning
// if we don't analysis R.E.(short for regular expression), quote(s) in R.E. will cause error when try to highlight/analysis quoted string
// else(that is we analysis R.E.) we don't know whether '/' stands for division(operator) or not
// [1]. I have NOT fixed part R.E.(fixed string as you can test, that is, a string defined like 'var s="abc', no enclose ", will be highlighted as string)
// [2]. I always suppose that your js code is clean and well-orgnized and you hardly use '/' as division twice or more OR mixed R.E. and division in the same line
// [3]. I always suppose your js code to be block not inline, you know(that is, multiline, so that an error ruins at most ONE line)
// [4]. The most important is that I suppose you DO according to the three rules above
var hacknum = 1/2, hackre = /"test/, hackcomment = "abc"; // this SHOULD have been single-line comment, but it is NOT as you see
// spam!
var HighlightParser =(function () {
// if you want to use jQuery $, please define a variable alias of $ by assignment, for example: var alias_$ = $, OR use jQuery itself
var $ = { // create a container so that we can access the previous attributes or methods next step by $.attributename or $.methodname
'library': {
// PHP-Partly-Compatible htmlspecialchars and htmlspecialchars_decode
'htmlspecialchars': function (str, encode_sq) { // make html special chars their entities, bool encode_sq means whether we wanna convert single quote to its entity
var ent = { '>': '>', '<': '<', '&': '&', '"': '"' };
ent["'"] = encode_sq ? ''' : "'";
return str.replace(/[\<\>&"']/g, function () { return ent[arguments[0]]; } );
},
'htmlspecialchars_decode': function (str, decode_sq) { // decode of htmlspecialchars
var ent_r = { '>': '>', '<': '<', '&': '&', '"': '"'};
ent_r['''] = decode_sq ? "'" : ''';
return str.replace(/&(?:gt|lt|quot|#039|amp);/g, function () { return ent_r[arguments[0]]; });
},
/*
'htmlspace': function (str) {
var ent = { '\n': '<br \/>', '\r\n': '<br \/>', '\t': $.config.tabreplacement };
//alert('sp:' + str + '*')
return str.replace(/\r?\n|\t|(\s)/g, function () { var $ = arguments; /*alert('1' + $[1] + '1'); if(! prompt('', false)) throw 'hi';* / return $[1] ? ' ' : ent[$[0]]; });
},
*/
'htmlspace': function (str) { // make html spaces(\t \n) its real representation in text mode
var ent = { '\t': $.config.tabreplacement, ' ': ' '}
return str.replace(/[\t ]/g, function () { return ent[arguments[0]]; });
},
'htmlbreakline': function (str, tagname) { // make html break-line character its real representation in text mode
if (! tagname) { // tagname means using tagname instead of tag br to render representation, possiablely use div, but div is block default while span is inline(this will cause problem)
return str.replace(/\r?\n/g, '<br \/>\r\n');
}
var bothtag = [ ['<', '>'].join(tagname), ['</', '>'].join(tagname)];
// return str.replace(/^.*$/mg, function () { return bothtag.join(arguments[0]); });
return str.replace(/(?:^|\r?\n)([^\r\n]*)/g, function () {
var $1 = String(arguments[1]);
//alert($1 + '*' + $1.charCodeAt(0) + '.' + $1.charCodeAt(1));
//alert(bothtag.join(arguments[1] || 'nbsp;'))
return bothtag.join(arguments[1] || ' '); });
},
'htmlpre': function (str, encode_sq) { // make representation of str in html mode the same as pre mode
//alert('pre:' + str + '*')
return $.library.htmlspace( $.library.htmlspecialchars(str, encode_sq) );
},
'objecttoarray': function (obj, includeprototype) { // reuturn an array of each propery VALUE of obj
var a = [];
for (var prop in obj) {
if (includeprototype || obj.hasOwnProperty(prop))
a.push(obj[prop]);
}
return a;
},
'propertytoarray': function (obj, includeprototype) { // return an array of each propery NAME of obj
var a = [];
for (var prop in obj) {
if (includeprototype || obj.hasOwnProperty(prop))
a.push(prop);
}
return a;
},
'repeat': function (repeat, times) { // return a string when variable "repeat" repeats "times" times
return new Array(times + 1).join(repeat);
}
},
'config': {
'language': 'js', // [current/selected] programing language in ['cpp', 'js', ... etc.]
'formattingmethod': 0 ? 'pre' : 'entity', // formatingmethod in ['pre', 'entity'] for [pre tag method(Fast), HTML entity method(Slow)]
'withlineno': false, // highlight with line number
'tabwidth': 4, // works only when formattingmethod is 'entity'
//'linenominwidth': 4, // works only when withlineno is true
'tabreplacement': new Array(4 + 1).join(' '), // but $.library.repeat(' ', $.config.tabwidth) doesn't work // replacement for tab
'updateTabwidth': function (tabwidth) {
$.config.tabwidth = tabwidth;
$.config.tabreplacement = new Array(tabwidth + 1).join(' ');
}
},
'regexp': { // core regular expressions for parser
'cpp': {
// both of 'string'(comment and uncomment) regexp below take the same effect
//'string': '"(?:\\\\(?:\\\\|"|\\r?\\n)|[^"\\n])*"?', // first is ", /"(?:\\(?:\\|"|\r?\n)|[^"\n])*"?/
'string': '"(?:\\\\(?:\\r?\\n|.)|[^"\\n\\\\])*"?', // first is ", /"(?:\\(?:.|\r?\n)|[^"\n\\])*"?/
'character': "'(?:\\\\(?:\\\\|'|\\r?\\n)|[^'\\n])*'?", // first is ', /'(?:\\(?:\\|'|\r?\n)|[^\n])*'?/
'preprocessor': '(?:^|\\r?\\n)[^\\S\\n]*#[^\\S\\n]*[a-zA-Z]+', // first is #, /(?:^|\r?\n)[^\S\n]*#[^\S\n]*[a-zA-Z]+/
'keyword': '(?:asm|auto|bad_cast|bad_typeid|bool|break|case|catch|char|class|const|const_cast|continue|default|delete|do|double|dynamic_cast|else|enum|except|explicit|extern|false|finally|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_cast|struct|switch|template|this|throw|true|try|type_info|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)(?=\\W|$)',
'userhighlight': '(?:assert|cout|cin|endl|std|string)(?=\\W|$)',
'identifier': '[^\\W\\d]\\w*', // first is [^\W\d], /[^\W\d]\w*/
//'number': '(?:0[xX][\\da-fA-F]+|\\d+)(?:[Ll]?[Uu]?|[Uu]?[Ll]?)|(?:\\d+\\.?\\d*|\\d*\\.?\\d+)(?:[Ee][+-]?\\d+)?[LlFf]?', // first is [\d\.], /(?:0[xX][\da-fA-F]+|\d+)(?:[Ll]?[Uu]?|[Uu]?[Ll]?)|(?:\d+\.?\d*|\d*\.?\d+)(?:[Ee][+-]?\d+)?[LlFf]?/
'number': '0[xX][\\da-fA-F]+(?:[Ll]?[Uu]?|[Uu]?[Ll]?)|\\d+(?:[Ll][Uu]?|[Uu][Ll]?)|(?:\\d+\\.?\\d*|\\d*\\.?\\d+)(?:[Ee][+-]?\\d+)?[LlFf]?', // first is [\d\.], /0[xX][\da-fA-F]+(?:[Ll]?[Uu]?|[Uu]?[Ll]?)|\d+(?:[Ll][Uu]?|[Uu][Ll]?)|(?:\d+\.?\d*|\d*\.?\d+)(?:[Ee][+-]?\d+)?[LlFf]?/
//'comment': '//.*|/\\*(?:[^*]|\\*[^\\/])*(?:\\*/)?', // first is /, expect * or / to be followed, /\/\/.*|\/\*(?:[^*]|\*[^\/])*(?:*/)?/
'comment': '//.*|/\\*(?:[^*]|\\*(?!\\/))*(?:\\*/)?', // first is /, expect * or / to be followed, /\/\/.*|\/\*(?:[^*]|\*(?!\/))*(?:\*\/)?/
'operator': '(?:[-!+*/%&^|=><]|>>|<<)=?|[?:]', // /(?:[-!+*/%&^|=><]|>>|<<)=?|[?:]/
'space': '[^\\S\\n]+' // first is [^\\S\\n], /[^\\S\\n]+/
},
'js': {
'string_dq': '"(?:\\\\(?:\\\\|"|\\r?\\n)|[^"\\n])*"?', // first is ", /"(?:\\(?:\\|"|\r?\n)|[^"\n])*"?/, $1
'string_sq': "'(?:\\\\(?:\\\\|'|\\r?\\n)|[^'\\n])*'?", // first is ', /'(?:\\(?:\\|'|\r?\n)|[^\n])*'?/, $2
'keyword': '(?:abstract|boolean|byte|char|class|const|debugger|double|enum|export|extends|final|float|goto|implements|import|int|interface|long|native|package|private|protected|public|short|static|super|synchronized|throws|transient|volatile|break|case|continue|default|do|else|false|function|if|in|instanceof|null|new|return|switch|this|true|typeof|undefined|var|void|while)(?=\\W|$)',
'userhighlight': '(?:Array|Bool|Date|Function|Global|Math|NaN|Number|Object|RegExp|String|apply|call|constructor|document|window)(?=\\W|$)',
'identifier': '[$_a-zA-Z][\\w\\$]*', // first is [$_a-zA-Z], /[$_a-zA-Z][\w\$]*/, $5
'number': '0[xX][\\da-fA-F]+|(?:\\d+\\.?\\d*|\\d*\\.?\\d+)(?:[Ee][+-]?\\d+)?', // first is [\d.], /0[xX][\da-fA-F]+|(?:\d+\.?\d*|\d*\.?\d+)(?:[Ee][+-]?\d+)?/, $6
//'comment': '//.*|/\\*(?:[^*]|\\*[^\\/])*(?:\\*/)?', // first is /, expect * or / to be followed, /\/\/.*|\/\*(?:[^*]|\*[^\/])*(?:*/)?/
'comment': '//.*|/\\*(?:[^*]|\\*(?!\\/))*(?:\\*/)?', // first is /, expect * or / to be followed, /\/\/.*|\/\*(?:[^*]|\*(?!\/))*(?:\*\/)?/
//'loprand': '\\)\\s*/(?![*/])', // first is ), /\)\s*\/(?![*\/])/
'loprand': '(?:[\\])]\\s*|\\}[ \\t]*)/(?![*/])', // first is )]}, /(?:[\])]\s*|\}[ \t]*)\/(?![*\/])/
//'regexp': '/(?:\\\\/|[^/\\n])*/?[gmi]*', // first is /, /\/(?:[^\/\n]|\\\/)+)\//[gmi]*
//'regexp': '/(?:\\\\[/\\\\]|[^/\\n])+/[gmi]*[^\\S\\n]*', // first is /, /\/(?:\\[\/\\]|[^\/\n])+\/[gmi]*[^\S\n]*(?![\w])/
//'regexp': '/(?:\\\\[\\/\\\\]|[^/\\n\\\\])+/[gmi]*', // first is /, /\/(?:\\[\/\\]|[^/\n\\])+\/[gmi]*/
'regexp': '/(?:\\\\.|[^\\\\\\n/])+/[gmi]*', // first is /, /\/(?:\\.|[^\\\n\/])+\/[gmi]*/, $9
'operator': '(?:[-!+*/%&^|=><]|>>|<<)=?|=|[?:]', // /(?:[-!+*/%&^|=><]|>>|<<)=?|[?:]/
'space': '\\s+' // first is \s, /\s+/
},
'html': { // to be implemented
'tag': '<([a-zA-Z])(\\s>)*>.' // /<([a-zA-Z]+)((?:\s+[-\w]+=(?:'[^']*'|"[^"]*"))*)\s*>(.*)(?:<\/\1>)?/
}
},
'getCompiledPattern': function(language) {
var pattern = '(' + $.library.objecttoarray($.regexp[language]).join(')|(') + ')';
return new RegExp(pattern, "g");
},
'getCurrentPattern': function () {
return $.getCompiledPattern($.config.language);
},
'parse': function (sourcecode) {
// first we convert windows break line "\r\n" to unix/linux "\n" to fix opera/safari bug
sourcecode = sourcecode.replace(/\r(?=\n)/g, ''); // fix opera/safari(Opera/Safari will treat '\r' as the same representation of '\n')
var language = $.config.language, classNames = $.library.propertytoarray($.regexp[language]);
var premethod = ($.config.formattingmethod == 'pre');
var htmlformater = premethod ? $.library.htmlspecialchars
: function (str, encode_sq) { return $.library.htmlspace($.library.htmlspecialchars(str, encode_sq));};
if (language == 'js') {
var pattern = new RegExp( '(' + $.library.objecttoarray($.regexp[language]).join(')|(') + ')' );
var isdivision = false, s = [], turnback;
var divisionflag = [null, true, true, false, false, true, true]; // only 1, 2, 5, 6 subpattern can be placed before division
var next = sourcecode, matches, i, len, highlightcode, htmlspace = function (str) { return premethod ? str : $.library.htmlspace(str); };
while ( (matches = pattern.exec(next)) != null) {
for (i = 1, len = matches.length; i < len; ++i) {
if (matches[i]) {
if (i == 9 && isdivision && matches.index == 0) { // when try to match regexp, but '/' turns out to be division, sorry we should turn back
s.push([htmlformater(next.substr(0, matches.index), true), '<span class="jsoperator"\>/\<\/sapn\>'].join(''));
turnback = true;
next = next.substr(matches.index + 1);
} else if (i == len - 1) { // spaces?
s.push ( htmlformater(next.substr(0, matches.index + matches[i].length), true) );
} else if (i == 8) { // loperand before '/'? i.e. '(function () { return 1; })() / 2', matches[0] is ') /'
s.push ( [htmlformater(next.substr(0, matches.index), true), '<span class="js', classNames[i-1], '"\>', htmlspace(matches[i]).replace('/', '\<span class="jsoperator"\>/\<\/sapn\>'), '\<\/span\>'].join('') );
} else {
s.push ( [htmlformater(next.substr(0, matches.index), true), '<span class="js', classNames[i-1], '"\>', htmlformater(matches[i], true), '\<\/span\>'].join('') );
}
if (i == len - 1) {
isdivision = (matches.index == 0 && matches[0].indexOf('\n') == -1) ? isdivision : false;
} else {
isdivision = Boolean(divisionflag[i]);
}
break;
}
}
if (! turnback) {
next = next.substr(matches.index + matches[0].length);
} else {
turnback = false;
}
}
s.push(next);
highlightcode = s.join('');
} else {
var replace_callback = function () {
var matches = arguments;
var reallen = matches.length - 3; // strip last spaces, position where pattern starts to match and the original string to be replaced
for (var i=1; i < reallen; ++i) {
if(matches[i]) {
//alert(['*', matches[i], '*', matches[i].length, '*'].join(''));
return ['<span class="', language, classNames[i-1], '">', htmlformater(matches[i], true), '<\/span\>'].join('');
break;
}
}
return htmlformater(matches[0], true);
}
highlightcode = sourcecode.replace($.getCurrentPattern(), replace_callback);
}
return premethod ? ['<pre>\r\n', '</pre>'].join(highlightcode) : $.library.htmlbreakline(highlightcode, false);
},
'getLineNoHTML': function (sourcecode) {
var linecount = sourcecode.split('\n').length, w = String(linecount).length;
var repeat = $.library.repeat, s, a = [];
for (var i=1; i <= linecount; ++i) {
s = String(i);
a.push( [repeat('0', w - s.length), s, ' '].join('') ); // possiblely use instead of 0
}
if($.config.formattingmethod == 'pre') {
return ['<pre\>', a.join("\r\n"), '\r\n<\/pre>'].join('');
}
return a.join("\r\n<br \/>");
}
};
return $;
})();
function g(id) {
return document.getElementById(id);
}
window.onload = function () {
var elineno = g('lineno');
var ehighlightcode = g('highlightcode');
var esourcecode = g('sourcecode');
var elanguage = g('language');
var ewithlineno = g('withlineno');
var etabwidth = g('tabwidth');
var ecodesettings = g('codesettings');
var eformattingmethod = g('formattingmethod');
var time = g('time');
var cfg = HighlightParser.config;
g('hidecodesettings').onclick = function () {
var style = ecodesettings.style;
if (style.display == 'none') {
style.display = 'block';
this.innerHTML = 'Hide Code Settings';
} else {
style.display = 'none';
this.innerHTML = "Display Code Settings";
}
}
var highlight = function () {
var t = new Date().getTime();
elineno.innerHTML = HighlightParser.getLineNoHTML(esourcecode.value);
ehighlightcode.innerHTML = HighlightParser.parse(esourcecode.value);
time.innerHTML = String( (new Date().getTime() - t) / 1000 );
if (window.ActiveXObject) { // fix ie6
ehighlightcode.style.height = elineno.clientHeight + 20 + 'px';
}
}
var reloadconfig = function () {
cfg.language = elanguage.value;
cfg.withlineno = ewithlineno.checked;
cfg.formattingmethod = eformattingmethod.value;
cfg.updateTabwidth(parseInt(etabwidth.value, 10));
}
g('submitcodesettings').onclick = function () {
reloadconfig();
highlight();
}
g('highlight').onclick = function () {
highlight();
}
g('testjsself').onclick = function () {
esourcecode.value = g('jsself').innerHTML;
/* There is a small difference(innerHTML of html script node) between w3c-compatible browser and IE6, here you can test
var v = esourcecode.value, a = [0, 1, 2, 3], s = [];
for (var i in a) {
s.push(v.charCodeAt(a[i]));
}
alert(s);
*/
elanguage.selectedIndex = 1;
reloadconfig();
highlight();
}
reloadconfig();
highlight();
}
</script>
</body>
</html>
相关文章推荐
- JS通用表单验证函数,基于javascript正则表达式
- JS通用表单验证函数,基于javascript正则表达式(IE下)
- JS通用表单验证函数,基于javascript正则表达式
- JS通用表单验证函数,基于javascript正则表达式
- JS通用表单验证函数,基于javascript正则表达式http://www.ccvita.com/60.html
- JS通用表单验证函数,基于javascript正则表达式 [转]
- JS通用表单验证函数,基于javascript正则表达式 最近工作比较忙
- JavaScript中的正则表达式解析[JS正则知识]
- JavaScript(JS)常用的正则表达式
- js---在前端开发中,如何通过正则表达式去除字符串string首尾的空格,JavaScript正则去除首尾空格
- JavaScript高级程序设计(第3版)学习笔记12 js正则表达式
- js、javascript正则表达式验证身份证号码
- javascript匹配js中注释的正则表达式代码
- JavaScript(JS)常用的正则表达式
- JS 正则表达式用法 javascript常用正则表达式 修正版
- javascript 基于正则表达式的文本框验证代码
- javascript中基于replace函数的正则表达式语法
- [js点滴]JavaScript之正则表达式详解01
- C++、Java、JavaScript中的正则表达式
- JS正则表达式应用——基于Jquery的验证密码强度特效