腾讯微博API OAuth验证及微博发送 (JavaScript)

首先, 道路是曲折的… 之前曾小试过 Basic 验证, 一开是也是不得要领, 不过悟透那个就显然快多了.

这是第一次做 OAuth 的验证, 在此实在想狠狠地骂腾讯的 API 说明文档, 不清不楚, 害得我到处查, 到处试… 总算, 经过一个上午的奋斗, 成功获取了 Request Token, 下午再稍加努力, 终于把 Access Token 拿到了手, 晚上又继续奋战, 搞定了微博发送, 至此, 也算对 OAuth 及腾讯微博的 API 有了个基本的了解.

JavaScript 实现腾讯微博的 OAuth 的难点在于 HMAC-SHA1 加密, 而且之后还得转换成 Base64, 最变态的是, 获取 Request Token 的时候, 加密时 AppKey 后面还得跟上个 “&”!!! 比如原来 AppKey 是 “123456”, 那么加密时的 Key 就是 “123456&”

好吧, 既然大家在看这篇文章, 相信关于 XMLHttpRequest 这些也比较熟悉了, 我就说说重点 (因为我是第一次真正接触 OAuth, 所以对里面一些约定俗成的东西可能还不清楚, 所以高手看了不要笑我这些 “重点” 太幼稚).

HMAC-SHA1 加密的 JavaScript 实现

在网上搜这个搜了很久, 最后找到了一个叫 Crypto-JS 的东西, 一看, 果然是好家伙, 里面提供了非常丰富的加密方式, 值得收藏. http://code.google.com/p/crypto-js/ 下载后引用 crypto.js, sha1.js, hmac.js 三个文件, 加密方式如下:

var message = “Message to be encoded”;
var secret = “Secret phrase”;

var bytes = Crypto.HMAC(Crypto.SHA1, message, secret, { asBytes: true }) ;
//但仅仅这样还不行, 还得转换成Base64.
//在Crypto.util下找到相应的方法.
var signature = Crypto.util.bytesToBase64(bytes);

这样就搞定啦. 需要注意的是 (前面我也有提到), 在获取 Request Token 的时候, secret需要在 App Secret 后面加上 “&”. 获取 Access Token 的时候, 则是用 “&” 把 App Secret 和 Token Secret连起来.

参数编码

按说这个是算不上重点的, 不过实在找不到东西写, 就把这个算上去吧.

方法很简单, 使用JavaScript内置的encodeURIComponent()方法即可.

看来这里还真是重点, 编码要求要什么 [RFC3986], 在 encodeURIComponent() 的基础上, 还要额外编码 ! * ( ) ‘ 这5个字符.

否则当添加微博的内容中出现这些字符的时候, oauth_signature 参数会有误, 导致鉴权失败.

时间戳

注意单位是哦! new Date().getTime()的单位是毫秒, 所以注意先除以1000再取整.

要签名的Base String

里面的参数要排序, 当然这个文档中也强调了. 另外就是这些参数中是不包括 oauth_signature 的. 显然, 也没法包含这个参数, 不过我这样点明下, 免得大家心生疑惑.

 

最后, 这个是我用 HTA (JavaScript) 实现的一个验证和发微博的小Demo, http://www.vilic.info/demo/vt.rar . 源码是基于 vejis 的, 如果有看不太明白的可以参考 vejis.js 文件中的说明. (补充, 后来发现有个小Bug, 其实不算Bug, 我一开始以为所有的API都可以用POST方法, 就全部使用的是POST, 对应的稍微小改就可以了)

原来JavaScript也可以有属性的get和set

今天在群里和大大们讨论问题, 顺便就谈到了开发 Prever 2 的时候遇到的一个. 归结下来, 就是希望能保护一些对象, 使之不能被修改.
后来Franky大神提到一个方法, Object.defineProperty, 据说是IE8部分支持, 其他浏览器的最新应该都是支持的. 因为我现在不求兼容很多浏览器, 所以对我来说, 这自然是非常有用的东西了.
查了查MSDN和MDC, 解释如下(翻译得不恰当的地方请见谅):

