分享
 
 
 

javascript实现语法分色编辑器

王朝html/css/js·作者佚名  2008-05-20
窄屏简体版  字體: |||超大  

最近一段时间忽然想到用javascript理论上可以实现一个复杂的在线编辑器,完全能够支持词法、语法方面的功能,于是试验了一下...效率和简易程度还是令人吃惊的,看来javascript比想象的还要强大...

<html>

<head>

<title>Silverna Demo Ver 0.01</title>

<style>

div.editbox{

margin:0 0 0 0;

padding:0 0 0 0;

font:16/18px Arial;

border:0px solid #000000;

}

p{

margin:0 0 0 0;

padding:0 0 0 0;

}

</style>

</head>

<body style="margin:0 0 0 0;padding:0 0 0 0;word-break:break-all;overflow-x:hidden" onload="editbox.focus()">

<div id="editbox" class="editbox" style="width:99%;height:80%;" contentEditable="true" onkeyDown="return KeyDown()" onkeyUp="KeyUp()" onclick="getCursorPosition()">

</div>

<select size="10" style="display:none;position:absolute" id="methods" onkeydown="SelectMethod()" onclick="SelMethod(this)">

</body>

</html>

</select>

<script language=JScript>

var testArray = new Array();

var testDate = new Date();

var testString = "aaa";

var testVal = 1;

var testObj = new myObj;

function myObj()

{

myObj.prototype.testFunc = function(){};

this.testProperty = "test";

}

function KeyDown()

{

//alert(event.altKey);

if(event.keyCode == 9) //TAB 键

{

clipboardData.setData('text',' ');

event.srcElement.document.execCommand('paste');

return false;

}

if(event.keyCode == 8) //Backspace 键

{

var oSel = document.selection.createRange();

var offset = event.srcElement.document.selection.createRange();

offset.moveToPoint(oSel.offsetLeft, oSel.offsetTop);

offset.moveStart('character', -4);

if(offset.text.length < 4) return true;

for (var i = 0; i < offset.text.length; i++)

{

if (offset.text.charAt(i) != " ")

{

return true;

}

}

offset.select();

event.srcElement.document.execCommand('Delete');

return false;

}

return true;

}

function KeyUp()

{

var oSel, offset;

if(event.keyCode == 13)

{

testStr = event.srcElement.innerText.substring(0, getCursorPosition());

//alert(testStr);

var space = "";

for (var i = testStr.length - 1; i >= 0; i--)

{

//alert(testStr.length+":"+testStr.charAt(i) + ":" + space.length);

if (testStr.charAt(i) == "\n") break;

if (testStr.charAt(i) == " ")

space += " ";

else

space = "";

}

//alert(testStr);

clipboardData.setData('text',space);

event.srcElement.document.execCommand('paste');

}

oSel = document.selection.createRange();

var left = oSel.offsetLeft;

var top = oSel.offsetTop;

var token = getCurrentToken(event.srcElement);

var chars = getCursorPosition();

parseSyntax(event.srcElement);

offset = event.srcElement.document.selection.createRange();

offset.moveToPoint(left, top);

offset.select();

if(event.keyCode == 190) //.键

{

setMethods(token.posTok.slice(0, -1));

}

}

function parseSyntax(src) //解析当前文本

{

var text = src.innerHTML;

text = text.replace(/<FONT[^<>]*>/gi, "").replace(/<\/FONT[^<>]*>/gi,"");

text = text.replace(/<P>/gi, "\xfe").replace(/<\/P>/gi, "\xff");

text = text.replace(/\&nbsp;/gi, "\xfd");

text = text.replace(/\r\n/gi,"");

for (var i = 0; i <SyntaxSet.All.length; i++)

{

var syntaxes = SyntaxSet.All[i];

for (var j = 0; j < syntaxes.rules.All.length; j++)

{

syntaxes.rules.All[j].color = syntaxes.color;

syntaxes.rules.All[j].cons = syntaxes.cons;

text = parseRule(text, syntaxes.rules.All[j]);

}

}

src.innerHTML = text.replace(/\xfc/g,"'").replace(/\xfe/g,"<P>").replace(/\xff/g,"</P>").replace(/\xfd/g,"&nbsp;");

}

