十月 30, 2006

实践中有时候需要两个浏览器窗口之间能进行简单的通讯,已有的解决方案是使用window.opener与使用window.open返回的一个Handler进行窗口之间的通讯,某种意义上解决了窗口之间通讯的问题,可是这样的方法局限在进行通讯的任何一个窗口都不能进行刷新动作,否则的话,Handler就会丢失,已有的通讯就会被破坏。

当然,使用js进行任意窗口之间稳定的通讯也是有方法的,就是使用cookie这种浏览器本地信息存储的机制来解决问题。其核心方法如下,仿照传统C/S通讯类似的方式,分为Client/Server两个部分:

Server部分:

  • 在Cookie中创建两个字段xxx_HB,xxx_MSG。xxx_HB存储服务器上一次heartbeat发生的时间,xxx_MSG负责存储Client发送给Server的消息。
  • 每次heartbeat发生的时候都即时更新xxx_HB内的时间戳,heartbeat之间的时间间隔应该尽可能小。
  • 发生heartbeat时,如果发现xxx_MSG中有客户端发送的消息,则取走该消息,清空xxx_MSG字段,并且将消息发送给Server的on message event handler
...
var CookieServer = function(address,messageCallback){
	this._address = address;
	this._heartBeatKey = address + '_HB';
	this._messageKey = address + '_MSG';
	this._heartBeatTimer = null;
	this.onMessage = messageCallback;
}
CookieServer.prototype.listen = function(){
	if(readCookie(this._heartBeatKey) != null){
		throw('There is exists a server at this address!');
	};
	this._heartBeatTimer = setInterval(this._heartBeat.bind(this),10);
}
CookieServer.prototype._heartBeat = function(){
	createCookie(this._heartBeatKey,(new Date()).getTime().toString(),100);
	var value = readCookie(this._messageKey);
	if(value == null || value ==  '')
		return;
	eraseCookie(this._messageKey);
	this.onMessage(value);
}
...

Client部分:

  • 每次调用Client对象的send方法时,通过构造Client对象时传入的xxx参数,检测xxx_HB与当前时间的间隔,如果该间隔大于每次Server heartbeat发生的间隔越10倍左右(出于稳定性考虑),则判定Server timeout。
  • 如果Server is online,则将xxx_MSG字段置为消息内容,模拟向服务器发送消息的过程。

 

var CookieClient = function(address){
	var server = {};
	server._address = address;
	server._heartBeatKey = address + '_HB';
	server._messageKey = address + '_MSG';
	this.server = server;
	var value = readCookie(this.server._heartBeatKey);
	if(value == null || value == ''){
		throw('There is not exist a server at address: ' + address);
	}
}
CookieClient.prototype.send = function(message){
	var timeSpan = (new Date()).getTime() - parseInt(readCookie(this.server._heartBeatKey));
	if(timeSpan > 1000){
		eraseCookie(this.server._heartBeatKey);
		eraseCookie(this.server._messageKey);
		throw('Server at address: ' + this.server._address + ' is time out!');
	}
	createCookie(this.server._messageKey,message,100);
}

对于C/S任何一方发生“通讯故障”的情况,代码中都有处理,就不再多说了,具体的可以看实际的代码以及演示

最后补充一下,使用这个通讯模块,需要prototype.js的支持,因为用到了其中的Function.bind()方法。

 

Technorati: , , ,

十月 25, 2006

之前的一篇post里面,我说过准备实现一种share memory object,实际上就是一种fetch data ondemand dynamic object,使用一种对于客户端代码透明的方式来在需求数据的时候才请求服务器。

我的目标是越接近javascript操纵object的自然方式越好,最方便的就是当代码请求object.property的时候对服务器发出请求。要实现这种目标,无可回避的就要直接面对实现一种object property getter and setter的功能。据google所知,是没有一种在IE下面原生支持getter与setter的方法的,所以,我就学习了java中的使用对应的getProperty()与setProperty()的方法来实现这种功能,核心代码如下:

var ObjectType = {
	STRING : 0x01,
	NUMBER : 0x02,
	ARRAY : 0x03,
	DATE : 0x04,
	OBJECT : 0x99
}

var DynamicObject = function(/*Object*/tplObj){
	/* tplObj is a 'template object' for construct object getter and setter*/
	if(tplObj == null)
		throw('Template object couldn not be null !');
	for(var prop in tplObj){

		switch(this._getType(tplObj[prop])){
			case ObjectType.STRING:
				this['_'+prop] = new String('');
			break;
			case ObjectType.NUMBER:
				this['_'+prop] = new Number(0);
			break;
			case ObjectType.DATE:
				this['_'+prop] = new Date();
			break;
			case ObjectType.OBJECT:
				this['_'+prop] = new Object();
			break;
			case ObjectType.ARRAY:
				this['_'+prop] = new Array();
			break;
		}

		var strGetter =
'this["get' + prop + '"] = function(){return this._invokeGetter("_'+prop+'");}';
		var strSetter =
'this["set' + prop + '"] = function(value){return this._invokeSetter("_'+prop+'",value);}';
		eval(strGetter);
		eval(strSetter);

	}
}

