二月 23rd, 2010

web端语法高亮原理:走进jssc的世界(五)

jssc, 系列文章, by army8735.

整体流程

终于说到整体流程上了。之前的文章一直在解说as中如何来做词法分析,js方面丝毫未提,更没说jssc到底在页面上是怎么工作的。现在,就来详细地解释一下。

环境需求:

  1. 现代浏览器(废话)
  2. 装有Adobe Flash Player插件,版本号为9或9以上
  3. javascript启用
  4. 页面中放入jssc.swf文件

大致流程:

  1. swf文件载入完毕
  2. 内嵌在swf中的js首先被执行,寻找页面中所有符合规则的pre节点
  3. 顺序遍历这些pre节点,提取每个pre节点的文本内容(即原始代码)
  4. 将原始代码传递给swf
  5. swf对传递来的原始代码进行词法分析,并生成一段结果html片段(即一串li节点)
  6. 将结果传递回js
  7. js对结果进行包装(一串li节点放入ol节点中,以及标题头、边距和复制等等)
  8. 将对应的原始pre节点隐藏(display:none)
  9. 将新生成的内容插入原始pre节点的前面

可以看出,js和as耦合的地方在哪里,以及它们之间是如何相互合作的。下面将对以上9个步骤细细说来,希望更多的开发者能提出改进意见~

1.载入swf

这里没什么好说的,在页面底部加入flash标签即可。值得注意的是不同浏览器中标签有所区别,理想情况下可以使用swfobject来插入标准的html标签。不过为了兼容以及简单,一般我是这样做的:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5" style="position:absolute;visibility:hidden;">
	<param name="movie" value="jssc5.swf"/>
	<param name="flashvars" value="find=brush"/>
	<embed src="jssc5.swf" flashvars="find=brush" name="jssc5" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/>
</object>

这里面有许多可配置的地方。

objectidembedname必须一致且唯一(应该是唯二才对),为了满足不同浏览器的需要。我设为jssc5,这也是默认值。如果和页面上某个节点有冲突需要修改,那么就需要传入参数修改配置。比如说变成jssc5_modify,就得在flashvars中这样做:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5_modify" style="position:absolute;visibility:hidden;">
	<param name="movie" value="jssc5.swf"/>
	<param name="flashvars" value="find=brush&swf=jssc5_modify"/>
	<embed src="jssc5.swf" flashvars="find=brush&swf=jssc5_modify" name="jssc5_modify" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/>
</object>

注意到param标签和embed标签中都得改,为了满足浏览器兼容需要……

同样,jssc会占用一个全局js变量名jssc(注意这个jssc是指变量名字符串,而前者是指软件),这是为了在js和as之间互相调用通信必须的。如果这个变量名和页面上的js变量有冲突,那么就会造成错误。所以它也可以进行修改配置,假如要改成jssc_js,就得在flashvars中这么做:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5_modify" style="position:absolute;visibility:hidden;">
	<param name="movie" value="jssc5.swf"/>
	<param name="flashvars" value="find=brush&swf=jssc5_modify&js=jssc_js"/>
	<embed src="jssc5.swf" flashvars="find=brush&swf=jssc5_modify&js=jssc_js" name="jssc5_modify" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/>
</object>

最后,还有两个可以配置的参数:cssurl。前者是指新生成的结果节点的class名称,你可以用firebug看到默认的亦是jssc;后者是指定swf的路径,这是为了在非ie浏览器下做copy使用(ie可以直接调用api做复制功能),从这个意义上说,整个swf文件其实是有两种功能——词法分析和复制按钮。这么做是为了减少http请求,同时js融合在as里也是为了减少http请求。

2.内嵌的js执行

js首先会寻找页面上所有符合规则的pre节点,那么是什么规则的?默认情况下,pre的class名称如果这样开头,js就会认为它是正确的(其实并不一定要求是开头,出现在其它位置也可以,但为了良好的习惯推荐如此):

<pre class="brush:html"></pre>

这段被高亮的代码其实就是它本身。brush键名html键值。假如想要高亮一段js代码,那么就需要更改键值:

<pre class="brush:js"></pre>

其它语言道理相同。另外,可以高亮的代码必须是jssc本身已经支持的语法种类,不支持的话也会被格式化,但没有高亮效果。

细心的人可能已经发现,brush键名和最开始那段代码中出现的相一致。没错,假如pre节点的class名称也有冲突,不能使用brush,要改成brush_modify,那么就要如此修改配置:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="jssc5_modify" style="position:absolute;visibility:hidden;">
	<param name="movie" value="jssc5.swf"/>
	<param name="flashvars" value="find=brush_modify&swf=jssc5_modify&js=jssc_js"/>
	<embed src="jssc5.swf" flashvars="find=brush_modify&swf=jssc5_modify&js=jssc_js" name="jssc5_modify" type="application/x-shockwave-flash" style="position:absolute;visibility:hidden;"/>
</object>

注意param和embed中要修改2次。

在找到所有符合规则的pre节点之后,js将这些节点的引用存储下来,然后一一遍历它们。下面的经过就是每次遍历一个pre节点时所发生的。