function parseRule(text, rule) //解析词法

{

//利用正则表达式

var newText = "";

var idx = text.search(rule.expr);

while (idx != -1)

{

var remark = text.match(rule.expr);

//alert(text.substring(0, idx+remark[0].length));

var subText = text.substring(0, idx + remark[0].length);

if(rule.cons == null || (idx == 0 || rule.cons.test(text.charAt(idx-1))) && (idx + remark[0].length >= text.length || rule.cons.test(text.charAt(idx + remark[0].length))))

{

//alert(remark[0]);

//alert(remark[0].replace(/<FONT[^<>]*>/gi, "").replace(/<\/FONT[^<>]*>/gi,""));

subText = subText.replace(remark[0], "<FONT color=\xfc"+rule.color+"\xfc>" + remark[0].replace(/<FONT[^<>]*>/gi, "").replace(/<\/FONT[^<>]*>/gi,"") + "</FONT>");

//alert(subText);

}

newText += subText;

text = text.substring(idx + remark[0].length);

idx = text.search(rule.expr);

}

newText += text;

return newText;

}

function getCurrentToken(src)

{

var oSel = document.selection.createRange();

var offset = src.document.selection.createRange();

offset.moveToPoint(oSel.offsetLeft, oSel.offsetTop);

offset.moveStart("character", -99999);

var tokens = offset.text.split(/[\s\+\-\*\/]/); //token由连续字母数字、下划线、点号、括号、引号构成

var currentToken = tokens[tokens.length - 1];

var idx = offset.text.length;

var fullToken = src.innerText.substring(idx);

fullToken = fullToken.replace(/[\s\+\-\*\/]/,"@@@@");

idx = fullToken.indexOf("@@@@");

if(idx != -1)

fullToken = fullToken.substring(0, idx);

var token = new Array();

token.currentToken = currentToken + fullToken;

token.posTok = currentToken;

return token;

}

Array.prototype.pushDistinct = function(obj)

{

for (var i = 0; i < this.length; i++)

{

if (this[i] == obj)

{

return null;

}

}

this.push(obj);

return obj;

}

function putMethods(methodList, obj, methods) //将方法添加到方法列表

{

var list = methods.split(",");

for (var i = 0; i < list.length; i++)

{

if (obj[list[i]] != null)

{

methodList.pushDistinct(list[i]);

}

}

}

var now = new Date(); //测试用

var a = 33.3333; //测试用

var __expr = new RegExp("tt"); //测试用

function setMethods(objStr)

{

var oSel = document.selection.createRange();

try

{

if (objStr == "alert") return;

var methodList = new Array();

var obj = eval(objStr);

if (obj.prototype != null)

{

methodList.pushDistinct("prototype");

}

if (obj != null)

{

//基本Object方法

putMethods(methodList, obj,"constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf");

//基本Array方法

putMethods(methodList, obj,"concat,join,length,pop,push,reverse,shift,slice,sort,splice,unshift");

//基本Date方法

putMethods(methodList,obj,"getDate,getUTCDate,getDay,getUTCDay,getFullYear,getUTCFullYear,getHours,getUTCHours,getMilliseconds,getUTCMilliseconds,getMinutes,getUTCMinutes,getMonth,getUTCMonth,getSeconds,getUTCSeconds,getTime,getTimezoneoffset,getYear");

putMethods(methodList,obj,"setDate,setUTCDate,setFullYear,setUTCFullYear,setHours,setUTCHours,setMilliseconds,setUTCMilliseconds,setMinutes,setUTCMinutes,setMonth,setUTCMonth,setSeconds,setUTCSeconds,setTime,setYear,toDateString,toGMTString,toLocaleDateString,toLocaleTimeString,toString,toTimeString,toUTCString,valueOf,parse,UTC");

//基本Math方法

putMethods(methodList,obj,"E,LN10,LN2,LOG10E,LOG2E,PI,SQRT1_2,SQRT2");

putMethods(methodList,obj,"abs,acos,asin,atan,atan2,ceil,cos,exp,floor,log,max,min,pow,random,round,sin,sqrt,tan");

//基本Function方法

putMethods(methodList,obj,"arguments,caller,length,prototype,apply,call,toString");

//基本Number方法

putMethods(methodList,obj,"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY");

putMethods(methodList,obj,"toString,toLocalString,toFixed,toExponential,toPrecision");

//基本RegExp方法

putMethods(methodList,obj,"global,ignoreCase,lastIndex,multiline,source,exec,test");

//基本String方法

putMethods(methodList,obj,"charAt,charCodeAt,contact,indexOf,lastIndexOf,match,replace,search,slice,split,substring,substr,toLowerCase,toString,toUpperCase,valueOf,fromCharCode");

putMethods(methodList,obj,"anchor,big,blink,bold,fixed,fontcolor,fontsize,italics,link,small,strike,sub,sup");

}

for (each in obj)

{

methodList.pushDistinct(each);

}

methodList.sort();

if (methodList.length > 0)

{

methods.options.length = 0;

for (var i = 0; i < methodList.length; i++)

{

methods.options.add(new Option(methodList[i]));

}

if (methods.options.length > 10)

{

methods.size = 10;

}

else

{

methods.size = methods.options.length;

}

methods.style.top = oSel.offsetTop;

methods.style.left = oSel.offsetLeft;

methods.style.display = "";

methods.options[0].selected = true;

methods.focus();

}

}

catch(e){}

}