语法

Object.defineProperty(obj, prop, descriptor)

参数

obj
要定义属性的对象
prop
要修改或者定义的属性名称
descriptor
属性定义和修改的描述符

描述

这个方法允许对象属性的严格添加和修改. 普通的属性添加创建的属性可以在属性枚举(for…in 循环)中枚举, 这时候, 值可能被改变或者删除. 这个方法允许修改这些额外的特性(即可改变或可删除).

属性描述符是对象形式的, 它可以由两种方式表示: 数据描述符和访问描述符. 数据描述符是一个有值, 可写或者不可写的属性. 访问描述符是通过一个getter和setter函数对来描述的. 一个描述符必须是这两种之一, 不能掺杂. 这两种描述符都可以使用configurable和enumerable.

一个属性描述符是一个具有以下字段的对象:

value
属性的值. (仅限数据描述符). 默认为undefined.

writable
如果值为true, 属性的值可以被改变. (仅限数据描述符). 默认是false.

get
属性的getter, undefined表示没有getter. (仅限访问描述符). 默认是undefined.

set
属性的setter, undefined表示没有setter. (仅限访问描述符). 默认是undefined.

configurable
如果值为true, 则该属性的描述符可以被修改并且该属性可以从相关对象上删去. 默认为false.

enumerable
如果只为true, 则该属性可以从相关对象上枚举. 默认是false.

JavaScript 能有多快?

最近想玩一玩HTML5, 特别是canvas和CSS3的3D transform. 不过3D transform貌似只有Safari支持比较正常, 之前的Chrome也可以, 现在不知道怎么的好像不行了. 那自然就把重心落到了canvas上.

想用canvas实现一些3D的效果, 但却无从下手. 一方面是因为自己算法方面的知识匮乏, 另一方面, 则是不清楚JavaScript究竟能有多快. 毕竟一牵涉到图形处理, 计算量就摊在了那里.

各大浏览器都陆陆续续地支持硬件加速了, 所以渲染应该不是问题, 今天便做了个小测试, 想看看JS的效率. 内容比较简单:

//JavaScript:
var l = 100000000;
var o = [];
for (var i = 0; i < l; i++) {
    if (o.length == 100000)
        o.length = 0;
    o.push(i);
}

//C++ (vector)
int l = 100000000;
for (int i = 0; i < l; i++) {
    if (vct.size() == 100000)
        vct.clear();
    vct.push_back(i);
}

//C++ (原生数组)
int vct[100000];
int vi = 0;
int l = 100000000;
for (int i = 0; i < l; i++)
{
    if (vi == 100000)
    {
        vi = 0;
        memset(vct, 0, sizeof(int) * 10000);
    }
    vct[vi] = i;
}

最后, C++ vector耗费的时间最长, 接近一分钟; JavaScript Array在Chrome 10中耗时3s多一点; C++原生数组最快, 不到1s.

当然, 我也不指望JavaScript的Array能快过C++的原生数组, 但取得了这样的成绩也是令人惊讶的. 原来一直觉得脚本语言和被编译为机器码的语言速度上肯定不在一个数量级, 而且可能会差很多个数量级, 现在看来, 似乎并非如此. 虽然可能一个for循环和一个数组操作没有暴露JavaScript速度上的弱点, 但也足以改变我的看法. JavaScript速度的提升, 也意味着Web与我们生活的又一次靠近!

JavaScript 轻松实现强大的Cookies操作

刚刚群里有人问到JavaScript的Cookies操作, 于是想到之前也写过一个, 刚刚一搜, 是去年8月份的事情了… 不过功能还算不错, 而且使用方便, 于是整理一下, 献上:

<!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>
    <title></title>
    <script src=”http://www.vilic.info/vejis/vejis.1.0.0.js” type=”text/javascript”></script>
    <script src=”http://www.vilic.info/vejis/cookies.1.0.0.js” type=”text/javascript”></script>
    <script type=”text/javascript”>
        /*
        cookies[String name] 获取cookies集合中名为name的Cookie的值.
        cookies.clear() 清除所有Cookie, 并更新cookies集合, 无返回值.
        cookies.del(String name) 删除相应名称的Cookie, 并更新cookies集合, 无返回值.
        cookies.del(Array names) 批量删除相应名称的Cookie, 并更新cookies集合, 无返回值.
        cookies.get(String name) 更新cookies集合, 获取相应名称的Cookie的值, 返回值为String.
        cookies.refresh() 更新cookies集合, 无返回值.
        cookies.set(String name, String value) 设置相应名称的Cookie的值, 并更新cookies集合, 无返回值.
        cookies.set(String name, String value, Date date) 设置相应名称的Cookie的值与过期时间, 并更新cookies集合, 无返回值.
        cookies.set(Array names, Array values) 批量设置相应名称的Cookie的值, 并更新cookies集合, 无返回值.
        */

        if (cookies[“abc”] == undefined) {
            alert(“set cookie”);
            cookies.set(“abc”, “123”);
        }

        alert(cookies[“abc”]);
    </script>
</head>
<body>

</body>
</html>

相关文件:
http://www.vilic.info/vejis/vejis.1.0.0.js
http://www.vilic.info/vejis/cookies.1.0.0.js

JavaScript 实现函数重载及类的多构造函数