DynamicObject.prototype._invokeGetter = function(prop){
	//alert(prop+':'+this[prop]);
	return this[prop];
}
DynamicObject.prototype._invokeSetter = function(prop,value){
	//alert('set '+prop+'='+value);
	this[prop] = value;
}

当然 ,以上是一个跨平台的setter getter解决方案,虽然在mozilla下面可以使用__defineGetter__这种方法解决,但是 IE并不接受这种做法。具体使用的时候,如果需要构建一个属性具有getter setter特性的对象,需要:

var obj = new DynamicObject({result:'',mails:[],obj:{},num:0});

构建obj时,传入的参数是一个template object,其作用在于告诉DynamicObject究竟如何构建针对于obj这个对象所拥有属性的getter与setter方法,其实就是确定getter setter方法的名字的作用,可能看了下面的演示代码,大家会清楚一些:

function init() {
	var obj = new DynamicObject({result:'',mails:[],obj:{},num:123},'test');
	obj.setresult('my result');
	alert(obj.getresult());
	obj.setmails([111,222]);
	alert(obj.getmails()[0]);
	obj.setnum(99999);
	alert(obj.getnum());
}

稍后会有DynamicObject更详细的实现,包括如何实现fetch data ondemand的部分

 

Technorati: , ,

十月 24, 2006

这里说的“共享内存”,其实意思是共享数据,就是客户端能够透明的直接访问服务器端“内存”里存储的某个数据实体对象。概念源于Clorox这个项目。该项目就是提供一种类似与传统javascript数据机构的对象,使得客户端js访问该对象中的数据字段时,该对象能向服务器发出rpc请求来获得该字段确切的数据(It provides data structures that look exactly like ordinary JavaScript objects but that actually make AJAX RPC calls behind the scenes to fetch data),而该项目提出的一种概念比较符合工程思想:当下很多web应用程序实际上就是某些数据结构的浏览器而已(Since many web applications can be thought of as viewers over structured data),比如Web mail系统实际上就是对服务器数据库种mail数据表的一种浏览、管理工具。

说到这里,如果我们创建一种足够smart的数据结构,能够在我们需要某些数据字段的时候,自动发出rpc请求,返回该字段的数据给我们,并且让程序员操作这种数据结构就像操作传统的javascript数组/对象一样方便的话,确实是非常有吸引力的一种想法,而且这种思路将传统的Ajax上升到了一个更高的抽象层次–以数据为中心(Data centeric)。

虽然这样的语法看起来比较不雅观,可是确实是一种思想上的变化:

// read-ahead ten headers at a time
var mailHeaders = new SmartArray("mailHeaders",
new PrefetchReadAheadPolicy(10),
new ClientCache(new AJAXTransport()));
function bodyLoad() {
	mailHeaders.forloop(0, numMsgs,
	function(i, header) {
		// display headers
	});
}

上升到这样的抽象层次,总是比下面这样好吧:

function update(){
	var url = 'http://123.com/a.aspx';
	var pars = '';
	var myAjax = new Ajax.Request(
			url,
			{
				method: 'get',
				parameters: pars,
				onComplete: _update_callback
			});
}
function _update_callback(oResponse){
	mailHeaders = eval('('+oResponse.responseText+')');
	//display headers
}

如果大家有兴趣的话,可以去研究Colrox的文档。而我要说的是,似乎Colrox这样的实现方式虽然概念不错,但是似乎不够雅观,如果能更接近javascript的语法的话,说不定会是个更好的主意,设想如果能像下面这样:

var persistUrl = 'http://abc.com/a.aspx';
var obj = new DynaObj({mails:[{header:'',body:'',date:''}]},persistUrl);
function bodyLoad() {
	mails = obj.getmails();
	for(var i=0;i<mails.length;i++){
		//display mails[i];
	}
}

当然,或许,Colrox在这方面遇上了无法克服的麻烦,我没有深入研究过,不敢妄言。

By the way:如果应用javascript 1.5标准里面的property getter and setter的话,说不定代码会更雅观。最近打算自己实现一下以上构想的DynaObj

 

Technorati: , , , ,

十月 23, 2006

听闻IE下面使用javascript多次拼接一个String会带来很严重的性能问题,于是就做了一个简单的测试。

测试内容,进行10000次字符串的拼接操作,实现代码:

var sTest = '';
var d1 = (new Date()).getTime();
for(var i=0;i<10000;i++){
	sTest += 'test';
}
var d2 = (new Date()).getTime();
document.writeln('common method : '+ (d2-d1).toString());

测试数据:

IE  common method : 422

FireFox  common method : 62

测试结果表明:在本次测试中Firefox以优异的性能胜出,而IE的表现则奇差无比。事实证明,IE不但在DOM标准的实现上面远远落后,而且很多处理js的方式,导致js在IE中的运行效率及其低下。所以,建议以后的所有Web2.0站点都使用Firefox进行浏览。