function SelectMethod()

{

var src = event.srcElement;

if(event.keyCode == 13)

{

SelMethod(src);

}

if(event.keyCode == 27 || event.keyCode == 8 || event.keyCode == 32)

{

src.style.display = "none";

editbox.focus();

}

}

function SelMethod(src)

{

clipboardData.setData('text',src.options[src.selectedIndex].text);

editbox.focus();

editbox.document.execCommand('paste');

src.style.display = "none";

getCursorPosition();

}

function getPos(text) //计算行数、列数

{

var rows = 1;

var cols = 1;

var idx = 0;

var subText = text;

while((idx = subText.indexOf("\n")) != -1)

{

subText = subText.substring(idx + 1);

rows++;

}

return new Array(rows, subText.length + 1);

}

function getNullRows(src,oSel) //计算空行

{

var rows = 0;

var offsetEnd = src.document.selection.createRange();

var oldTop = 2;

var oldLeft = 2;

while(1)

{

offsetEnd.moveToPoint(oSel.offsetLeft, oSel.offsetTop);

offsetEnd.moveStart("character",-1-rows);

if (offsetEnd.text.length > 0 || offsetEnd.offsetTop == oldTop && offsetEnd.offsetLeft == oldLeft)

{

break;

}

rows ++;

oldTop = offsetEnd.offsetTop;

oldLeft = offsetEnd.offsetLeft;

}

return rows;

}

function getCursorPosition()

{

var src = event.srcElement;

var offset = src.document.selection.createRange();

var oSel = document.selection.createRange();

var textLength = src.innerText.length;

offset.moveToPoint(oSel.offsetLeft, oSel.offsetTop);

offset.moveStart("character", -99999);

//src.document.execCommand("ForeColor",false,"#ff0000");

var rowSpans = offset.getClientRects();

var pos = getPos(offset.text);

var charCodes = offset.text.length; //字符总数

var chars = offset.text.replace(/\r\n/g,"").length + 1; //字符

var extRows = getNullRows(src,oSel);

if(extRows > 0)

{

pos[0] += extRows;

pos[1] = 1;

}

window.status = "行: " + pos[0] +", 列: " + pos[1] + ", 第 " + chars + " 个字符" + " ("+ oSel.offsetTop +","+

oSel.offsetLeft +")";

return charCodes;

}

///词法解析过程................................................................................

///............................................................................................

///............................................................................................

var SyntaxSet = new Array(); //词法规则集合

SyntaxSet.All = new Array();

SyntaxSet.parse = function(token) //针对token返回rule