这个是之前一篇 (http://www.vilic.info/blog/archives/610) 的升级版, 添加了更强的对 “类” 的支持, 取消了Null类型, 顺便修正了一些bug.

脚本链接: http://www.vilic.info/vejis/vejis-mo.js

然后是用法:

/* 函数的用法 */

//创建一个函数并定义重载1
//这里也可以使用 var fn = _(); 创建空函数, 并在以后添加重载
var fn = _(function () {
    alert(“No arguments!”);
});

//重载2
fn._(String, function (str) {
    alert(“You got me a string: ” + str + “.”);
});

//重载3
fn._(String, Integer, function (str, n) {
    alert(“The string is: ” + str + “; and the integer is: ” + n + “.”);
});

fn();
fn(“test1”);
fn(“test2”, 123);

/* 类的用法 */
//定义一个类
var C = class_(function () {
    var x;

    //成员函数
    this.getX = _(function () {
        return x;
    });

    /* 也可无构造函数 */
    //构造函数重载1
    C._(Integer, function (i) {
        x = “Integer: ” + i;
    });

    //构造函数重载2
    C._(String, function (s) {
        x = “String: ” + s;
    });
});

//创建两个C的实例
var c1 = new C(“abc”);
var c2 = new C(123);

alert(c1.getX());
alert(c2.getX());

JavaScript for…in 语句的最佳用法

大家对JavaScript里的for…in语句一定不陌生吧, 不过, 这个语句有时会做一些我们可能不想它做的事. 比如:

Object.prototype.test = function () { };

var obj = {
    a: “aaa”,
    b: “bbb”
};

//需要说明的一点是, 这里的i类型为string, 即使是对于数组, 也同样如此.
for (i in obj)
    alert(obj[i]);

结果发现test方法也跑出来. 为了避免这样的情况, 我们通常做简单的处理:

for (i in obj)
    if (typeof obj[i] != “function”) 
        alert(obj[i]);

不过如果枚举的类型里也有function呢? 所以, 提出我认为的最佳方案:

for (i in obj)
    if (obj.constructor.prototype[i] === undefined)
        alert(obj[i]);

JavaScript完美实现函数重载

今天Google了一下JS的函数重载, 居然没有发现自己的文章, 结果在博客内搜索了下, 才发现是SEO的问题.

于是, 在这里贡献出暑假的时候写的JS函数重载, 其实它本身是自己的类库vejis的一部分, 不过vejis估计短时间内不会完善了, 所以现在把代码单独提出来, 供大家使用. 至少这是目前我见过的最爽的JS函数重载.

先看示例代码:

<!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>
    <title>JavaScript完美实现函数重载</title>
    <script src=”vejis-mo.js” type=”text/javascript”></script>
    <script type=”text/javascript”>
        /*
            这个函数重载代码是vejis库的一部分, 现在单独提取出来分享.
            vejis命名空间下有两个特殊的类型, Integer和Null, 在调用vejis.use(vejis)后即可直接使用.
        */

        /*
            使用vejis命名空间, 将目标对象的方法和属性拷贝到window对象, 第二个参数可选, 指示是否覆盖.
            Method, _等都是vejis的成员.
        */

        vejis.use(vejis, true);

        //定义一个方法
        var test = new Method();
       
        //重载, 1个String类型的参数
        test.overload(String, function (msg) {
            alert(msg);
        });
       
        //重载, 1个Integer类型的参数
        test.overload(Integer, function (n) {
            alert(“The integer given is ” + n + “.”);
        });

        test(“Hello, Vejis!”);
        test(57);

        //以下为简写
        var test2 = _(function () {
            alert(“No arguments given.”);
        });

        test2._(Object, String, function (obj, str) {
            alert(“You’ve given me a Object and a String!”);
        });

        //自定义的类型
        test2._(MyClass, function (mc) {
            alert(mc.description);
            mc.sayHello();
        });

        test2();
        test2({}, “”);
        test2(new MyClass());

        function MyClass() {
            this.description = “This is my class.”
            this.sayHello = _(function () {
                alert(“Hello! I am MyClass!”);
            });
        }
    </script>
</head>
<body>
</body>
</html>

木哈哈, 是不是很爽~ 其实这里不止可以实现JS重载普通的函数, 还可以重载类型, 慢慢发掘吧~

脚本在这里, 当然, 因为只是提取的函数重载部分, 其他的我能删的都删了:

http://www.vilic.info/vejis/vejis-mo.js

2011.3.13 文件有改动, 详见: http://www.vilic.info/blog/archives/638

JavaScript RegExp 正则表达式g模式的使用

说明下, 文章中的有些东西来自Franky大神的文章<浏览器中的 正则表达式陷阱>.

试考虑如下代码:

var re = /^\w$/g;
re.test(‘a’); //返回true
re.test(‘b’); //还是true吗?

猜猜返回值都是什么呢? 如果你已经阅读了Franky大神的那篇文章, 或者自己动手试了下, 就会发现, 一次是true, 一次是false. 原因在于正则表达式中的g, 使得搜索过程后, 如果匹配成功, 则记录上一次的位置, 如果匹配不成功, 则会归零. 所以, 如果在上面的语句中再加一条re.test(‘s’), 那么返回的将又会是我们期望的true了.

不过一般情况下, 我们自然会希望得到同样的结果, 这个时候可以在其中添加一条语句, 人工将位置归零, 防止这个 “错误” 的发生:

var re = /^\w$/g;
re.test(‘a’); //返回true
re.lastIndex = 0; //归零搜索的位置
re.test(‘b’); //返回true

或者我们可以更简单地直接将g去掉:

var re = /^\w$/;
re.test(‘a’); //返回true
re.test(‘b’); //返回true

于是, 利用g模式的这个性质, 可以这样使用. 考虑下面的代码:

var re = /\w/g; //注意, 我将分别表示开头的^和$去掉了
re.test(‘ab’);
re.test(‘ab’);

猜猜现在会返回什么呢? 答案是两个true. 用之前说到的东西也很好解释, 因为第一次记录了一个lastIndex, 但是在这个lastIndex的情况下, 同样能匹配成功第二个. 也就是说, 第一次匹配的是字符串中的a字母, 第二次则是b字母. 我在这里刻意地使用了两个相同的字符串, 因为我想告诉大家, 这才是g模式下, 正确的用法. 下面我们继续讨论这种机制存在的意义, 我也直接切入要害. 考虑下面的代码:

var re = /\d+/g;
var str = ‘1## %$xx 34*&920 3’; //包含了数字的字符串

var arr = [];

while (re.test(str))
    arr.push(RegExp.lastMatch);
/*
    上面的代码我更愿意写成:
    var parts;
    while (parts = re.exec(str))
        arr.push(parts[0]);
    这样可以避免全局的RegExp对象造成的一些问题.
*/

alert(arr); //”1,34,920,3″

点到这里, 相信大家已经初步明白这种机制的用意了,它方便自定义的遍历 (当lastIndex已经达到字符串末尾时, 并不归零), 要知道, 在明白这点之前, 我一直用着replace来完成这一步. 现在, 大家可以用自己的循环来搞定了. 这种情况也提醒了我们, 要正确地使用g参数, 像前面那种正则中包含^或者$的加上g参数, 就是完全的画蛇添足, 这些是应当避免的.

当然, 以上也只是Vilic的个人认识, 欢迎大家指正!

Vlight 开发笔记 2010.8.19

Github https://github.com/vilic/vlight

昨天主要是添加了HTML代码高亮和CSS代码高亮, 并且自动识别HTML/CSS/JS. 同时也能高亮HTML中嵌套的CSS和JS. 有些判断并不是很严谨, 但是多数情况下是适用的. 下面分别是HTML/CSS/JS代码示例.

HTML代码:

<!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>
    <!-- HTML Code -->
    <title>Vlight Demo</title>
    <style type="text/css">
        /* CSS Code */
        body { margin: 0px; line-height: 16px; font-size: 12px; font-family: Microsoft Yahei, Arial; }
        #test_div { height: 40px; }
        .test { background-color: #FFFFFF; }
    </style>
    <script src="vejis.js" type="text/javascript"></script>
    <script src="vlight.js" type="text/javascript"></script>
    <script type="text/javascript">
        /* JavaScript Code */
        vejis.use(vejis, true); //use vejis namespace
        _event.add(window, 'resize', resize); //add an event

        function resize() {
            var width = document.body.offsetWidth;
            alert(width);
        }
    </script>
</head>
<body>
    <h1>Vlight Demo</h1>
    <div id="test_div">
        <a href="http://www.vilic.info/" title="VILIC's Blog">VILIC's Blog</a>
        <img alt="" src="http://www.vilic.info/xxx.jpg" />
        <div></div>
    </div>
</body>
</html>

独立的CSS代码:

/* CSS Code */
body { margin: 0px; line-height: 16px; font-size: 12px; font-family: Microsoft Yahei, Arial; }
#test_div { height: 40px; }
.test { background-color: #FFFFFF; }

独立的JavaScript代码:

/* JavaScript Code */
vejis.use(vejis, true); //use vejis namespace
_event.add(window, 'resize', resize); //add an event

function resize() {
    var width = document.body.offsetWidth;
    alert(width);
}

var multilineStr = "abc\
def\
ghi";

Vlight & Vejis 开发笔记 2010.8.18

Github https://github.com/vilic/vlight

同志们可能已经发现了, 现在有代码高亮了.

这个属于心血来潮, 突然想到写一个, 然后就写了一个, 不过后来兼容IE6还是费了点事. 最后在样式上也动用了JS. 不过顺道, 也进一步完善了Vejis (Vlight需要Vejis). 代码不多, 去掉注释的话只有80+行. 所以直接贴出来, 也顺道测试下.

/*
Vlight JS Code Highlight (Style from Visual Studio 2010)

Version 0.3
Vejis JavaScript Library 0.0.0.5 is needed.

By Vilic Vane
http://www.vilic.info/

©2010 Groinup Studio
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:
Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
Redistributions in binary form must reproduce the
above copyright notice, this list of conditions and
the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the Groinup Studio nor the names
of its contributors may be used to endorse or promote
products derived from this software without specific
prior written permission.
*/

vejis.ready(function () {
    var className = 'vlight', //自定义类名
        maxLineCount = 20, //最多同时显示的行数
        lineHeight = 16, //行高
        scrollBarWidth = 24, //预计滚动条宽度
        cssText = //CSS内容
        'div.vlight { position: relative; margin: 5px 0px; border: solid 1px #FFF3D0; line-height: ' + lineHeight + 'px; color: #000000; font-size: 12px; font-family: Courier New, monospace; white-space: nowrap; overflow: hidden; }' +
        'div.vlight div.vlight_top { height: 5px; background-color: #FFE8A6; font-size: 0px; }' +
        'div.vlight div.vlight_left { position: absolute; width: 65px; left: 0px; text-align: right; color: #2B91AF; overflow: hidden; }' +
        'div.vlight div.vlight_left div { position: relative; width: 40px; left: 0px; padding-right: 5px; border-left: solid 16px #F0F0F0; border-right: solid 4px #6CE26C; }' +
        'div.vlight div.vlight_main { position: relative; margin-left: 65px; padding-left: 5px; overflow-x: scroll; overflow-y: auto; }' +
        'div.vlight span.vlight_comm { color: #008000; }' +
        'div.vlight span.vlight_re { color: #000000; }' +
        'div.vlight span.vlight_str { color: #800000; }' +
        'div.vlight span.vlight_key { color: #0000FF; }';

    cssText = cssText.replace(/vlight/g, className); //替换类名
    vejis.html.createStyleSheet(cssText); //创建样式

    var eles = vejis.html.getElementsByClassName(className); //获取元素
    var spanl = '<span>',
        spanr = '</span>';

    //处理每一个类名符合的元素
    vejis.foreach(eles, function (ele) {
        var div = document.createElement('div');
        div.className = className;
        div.innerHTML =
        '<div></div>' +
        '<div></div>';

        var top = div.childNodes[0],
            left = div.childNodes[1];

        var main = document.createElement('div');
        main.className = className + '_main';

        var oText;
        if (ele.tagName == 'TEXTAREA') oText = ele.value; //如果是textarea则直接取value
        else if (ele.tagName == 'PRE') oText = ele.innerText || ele.innerHTML;
        else oText = ele.innerHTML.replace(/\r?\n/g, '').replace(/<p( [^>]*)?>([\s\S]*?)<\/p>/gi, '$2\r\n\r\n').replace(/<div( [^>]*)?>([\s\S]*?)<\/div>/gi, '$2\r\n').replace(/<([a-z]+)( [^>]*)?>([\s\S]*?)<\/\1>/gi, '$3').replace(/<br[^>]*>/gi, '\r\n').replace(/&nbsp;/g, ' ').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');

        var result = convert(oText); //转换文本到高亮的HTML
        main.innerHTML = result.html;
        div.appendChild(main);

        //创建行号
        var lines = ''
        for (var i = 1; i <= result.count; i++)
            lines += i + '<br />';
        left.innerHTML = '<div>' + lines + '</div>';

        //将原来的元素替换成代码高亮区域
        ele.parentNode.replaceChild(div, ele);

        //设置高宽
        left.style.height = main.style.height = lineHeight * (result.count > maxLineCount ? maxLineCount : result.count) + scrollBarWidth + 'px';
        left.childNodes[0].style.height = result.count * lineHeight + scrollBarWidth + 'px';

        //绑定事件
        vejis._event.add(window, 'resize', resize);
        vejis._event.add(main, 'scroll', scroll);

        resize(); //初始化

        function resize() {
            try {
                main.style.width = top.offsetWidth - left.offsetWidth - 5 + 'px';
            } catch (e) { }
        }

        function scroll() {
            left.childNodes[0].style.marginTop = -main.scrollTop + 'px';
        }
    });

    function convert(text) {
        var names = ['comm', 're', 'str', 'key']; //类名的后缀

        //全局正则表达式
        var globalRE = /((\/\*[\s\S]*?\*\/|\/\/.*)|('('|.*?([^\\]'|\\\\'))|"("|.*?([^\\]"|\\\\")))|\/(\/|.*?([^\\]\/|\\\\\/))[gim]{0,3}|([^\w]|^)(break|delete|function|return|typeof|case|do|if|switch|var|catch|else|in|this|void|continue|false|instanceof|throw|while|debugger|finally|new|true|with|default|for|null|try)(?=[^\w]|$))/g;
       
        //拆分开的正则表达式
        var res = [
            /^(\/\*[\s\S]*?\*\/|\/\/.*)$/,
            /^\/(\/|.*?([^\\]\/|\\\\\/))[gim]{0,3}$/,
            /^'('|.*?([^\\]'|\\\\'))|"("|.*?([^\\]"|\\\\"))$/,
            /(break|delete|function|return|typeof|case|do|if|switch|var|catch|else|in|this|void|continue|false|instanceof|throw|while|debugger|finally|new|true|with|default|for|null|try)$/
        ];

        text = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); //符号处理

        //匹配
        text = text.replace(globalRE, function (s) {
            for (var i = 0; i < res.length; i++) {
                if (!res[i].test(s)) continue;
                var spanl2m = spanl + names[i] + spanm;

                s = s.replace(res[i], function (s) {
                    return spanl2m + s + spanr; //加标签
                });
                return s;
            }
        });

        var count = 1; //行数

        //字符处理
        text = text.replace(/\t/g, '    ').replace(/  /g, '&nbsp; ').replace(/  /g, ' &nbsp;').replace(/(\r?\n)+$/g, '').replace(/\r?\n/g, function () { count++; return '<br />'; });

        return { html: text, count: count };
    }
});

