http://hax.javaeye.com/blog/850778
回帖时突然想到了:
所有dom原生API都是和此文表述一致。比如addEventLister、removeEventLister;没见过eventLister()根据参数个数确定是add还是remove。
相反,jq当中语法风格都是这样。
问题就变成了,为什么会有这两种相反情况的出现?
不知为何我很想笑……
请去原帖参与讨论。
http://hax.javaeye.com/blog/850778
回帖时突然想到了:
所有dom原生API都是和此文表述一致。比如addEventLister、removeEventLister;没见过eventLister()根据参数个数确定是add还是remove。
相反,jq当中语法风格都是这样。
问题就变成了,为什么会有这两种相反情况的出现?
不知为何我很想笑……
请去原帖参与讨论。
http://jssc.googlecode.com/svn/trunk/jssc5/bin/index.html
已经可以看到添加了不少语言了,找翻译可真够受罪的。通过flashvars中传递lang=param来设置语系,比如说:lang=chinese。
还有检测时添加了对textarea节点的检查,最初也是有的,后来为了标准只检测pre节点,现在发现textarea还是有优势的,比如html代码在pre节点中得转义<号,textarea则不需要。控制权还是交给用户为妙。
另外删除了关于按钮,右上角只保留复制功能,协议改成了MIT。github和google code并存:https://github.com/army8735/jssc
最近始终在考虑5.0的结构设计问题,lexer之间父子继承的关系真可谓一坨坨的:
想要添加新语种十分麻烦,因为要实现这个语言的lexer才行。虽说当初设计的两大目的是准确性和速度性能,但是忽略了易配置性,罪过罪过。UNIX上的Lex做得很不错,每种语言用描述自己的规则文件来定义,值得借鉴,这样即使不懂如何实现lexer的人只要通过配置自己所需的语言规则,也能添加语法种类。
下一代的jssc要提炼出一个lexer核心,这样其它地方也能用这个核心,不必重复实现。现在的做法耦合太严重,解析过程和生成html的逻辑死死绑在一起,当然唯一的好处是性能最快。解耦后将略微损失一些性能,但总体时间复杂度还是O(n)的,再加上加入缓冲区的概念,性能不是问题,最终结果得要远远大于失的。
至于版本号么,5.1?5.5?6.0?算了,还是别学chrome了……
最后,昨天看见syntaxhighlighter更新到3.x了,开源竞争激烈啊,不过准确性和速度性还是不能比的。习惯性地祭出js校验代码(前一部分是我编的,后一部分来源于hax),还是没有根本性的改变:
//javascript
var reg = /**//[\/][/][*]([\S\s]*?)(?:[*][/;\[\]]|$)|[\/][/g;//](.*)|"((?:\\\\|\\"|[^"///])*)"|'((?:\\\\|\\'|[^'\//;])*)'/gm; ''; /\d/.test(1);
reg = 12;
reg = 2 / 1 /**///
/**///
/reg/ 1;
var num1 = 0.541;
function f(test) {
return (test/*
/* //
' "
{ ;
\*/ && // /* // " ' { ; \
test &&
" /* // \
\" ' \
{ ;" &&
' /* // \
" \' \
{ ;' &&
test);
}
http://code.google.com/p/swfobject/
swfobject是目前比较通用的嵌入flash的js库,很多库自己实现的插件也无一例外借鉴了它。不过我的日常工作中很少用到静态方式嵌入flash,都是动态方式调用,因此许多代码是冗余的;而且swfobject虽然针对不同浏览器进行了分支判断,但没有进行“惰性化”,性能没有最优。
举个例子,判断flash的版本,swfobject会在初始化的时候进行,即使没有使用它。很多人会进行改写,使之成为“首次使用时初始化”。我之前也是如此:
(function() {
var SHOCKWAVE_FLASH = 'Shockwave Flash',
FLASH_MIME_TYPE = 'application/x-shockwave-flash',
SHOCKWAVE_FLASH_AX = 'ShockwaveFlash.ShockwaveFlash',
version;
function getVersion() {
var d,
ver = [0, 0, 0];
if(!$.isUndefined(navigator.plugins) && $.isObject(navigator.plugins[SHOCKWAVE_FLASH])) {
d = navigator.plugins[SHOCKWAVE_FLASH].description;
if (d && !(!$.isUndefined(navigator.mimeTypes) && navigator.mimeTypes[FLASH_MIME_TYPE] && !navigator.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
d = d.replace(/^.*\s+(\S+\s+\S+$)/, '$1');
ver[0] = parseInt(d.replace(/^(.*)\..*$/, '$1'), 10);
ver[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, '$1'), 10);
ver[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, '$1'), 10) : 0;
}
}
else if(!$.isUndefined(window.ActiveXObject)) {
try {
var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
if(a) { // a will return null when ActiveX is disabled
d = a.GetVariable('$version');
if (d) {
d = d.split(' ')[1].split(',');
ver = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
}
catch(e) {}
}
return ver;
}
TUI.getVersion = function() {
if(!version) {
version = getVersion();
}
return version;
}
})();
这样做的好处是在第一次获取版本时才去初始化,因为ActiveX的消耗很厉害,而以后再次获取的时候取单例version,避免再次初始化。
但是这不是最优的方案,因为即使这样,每次获取版本的时候还会进行一次if判断,检查是否有初始化过程,访问父层作用域的变量。理想情况应该是直接返回版本,改写方案如下:
(function() {
var SHOCKWAVE_FLASH = 'Shockwave Flash',
FLASH_MIME_TYPE = 'application/x-shockwave-flash',
SHOCKWAVE_FLASH_AX = 'ShockwaveFlash.ShockwaveFlash';
function getVersion() {
var d,
ver = [0, 0, 0];
if(!$.isUndefined(navigator.plugins) && $.isObject(navigator.plugins[SHOCKWAVE_FLASH])) {
d = navigator.plugins[SHOCKWAVE_FLASH].description;
if (d && !(!$.isUndefined(navigator.mimeTypes) && navigator.mimeTypes[FLASH_MIME_TYPE] && !navigator.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
d = d.replace(/^.*\s+(\S+\s+\S+$)/, '$1');
ver[0] = parseInt(d.replace(/^(.*)\..*$/, '$1'), 10);
ver[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, '$1'), 10);
ver[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, '$1'), 10) : 0;
}
}
else if(!$.isUndefined(window.ActiveXObject)) {
try {
var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
if(a) { // a will return null when ActiveX is disabled
d = a.GetVariable('$version');
if (d) {
d = d.split(' ')[1].split(',');
ver = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
}
catch(e) {}
}
return ver;
}
TUI.getVersion = function() {
var version = getVersion();
this.getVersion = function() {
return version;
};
return version;
}
})();
第一次调用时初始化,并且覆盖本身的方法,使之成为直接返回的函数,省略了外部的version变量以及if判断。甚至可以将getVersion方法移到TUI.getVersion中,这便是惰性函数。
这个操作比较简单,分支较少,还体现不出来优势,不如嵌入swf等操作明显,以下是全貌:
(function() {
var SHOCKWAVE_FLASH = 'Shockwave Flash',
FLASH_MIME_TYPE = 'application/x-shockwave-flash',
SHOCKWAVE_FLASH_AX = 'ShockwaveFlash.ShockwaveFlash';
function getVersion() {
var d,
ver = [0, 0, 0];
if(!$.isUndefined(navigator.plugins) && $.isObject(navigator.plugins[SHOCKWAVE_FLASH])) {
d = navigator.plugins[SHOCKWAVE_FLASH].description;
if (d && !(!$.isUndefined(navigator.mimeTypes) && navigator.mimeTypes[FLASH_MIME_TYPE] && !navigator.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
d = d.replace(/^.*\s+(\S+\s+\S+$)/, '$1');
ver[0] = parseInt(d.replace(/^(.*)\..*$/, '$1'), 10);
ver[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, '$1'), 10);
ver[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, '$1'), 10) : 0;
}
}
else if(!$.isUndefined(window.ActiveXObject)) {
try {
var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
if(a) { // a will return null when ActiveX is disabled
d = a.GetVariable('$version');
if (d) {
d = d.split(' ')[1].split(',');
ver = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
}
catch(e) {}
}
return ver;
}
function createSwf(attrs, params, node) {
//ie下需考虑原有节点就是个object(flash)的情况,强行覆盖会致使flash无法正常播放。移除原object再插入新div节点,再动态插入flash到新div节点中。也可防止内存泄漏
if(node.tagName.toLowerCase() == 'object') {
var newDiv = document.createElement('div');
node.parentNode.insertBefore(newDiv, node);
removeObject(node);
createHtml(attrs, params, newDiv);
}
else {
createHtml(attrs, params, node);
}
}
function createHtml(attrs, params, node) {
if($.browser.msie) {
createHtml = createInIe;
}
else {
createHtml = createInNormal;
}
createHtml(attrs, params, node);
}
function createInIe(attrs, params, node) {
var att = '',
par = '';
for(var i in attrs) {
if(attrs[i] != Object.prototype[i]) { //需过滤掉一些潜在因素为prototype添加属性
if(i.toLowerCase() == 'data') {
params.movie = attrs[i];
}
else if(i.toLowerCase() == 'class') {
att += ' class="' + attrs[i] + '"';
}
else if(i.toLowerCase() != 'classid') {
att += ' ' + i + '="' + attrs[i] + '"';
}
}
}
for(var j in params) {
if(params[j] != Object.prototype[j]) { //同上
par += '<param name="' + j + '" value="' + params[j] + '"/>';
}
}
node.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
}
function createInNormal(attrs, params, node) {
var o = document.createElement('object');
o.setAttribute('type', FLASH_MIME_TYPE);
for(var m in attrs) {
if(attrs[m] != Object.prototype[m]) { //同上
if(m.toLowerCase() == 'class') {
o.setAttribute('class', attrs[m]);
}
else if(m.toLowerCase() != "classid") { //过滤掉IE特有属性
o.setAttribute(m, attrs[m]);
}
}
}
for(var n in params) {
if(params[n] != Object.prototype[n] && n.toLowerCase() != 'movie') { //同上
createObjParam(o, n, params[n]);
}
}
node.parentNode.replaceChild(o, node);
}
function createObjParam(el, pName, pValue) {
var p = document.createElement('param');
p.setAttribute('name', pName);
p.setAttribute('value', pValue);
el.appendChild(p);
}
function removeObject(obj, cb) {
//ie下需移除object上的function,以免内存泄漏。移除需要在readyState为4后进行,无法侦听,只能用定时器
if($.browser.msie) {
removeObject = removeObjectInIe;
}
else {
removeObject = removeObjectInNormal;
}
removeObject(obj, cb);
}
function removeObjectInIe(obj, cb) {
obj.style.display = 'none';
(function(){
if(obj.readyState == 4) {
for(var i in obj) {
if($.isFunction(obj[i])) {
obj[i] = null;
}
}
if($.isFunction(cb)) {
cb();
}
return;
}
setTimeout(arguments.callee, 100);
})();
}
function removeObjectInNormal(obj, cb) {
obj.parentNode.removeChild(obj);
if($.isFunction(cb)) {
cb();
}
}
TUI.mix({
/**
* @public 获取flash对象
* @param {string} 对象id
*/
getItem: function(id){
return $.browser.msie ? window[id] : document[id];
},
/**
* @public 改写swfobject设置flash对象
* @param {string} swf对象的url
* @param {string} 动态写入的node的id
* @param {int} width
* @param {int} height
* @param {object} flashvars
* @param {object} param标签参数
* @param {object} object标签参数
*/
setItem: function(url, id, width, height, flashvars, params, attrs) {
var node = document.getElementById(id),
p = {},
a = {};
if(node) {
//将url、width、height混入attrs中,防止原配置参数被修改:这是个很隐蔽的引用修改bug
$.extend(true, a, attrs, {
data: url,
width: width + '',
height: height + ''
});
//没有设置id则继承原有dom元素的id
if(!a.id) {
a.id = id;
}
$.extend(true, p, params);
p.flashvars = $.param(flashvars);
createSwf(a, p, node);
return this.getItem(a.id);
}
},
/**
* @public 移除flash对象
* @param {string/object} 对象id或对象本身
* @param {func} 移除后的回调函数
*/
removeItem: function(id, cb) {
var obj = $.isString(id) ? document.getElementById(id) : id;
if (obj && obj.nodeName.toUpperCase() == 'OBJECT') {
removeObject(obj, cb);
}
},
/**
* @public 获取flash版本,单例闭包
* @return {array} 3位长度的版本号
*/
version: function() {
var version = getVersion();
this.version = function() {
return version;
}
return version;
}
}, 'swf');
})();
http://code.google.com/p/jssc/
昨天偶然同学使用发现ie下无法显示,maxthon下却行,很是郁闷。最后发现是object标签上的visibility:hidden导致,真没想到。于是乎连忙修正之,并用compiler压缩js部分的代码,这样整体又稍稍减少了0.2k。
成员中增加了lubin1119,来尝试编写sas的解析,我本人倒是希望加入python的解析,随后便是拖延很久的缓冲式解析方法,用以支持超多行数代码高亮。看来又要变动不少了。
上篇中外链的一篇分享:http://limu.javaeye.com/blog/840159提到了依赖循环的问题,即模块互相依赖形成了死循环。
举例说明:a->b、b->c、c->a。这种依赖是没法执行的,如果运行的话会引发无限死循环,必须预防。可以考虑的方案无非定义时检测(链接中limu中的方案),或者使用时检测,两者大同小异。
伪代码:
//检测是否有循环依赖
function checkRecursion(list) {
var history = {},
stack;
//遍历需要异步加载的模块列表,每个模块深度遍历检测自己的依赖,出现过的说明此回路已检测过无需重复,记录在history中
list.forEach(function(mod) {
stack = [];
if(!history[mod.name]) {
history[mod.name] = 1;
scanRecursion(mod, {});
}
});
//每个模块深度遍历过程中,边记录边遍历,发现重复的说明出现循环回路,报异常
function scanRecursion(mod, has) {
if(mod && mod.deps) {
stack.push(mod.name);
mod.deps.forEach(function(dep) {
if(has[dep]) {
throw new Error('InfiniteLoop of dependence: ' + stack.join('->'));
}
has[dep] = 1;
history[dep] = 1;
scanRecursion(module[dep], has);
});
//自身完成后需清除
has[mod.name] = 0;
stack.pop();
}
}
}
我用的是使用时检测,防止出现死循环回路,代码都在上面了,没啥好说的。传入的list即是下面3个模块组成的列表,准备一次性并发请求的。做了个测试,成功地抛出了一个详细定位的异常:
$$.def('a', 'a.js', 'b');
$$.def('b', 'b.js', 'c');
$$.def('c', 'c.js', 'a');
$$.use('a');
错误: InfiniteLoop of dependence: a->b->c->a