How to implement a syntax hiliter in JavaScript?

Recently, I'd participated in an AJAX/RIA project. Our team included about 5 members: one lead, two programmers and two QAs. The main goal of this project is to provide a prototype for asset management by using <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> technologies. As you know, <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> application can provide rich and good experience to the user . For example, the application used <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> is almost composed of several pages and other resources like CSS and images. Therefore, the transfer amount between client and server could be down and at the same time the user's operation interruption are to avoided.

For some reasons, we chose a commercial framework SmartClient as our toolkit for developing <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city>. SmartClient is without question the one of bested AJAX GUI system or frameworks. We expressed deeply by its functionalities and effects. It provided various client components, plentiful documents and examples. You can easily find the answer we want. But, the biggest limitation of SmartClient is that it is commercial software, so we could not see more detailed of it. As a result, the development was difficulty when you desire to extend some components. Anyway, SmartClient is good <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> library except for high price and development efficiency.

We spent about two months to archive this prototype. Finally, this prototype gave our teammates and the customer good impression finally.

The source code from SmartClient is high quality code; easy understanding, simple and clean. JavaScript is an object based programming language. We used to thinking in OO because we are Object-Oriented programmer. Maybe some book mentioned the techniques for writing Object-Oriented programs in JavaScript. Standing the shoulders of giants, you can get through the mountaintop more quickly and safely. Reading high quality source code could be a short cut to become a software craftsman.
js 代码
 
  1. if(window.isc&&window.isc.module_Core&&!window.isc.module_SyntaxHiliter)  
  2. {  
  3.     isc.module_SyntaxHiliter=1;  
  4.     isc._moduleStart=isc._SyntaxHiliter_start=(isc.timestamp?isc.timestamp():new Date().getTime());  
  5.     if(isc._moduleEnd&&(!isc.Log||(isc.Log && isc.Log.logIsDebugEnabled('loadTime'))))  
  6.     {  
  7.         isc._pTM=  
  8.         {  
  9.             message:'SyntaxHiliter load/parse time: ' + (isc._moduleStart-isc._moduleEnd) + 'ms', category:'loadTime'  
  10.         };  
  11.         if(isc.Log && isc.Log.logDebug)isc.Log.logDebug(isc._pTM.message,'loadTime')  
  12.         else if(isc._preLog)isc._preLog[isc._preLog.length]=isc._pTM  
  13.         else isc._preLog=[isc._pTM]  
  14.     }  
  15.     isc.defineClass("SyntaxHiliter");  
  16.     isc.A=isc.SyntaxHiliter.getPrototype();  
  17.     isc.A.spanStart=";  
  18.     isc.A.spanStartClose="'>";  
  19.     isc.A.spanEnd="";  
  20.     isc.A=isc.SyntaxHiliter.getPrototype();  
  21.     isc.B=isc._allFuncs;  
  22.     isc.C=isc.B._maxIndex;  
  23.     isc.D=isc._funcClasses;  
  24.     isc.D[isc.C]=isc.A.Class;  
  25.     isc.B.push(isc.A.init=function()  
  26.     {  
  27.         this.fixedSpanLengths=this.spanStart.length+this.spanStartClose.length+this.spanEnd.length;  
  28.         this.spanEndLength=this.spanEnd.length  
  29.     }  
  30.     ,isc.A.hilite=function(_1,_2,_3,_4)  
  31.     {  
  32.         var _5=this.regexps;  
  33.         if(!_5)return _1;  
  34.         var _6=[_1];  
  35.         for(var _7=0;_7<_5.length;_7++)  
  36.         {  
  37.             var _8=_5[_7];  
  38.             var _9=_8.regexp;  
  39.             var _10=_8.cssStyles;  
  40.             if(!isc.isAn.Array(_10))_10=[_10];  
  41.             var _11=0;  
  42.             while(_11<_6.length)  
  43.             {  
  44.                 var _12=_6[_11];  
  45.                 if(_12==null)  
  46.                 {  
  47.                     _22+=this.fixedSpanLengths+_6[_11+2].length+_6[_11+4].length;  
  48.                     _11+=6;  
  49.                     continue  
  50.                 }  
  51.                 var _13=_9.exec(_12);  
  52.                 if(_13==null)  
  53.                 {  
  54.                     _11++;  
  55.                     _22+=_12.length;  
  56.                     continue  
  57.                 }  
  58.                 if(_13.length-1!=_10.length)  
  59.                 {  
  60.                     this.logWarn("regexp: "+_9+" matched "+(_13.length-1)+" groups, but only "+_10.length+" cssStyles are defined - skipping this regexp.");  
  61.                     _11=_6.length;  
  62.                     continue  
  63.                 }  
  64.                 var _14=_13[0];  
  65.                 var _15=_13.index;  
  66.                 _22+=_15;  
  67.                 var _16=_12.substring(0,_15);  
  68.                 var _17=_12.substring(_15+_14.length);  
  69.                 if(_16.length>0)  
  70.                 {  
  71.                     _6.splice(_11++,1,_16,_17)  
  72.                 }  
  73.                 else  
  74.                 {  
  75.                     _6[_11]=_17  
  76.                 }  
  77.                 for(var _18=1;_18<_13.length;_18++)  
  78.                 {  
  79.                     var _19=_13[_18]||isc.emptyString;  
  80.                     var _20=_10[_18-1];  
  81.                     if(_20==null)  
  82.                     {  
  83.                         _6.splice(_11++,0,_19);  
  84.                         continue  
  85.                     }  
  86.                     var _21=isc.Canvas.getStyleText(_20)||isc.emptyString;  
  87.                     _6.splice(_11,0,null,this.spanStart,_21,this.spanStartClose,_19,this.spanEnd);  
  88.                     _11+=6  
  89.                 }  
  90.             }  
  91.         }  
  92.         var _22=0;  
  93.         var _23=_3!=null;  
  94.         for(var i=0;i<_6.length;i++)  
  95.         {  
  96.             var _12=_6[i];  
  97.             var _25;  
  98.             if(_12==null)  
  99.             {  
  100.                 _12=_6[i+4];  
  101.                 _25=_12.asHTML(!this.autoWrap);  
  102.                 if(_23&&_3>_22)  
  103.                 {  
  104.                     if(_3<(_22+_12.length))  
  105.                     {  
  106.                         var _26=_3-_22;  
  107.                         _27=_12.slice(0,_26);  
  108.                         _3+=_27.asHTML(!this.autoWrap).length-_27.length;  
  109.                         _3-=this.spanEndLength  
  110.                     }  
  111.                     else  
  112.                     {  
  113.                         _3+=_25.length-_12.length  
  114.                     }  
  115.                     _3+=this.fixedSpanLengths+_6[i+2].length;  
  116.                     _22+=_25.length+this.fixedSpanLengths+_6[i+2].length  
  117.                 }  
  118.                 else  
  119.                 {  
  120.                     _23=false  
  121.                 }  
  122.                 _6[i+4]=_25;  
  123.                 i+=5;  
  124.                 continue  
  125.             }  
  126.             _25=_12.asHTML(!this.autoWrap);  
  127.             if(_23&&_3>_22)  
  128.             {  
  129.                 if(_3<(_22+_12.length))  
  130.                 {  
  131.                     var _26=_3-_22;  
  132.                     var _27=_12.slice(0,_26);  
  133.                     _3+=_27.asHTML(!this.autoWrap).length-_27.length  
  134.                 }  
  135.                 else  
  136.                 {  
  137.                     _3+=_25.length-_12.length  
  138.                 }  
  139.                 _22+=_25.length  
  140.             }  
  141.             else  
  142.             {  
  143.                 _23=false  
  144.             }  
  145.             _6[i]=_25  
  146.         }  
  147.         var _28=_6.join(isc.emptyString);  
  148.         if(_3!=null)  
  149.         {  
  150.             _28=_28.slice(0,_3)+_4+_28.slice(_3,_28.length)  
  151.         }  
  152.         if(_2)  
  153.         {  
  154.             var _29=[];  
  155.             var _30;  
  156.             while((_30=_28.indexOf("
    "
    ))!=-1)  
  157.             {  
  158.                 var _31=_28.slice(0,_30+4);  
  159.                 _28=_28.substring(_30+4);  
  160.                 _29[_29.length]=_31  
  161.             }  
  162.             if(_28.length)_29[_29.length]=_28;  
  163.             return _29  
  164.         }  
  165.         else  
  166.         {  
  167.             return _28  
  168.         }  
  169.     }  
  170.     ,isc.A.containsMultilineToken=function(_1)  
  171.     {  
  172.         if(this.multilineTokens)  
  173.         {  
  174.             for(var i=0;i<this.multilineTokens.length;i++)  
  175.             if(_1.match(this.multilineTokens[i]))return true  
  176.         }  
  177.         return false  
  178.     }  
  179.     );  
  180.     isc.B._maxIndex=isc.C+3;  
  181.     isc.defineClass("JSSyntaxHiliter","SyntaxHiliter");  
  182.     isc.A=isc.JSSyntaxHiliter.getPrototype();  
  183.     isc.A.regexps=[  
  184.     {  
  185.         regexp:/(\/\*.*?\*\/)/,cssStyles:"js_multilineComment"  
  186.     }  
  187.     ,  
  188.     {  
  189.         regexp:/(\/\/.*)/,cssStyles:"js_lineComment"  
  190.     }  
  191.     ,  
  192.     {  
  193.         regexp:/("(?:[^"\n]|\\")*")/,cssStyles:"js_doubleQuotedString"  
  194.     }  
  195.     ,  
  196.     {  
  197.         regexp:/('(?:[^'\n]|\\')*')/,cssStyles:"js_singleQuotedString"  
  198.     }  
  199.     ,  
  200.     {  
  201.         regexp:/(\/(?:\\\/|[^\/\n])+\/)/,cssStyles:"js_regex"  
  202.     }  
  203.     ,  
  204.     {  
  205.         regexp:/([^a-zA-Z0-9_$:]|^)(abstract|boolean|break|byte|case|char|class|const|continue|default|delete|do|dougle|else|enum|extends|final|finally|float|for|fun[c]tion|goto|if|implements|import|in|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|try|typeof|var|void|while|with)([^a-zA-Z0-9_$:]|$)/,cssStyles:[null,"js_reservedWord",null]  
  206.     }  
  207.     ,  
  208.     {  
  209.         regexp:/([^a-zA-Z0-9_$:]|^)(true|false|null)([^a-zA-Z0-9_$:]|$)/,cssStyles:[null,"js_nativeValue",null]  
  210.     }  
  211.     ,  
  212.     {  
  213.         regexp:/([a-zA-Z][a-zA-Z0-9_$]*)(\s*:)/,cssStyles:["js_label",null]  
  214.     }  
  215.     ];  
  216.     isc.A.multilineTokens=[/(\/\*)|(\*\/)/];  
  217.     isc.defineClass("CSSSyntaxHiliter","SyntaxHiliter");  
  218.     isc.A=isc.CSSSyntaxHiliter.getPrototype();  
  219.     isc.A.regexps=[  
  220.     {  
  221.         regexp:/(\/\*.*?\*\/)/,cssStyles:"css_multilineComment"  
  222.     }  
  223.     ,  
  224.     {  
  225.         regexp:/(\/\/.*)/,cssStyles:"css_lineComment"  
  226.     }  
  227.     ,  
  228.     {  
  229.         regexp:/([a-zA-Z][a-zA-Z0-9_$\-]*)(\s*:)/,cssStyles:["css_label",null]  
  230.     }  
  231.     ];  
  232.     isc.A.multilineTokens=[/(\/\*)|(\*\/)/];  
  233.     isc.defineClass("XMLSyntaxHiliter","SyntaxHiliter");  
  234.     isc.A=isc.XMLSyntaxHiliter.getPrototype();  
  235.     isc.A.regexps=[  
  236.     {  
  237.         regexp:/(<!---->)/,cssStyles:"xml_cdataBlock"  
  238.     }  
  239.     ,  
  240.     {  
  241.         regexp:/(<!---->)/,cssStyles:"xml_comment"  
  242.     }  
  243.     ,  
  244.     {  
  245.         regexp:/(\w+)(=)("(?:[^"]|\\")*")/,cssStyles:["xml_attributeName",null,"xml_attributeValue"]  
  246.     }  
  247.     ,  
  248.     {  
  249.         regexp:/(<\/?)([_:A-Za-z][A-Za-z0-9_.:\-]*)/,cssStyles:[null,"xml_tagName"]  
  250.     }  
  251.     ];  
  252.     isc.A.multilineTokens=[/(<!---->)/,/(<!---->)/];  
  253.     isc._moduleEnd=isc._SyntaxHiliter_end=(isc.timestamp?isc.timestamp():new Date().getTime());  
  254.     if(isc.Log&&isc.Log.logIsInfoEnabled('loadTime'))isc.Log.logInfo('SyntaxHiliter module init time: ' + (isc._moduleEnd-isc._moduleStart) + 'ms','loadTime');  
  255. }  
  256. else  
  257. {  
  258.     if(window.isc && isc.Log && isc.Log.logWarn)isc.Log.logWarn("Duplicate load of module 'SyntaxHiliter'.");  
  259. }  

As you see, those source codes are very clean simple. SyntaxHiliter is an interface that defines the functionality for syntax highlighting. JSSyntaxHiliter, XMLSyntaxHiliter and CSSSyntaxHiliter could be thinking as implementation for JavaScript, XML and CSS.

Reading source code can be fun!

你可能感兴趣的:(JavaScript,Ajax,xml,css,prototype)