3.遍历提取内容

提取pre节点的内容很简单,即我们熟知的innerText。不过在不同浏览器中也有让人头疼的地方。以前在做jssc4的时候,firefox2版本的innerText容量有限制,如果代码过多,提取的内容就不完整。另外也有低版本浏览器不支持innerText的可能性发生。所以综合考虑我写了个如下的js方法:

function getText(node) {
	var code = node.textContent || node.innerText;
	if(!code && node.firstChild) {
		code = node.firstChild.nodeValue;
	}
	return code || "";
}

4.传递内容给as

这是as定义好的接口,js和as可以通过ExternalInterface类来实现互调。不再多说。

5.as做词法分析

这是前几篇一直在说的内容,不再多说。

6.传递结果给js

同第4点。

7.js包装结果

生成的结果其实是一串li节点,js要做的就是创建一个ol节点,然后设置innerHTML为传递来的结果字符串。如此最基本的高亮代码就完成了。之后是设置标题、起始行数、边距、复制功能和鼠标移入高亮当前行背景。

其中边距是指ol节点的paddingLeft值,因为代码行数不一样,所以左边距一定是根据行数动态进行计算的。在js中我的公式如下:

oOl.style.paddingLeft = Math.max((line.length + 2) * 9, 30) + "px";

8.隐藏pre节点

最后是将本次循环的这个pre节点隐藏。注意,这里是隐藏不是删除!因为后面还要用到代码复制功能,所以只能隐藏不能删除!

9.插入结果

将包装好的结果插入本次循环pre节点的前面,这轮循环就算走完了,再继续下一轮循环。

结束语

好了,整个系列文章算结束了。欢迎大家提出好的建议和想法,或者直接参与到开发当中来,亦或以别的方式来参与(比如说做wordpress的插件)。如果你能做出比jssc更好的高亮插件,那么我也会放弃jssc,投入新的、更好的项目中去的——这话说的好假:)

Back Top

回复自“web端语法高亮原理:走进jssc的世界(五)”

  1. 因为对前端不太懂,前面基本跳过了。。。
    大赞最后一段~~
    p.s. 如我做个别的项目,跟前端无关的,你参与不?

  2. @Robin: 你做java我又不会……

  3. 虽然不是全明白,但是还是了解了语法高亮如何实现的概貌了~

  4. 干得漂亮! 学习了,打算周末用到自己的博客上去.

    另外想链接你的博客,不出声我就当默许了哈~

  5. lgpl本来就可以免费使用的……

  6. 哦,许可证这个我知道,我的意思是说 会在我的博客上加一个指向你这里的链接。。。
    我觉得加别人的链接还是说一下的好。

  7. @cjAsp: 忘记说了,IE7在你站上的可以显示,但是下载的那个页面就无法有效果,FF工作正常

  8. @cjAsp: 不好意思,刚才对swf的src改为绝对路径后IE7就可以显示了。src=/jssc5.swf ,flashvars=”find=brush&url=/jssc5.swf”

  9. @cjAsp: 路径问题我也经常会弄混。

  10. 现在只是代码的显示,如果想运行代码呢?能不能加上切换到可以运行代码的按钮呢?

  11. @cjAsp: 不可能,php、c你怎么在网页上运行?css和html的要做也是自己实现为妙。

  12. 发现您一个 bug
    oOl.style.paddingLeft = Math.max((line.length 这个地方应该将起始行号 start 计算在内。
    如果我使用 3024开头 小于10行 那么只能看到24
    建议:
    加一个判断逻辑,如果start存在 写一个算法计算 整数位数 style.paddingLeft 适当值

  13. @army: 我已经用js解决了,看这里
    http://www.archn.cn/forum.php?mod=viewthread&tid=4&page=1&extra=#pid11
    第二帖,as我不怎么会,希望您能给弄个改好的swf,谢谢!
    arc@archn.cn

  14. @army: 如果标题有背景的话,您的“复制”按钮在 ff和谷歌下不能透明!,请指教……

  15. 我也没搞清楚为什么,wmode已经是透明了……

  16. 搞定了,原来是value拼错了,拼成values……

  17. 是最新的版本吗?

  18. 怎么
    jssc5.1alpha.7z 上面是jssc5.0.7.7z
    这两个版本有什么区别?

  19. 对于今后可能出现一些不支持flash的浏览器,html5后逐步有这个趋势,您的是否考虑推出不一样的版本来应对?

  20. 5.1是开发版……

  21. 等js性能在browser普及才有可能

  1. web端语法高亮原理:走进jssc的世界(四) ­ army8735 (,2010年03月7日)

    [...] 这篇写得可能有点简单,因为的确是比较抽象的东西。我也偷偷懒,相信能做到前章所提的词法分析的情况下,纯理论来读本篇也不是什么难事了。可能直接读我的源代码反而会更容易些。在下一篇当中,我会介绍as和js的交互以及jssc的大概处理流程,它将作为结束篇章。 [...]

发表回复

Back Top