对于IE这种问题,可以自己构建一个StringBuilder类来解决,实现代码:

var StringBuilder = function(){
	this._sArray = new Array();
}
StringBuilder.prototype.append = function(str){
	this._sArray.push(str);
}
StringBuilder.prototype.toString = function(){
	return this._sArray.join('');
}

测试代码:

d1 = (new Date()).getTime();
var sb = new StringBuilder();
for(var i=0;i<10000;i++){
sb.append(’test2′);
}
sTest = sb.toString();
d2 = (new Date()).getTime();
document.writeln(’StringBuilder method : ‘+ (d2-d1).toString());

重新测试结果:

IE  StringBuilder method : 94

FireFox  StringBuilder method : 63

对于IE来说,好像String拼接的低效问题得到了比较好的解决,而对于Firefox来说,效果微乎其微,因为FireFox本来就没有这样的问题。

 

Technorati: , ,

十月 12, 2006

以往可能使用google analytics的方式,就是把code往自己的网站上面一放,就等着看结果了。可是在这个年代,什么ajax啊,各种javascript event啊,我们都想做一些统计,这样的话,以上说的办法就不再适用了,好在google analytics为我们提供了很灵活的方法来实现记录各种js event的功能,比如一下这段代码就可以实现记录网站链接点击情况的功能。

将以下代码放置到您需要统计的Blog上(当然同时需要将google analytics的脚本也放在这个页面上),等待24/48小时之后,在google analytics后台的Top Content报告中就可以看到你的用户在您的页面上点击了哪些外部链接。用户点击的外部链接将使用”out/外部链接地址”的方式显示出来。

    <script type="text/javascript">
        function onLoad(){
            var links = document.getElementsByTagName(’a');
            for(var i=0;i<links.length;i++){
                var element = links[i];
                if(element.addEventListener){
                    element.addEventListener(’click’,trackClick,false);
                }else if(element.attachEvent){
                    element.attachEvent(’onclick’,trackClick);
                }    
            }
        };
        
        function trackClick(e){
            if (!e) var e = window.event
            var href = ”;
            if(e.target){
                href = e.target.getAttribute(’href’);    
            }else if(e.srcElement){
                href = e.srcElement.href;
            }
            
            if(href.indexOf(’http://’)>-1){
                if(href.indexOf(’my.donews.com/digitalghost’)<0){
                    alert(’抱歉打扰您了,我正在进行一些测试,稍候将看到一篇关于这个测试的BlogPost’);
                    urchinTracker(’out/’+href);
                }
            }    
        }
        
        addLoadEvent(onLoad);
    </script>

以上代码在wordpress系统之外的Blog系统中或许还需要addLoadEvent()函数的定义才可以运行:

 

function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != ‘function’) {
    window.onload = func;
} else {
    window.onload = function() {
     oldonload();
     func();
    }
}
}

其实调用google analytics统计脚本中的urchinTracker这个函数还可以实现很多其他的功能,比如记录ajax请求,记录文件下载等等,同时统计脚本还提供了其他的功能供你使用,详情可以查看google analytics的API

 

Technorati: , , ,

十月 11, 2006

做了这么长时间的Web开发,由于公司的G宝盘这种稍微具有一些特殊性的Web Application的需要,我需要研究一下让Web表现层取得用户本机OS控制权限的方法,除了一直抱怨没有一个足够powerful的presentation layer之外,也尝试着想看看在现有的技术条件下有没有什么方式可以变相的将Web与OS黏合在一起,好像确实没有什么新鲜的技术了,就如旧瓶装新酒一样的ajax技术一样,其实还是这些东西:javascript,ActiveX(For IE),Mozilla(For firefox)

  • javascript:这种语言是不是足够的powerful有过很多争论,就目前看来,好像没有什么任务是这种动态语言无法完成的。要相信,javascript也可以构建出许多复杂的框架,实现很多久经事故的模式
  • ActiveX:MS平台上面广为被流氓软件使用的技术,搞得现在人心惶惶,很多半瓶醋的用户更是闻ActiveX色变,看见经典的ActiveX提示对话框就点“否”,甚至禁用ActiveX控件。可是不得不说,如果想在IE下面使用javascript取得OS的操作权限,是没有它不行的。经典的Web/OS粘合实现有:MS Live Favorite,Web迅雷等等……
  • Mozilla:这个东西不是一个浏览器内核,而是一个Application develpment platform,没错,他就是这么个东西,在这个平台上面你可以自由的使用javascript与XUL进行各种软件的开发,当然完成很多系统功能必须使用XPCOM这种Mozilla内置的跨平台组件技术。这种将Web与OS粘合比较有名的实现有:Allpeers.com,当然还有Firefox下面成千上万的各种plugin…..这个平台的潜力不容小视。

或许很多开发者翘首以盼的认为WPF/E能够解决Web与OS之间的鸿沟,可是为什么不先尝试使用Mozilla这种技术呢,个人认为至少在WPF/E成熟进入市场之前,这种技术是最成熟的解决了Web与OS之间的粘合问题的。

 

Technorati: , ,