{

for (var i = 0; i < this.All.length; i++)

{

var syntaxes = this.All[i];

for (var j = 0; j < syntaxes.rules.All.length; j++)

{

if (syntaxes.rules.All[j].test(token))

{

syntaxes.rules.All[j].color = syntaxes.color;

return syntaxes.rules.All[j];

}

}

}

return null;

}

SyntaxSet.add = function(syntaxes)

{

if(this[syntaxes.name] != null)

return;

this[syntaxes.name] = syntaxes;

this.All.push(syntaxes);

}

function Syntaxes(name, color, cons) //词法规则组(同组规则用一种颜色标记)

{

this.name = name; //规则组名称

this.color = color; //标记该语法的颜色

this.rules = new Array(); //语法规则(以次序决定优先级)

this.rules.All = new Array();

this.cons = cons; //边界约束

Syntaxes.prototype.addRule = function(rule)

{

if(this.rules[rule.name] != null)

return;

this.rules[rule.name] = rule;

this.rules.All.push(rule);

}

}

function SyntaxRule(name, regExp) //词法规则

{

this.name = name; //规则名称

this.expr = regExp; //规则描述 (正则表达式)

SyntaxRule.prototype.test = function(token)

{

return this.expr.test(token);

}

}

function RegExprX(exprStr) //扩展正则表达式的功能,支持定义嵌套

{

this.expr = exprStr;

}

RegExprX.prototype.getPattern = function(tag) //获取正则表达式对象

{

if (tag == null)

return new RegExp(this.expr);

else

return new RegExp(this.expr, tag);

}

RegExprX.prototype.concat = function(expr, rule) //连接两个正则表达式串

{

if (rule == null)

this.expr += expr; //直接连接

else if (rule == "union") //联合

this.expr = "(" + this.expr + ")" + "|" + "(" + expr + ")";

else if (rule == "cons") //约束

this.expr = this.expr + "(?=" + expr + ")";

return this.expr;

}

//为保证正确计算偏移量需要替换回车\n\r为\xff

SyntaxSet.add(new Syntaxes("keywords", "#0000ff", /[\s\.\xfe\xff\xfd\(\{\}\)\;\,]/)); //词法?关键词?蓝色

SyntaxSet["keywords"].addRule(new SyntaxRule("Function",/function/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Variable",/var/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Return",/return/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Exception",/(try|catch|throw)/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Condition",/(if|else|switch)/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Cycle",/(for|while|do)/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Type",/(int|double|float|void|char)/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Right",/(public|private|protected|static)/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Constant",/(null|undefined|NaN|Infinity)/));

SyntaxSet["keywords"].addRule(new SyntaxRule("Construct",/(new|delete)/));

SyntaxSet.add(new Syntaxes("objects", "#FF0000", /[\s\.\xfe\xff\xfd\(\{\}\)\;\,]/)); //词法?对象?红色

SyntaxSet["objects"].addRule(new SyntaxRule("Object",/(Array|arguments|Boolean|Date|Error|Function|Object|Number|Math|RegExp|String)/));

SyntaxSet.add(new Syntaxes("global", "#800000", /[\s\.\xfe\xff\xfd\(\{\}\)\;\,]/)); //词法?系统函数?红色

SyntaxSet["global"].addRule(new SyntaxRule("SystemFunc",/(alert|parseFloat|parseInt|eval|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|escape|eval|isFinite|isNaN|unescape)/));

SyntaxSet.add(new Syntaxes("String", "#ff00ff", /[\s\.\xfe\xff\xfd\(\{\}\)\;\,\+\-\*\/]/)); //词法?字符串?粉色

SyntaxSet["String"].addRule(new SyntaxRule("String",

/('((\\\')|[^\xff\'])*([^\\\']|(\\\'))')|("((\\\")|[^\xff\"])*([^\\\"]|(\\\"))")/));

SyntaxSet.add(new Syntaxes("remarks", "#008000")); //词法?注释?绿色

SyntaxSet["remarks"].addRule(new SyntaxRule("ShortRemark",/\/\/[^\xff]*/));

SyntaxSet["remarks"].addRule(new SyntaxRule("LongRemark",/\/\*((.*\*\/)|(.*$))/));

function Grammars() //语法规则

{

}

</script>

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有