Vejis主要改进是在事件处理上(没有完全测试过, 所以可能还有大量bug), 琐碎的就不说了, 上代码:

var _event = this._event = new function () {
    var objects = [];
    var holders = [];

    this.add = _(Object, String, Function, function (obj, name, callback) {
        name = name.toLowerCase();

        var index = array.indexOf(objects, obj),
            subHolder;

        if (index < 0) {
            index = objects.length;
            objects.push(obj);
            var holder = {};
            holders.push(holder);
            createListener(holder);
        }
        else {
            var holder = holders[index];
            subHolder = holder[name];
            if (subHolder) {
                if (array.exists(subHolder, callback)) return false;
                subHolder.push(callback);
            }
            else createListener(holder);
        }

        return true;

        function createListener(holder) {
            var subHolder = holder[name] = [callback];
            subHolder.handler = handler;
            if (obj.addEventListener) obj.addEventListener(name, handler, false);
            else if (obj.attachEvent) obj.attachEvent('on' + name, handler);
        }

        function handler(ev) {
            var e = window.event || ev;
            onEvent(e, obj, name);
        }
    });

    this.remove = _(Object, String, Function, function (obj, name, callback) {
        name = name.toLowerCase();
        var index = array.indexOf(objects, obj);
        if (index < 0) return false;
        else {
            var holder = holders[index],
                subHolder;
            if (holder && (subHolder = holder[name])) {
                var idx = array.indexOf(subHolder, callback);
                if (idx < 0) return false;
                else {
                    array.removeByIndex(subHolder, idx);
                    if (!subHolder.length) {
                        var handler = subHolder.handler;
                        if (obj.removeEventListener) obj.removeEventListener(name, handler);
                        else if (obj.detachEvent) obj.detachEvent('on' + name, handler);
                        delete holder[name];
                        if (object.isEmpty(holder)) {
                            array.remove(holders, index);
                            array.remove(objects, index);
                        }
                    }
                    return true;
                }
            }
            return false;
        }
    });

    function onEvent(e, obj, name) {
        var index = array.indexOf(objects, obj);
        var callbacks = holders[index][name];

        foreach(callbacks, function (callback) {
            callback(e);
        });
    }
} ();