从TUI.panel依赖想到的,panel的文件都在全局里面,每次修改都需上全局,修改测试起来都太麻烦。

TUI.use也只是单文件依赖加载,没有多文件也没有css依赖加载。

另一种角度考虑panel使用,将css和js分别都提取出来为单独的文件,在需要使用的时候,先mask掉page,然后异步加载这两个文件,完成后callback执行。

小知识:土豆前端静态文件版本管理系统很完善,每个静态文件发布时会在文件名(不包含扩展名)后自动增加_数字,这使得历史记录查询对比以及回退成为了可能。在需要异步加载所依赖的文件时,需要用use取得带版本号的完整url才可。

伪代码:

var module = {}, //模块库,用以类似YUI.use异步加载css或js模块
	modules = {}, //多个模块同时加载时记录的callback

TUI.mix({
	/**
	 * @public 字符串是否以指定sub结尾
	 * @param {string} str需要确定的字符串
	 * @return {string} 结尾
	 */
	endWith: function(str, sub){
		return str.lastIndexOf(sub) == str.length - sub.length;
	},

	/**
	 * @public 注册单个模块文件方法
	 * @param {string} js的url
	 * @param {boolean} 是否延迟加载,即是在use调用的时候加载还是domready时加载
	 */
	join: function(url, noLazy) {
		var res = /(.+)_\d+\.(js|css)/.exec(url),
			key = res[1] + '.' + res[2],
			mod = module[key] || {};
		mod.name = key;
		mod.url = url;
		mod.cb = [];
		module[key] = mod;
		//若指定第2个参数为true,则立刻加载,否则use时加载
		if(noLazy) {
			this.use(key);
		}
	},

	/**
	 * @public 注册多个模块文件方法
	 * @param {array} 模块列表
	 * @param {boolean} 是否延迟加载,即是在use调用的时候加载还是domready时加载
	 */
	joins: function(list, noLazy) {
		var that = this;
		list.forEach(function(item) {
			that.join(item, noLazy);
		});
	},

	/**
	 * @public 异步使用单个css或js文件方法,
	 * @param {string} 文件的url不带版本号
	 * @param {function} 加载成功后的调用函数
	 */
	use: function(key, cb) {
		var mod = module[key],
			that = this;
		//必须先join注册
		if(!mod) {
			return;
		}
		if($.isFunction(cb)) {
			mod.cb.push(cb);
		}
		//尚未加载成功时
		if(!mod.loaded && !mod.loading) {
			mod.loading = true;
			$(function() {
				if(that.endWith(key, '.js')) {
					that.getScript(mod.url, {
						charset: mod.charset,
						callback: function(){
							mod.loaded = true;
							mod.cb.forEach(function(cb) {
								cb();
							});
						}
					});
				}
				else if(that.endWith(key, '.css')) {
					that.getCss(mod.url, function() {
						mod.loaded = true;
						mod.cb.forEach(function(cb) {
							cb();
						});
					});
				}
				module[key] = mod;
			});
		}
		//已经加载完成了这个js文件,直接执行cb
		else if($.isFunction(cb)) {
			cb();
		}
	},

	/**
	 * @public 异步使用多个css或js文件方法,
	 * @param {array} url列表
	 * @param {function} 加载成功后的调用函数
	 */
	uses: function(keys, cb) {
		//所有mod必须先join注册
		for(var i = keys.length - 1; i > -1; i--) {
			if(!module[keys[i]]) {
				return;
			}
		}
		var k = keys.join(''),
			mods = modules[k] || [],
			that = this;
		if($.isFunction(cb)) {
			mods.push(cb);
		}
		modules[k] = mods;
		keys.forEach(function(mod) {
			that.use(mod);
		});
		//定时侦听所有模块,全部加载完成后执行cb列表
		var interval = setInterval(function() {
			var fin = true;
			keys.forEach(function(mod) {
				if(!module[mod].loaded) {
					fin = false;
					return fin;
				}
			});
			if(fin) {
				mods.forEach(function(item) {
					item();
				});
				clearInterval(interval);
			}
		}, 50);
	},

	/**
	 * @public 异步加载js文件方法
	 * @param {string} js的url
	 * @param {object} 选项传入charset和callback
	 */
	getScript: function(url, op) {
		var s = this.create('script'),
			head = this.tag('head')[0],
			done = false;
		s.type = "text/javascript";
		s.async = true; //for firefox3.6
		if($.isFunction(op)) {
			op = { callback: op };
		}
		op = op || {};
		if(op.charset) {
			s.charset = op.charset;
		}
		s.src = url;
		s.onload = s.onreadystatechange = function(){
			if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
				done = true;
				//防止ie内存泄漏
				s.onload = s.onreadystatechange = null;
				head.removeChild(s);
				if(op.callback) {
					op.callback();
				}
			}
		};
		head.appendChild(s);
	},

	/**
	 * @public 异步加载css文件方法
	 * @param {string} css的url
	 * @param {function} callback
	 */
	getCss: function(url, cb) {
		var that = this;
		$.ajax({
			url: url,
			success: function(data) {
				var s = that.create('style'),
					head = that.tag('head')[0];
				s.type = 'text/css';
				//ie和其它要分开对待
				if(s.styleSheet) {
					s.styleSheet.cssText = data;
				}
				else {
					s.appendChild(document.createTextNode(data));
				}
				head.appendChild(s);
				if(cb) {
					cb();
				}
			}
		});
	}
});

实际使用时,是这样:

//这会在模板上jion
TUI.joins(['out_1.css', 'out_1.js']);
//真正执行
TUI.uses(['out.css', 'out.js'], function() {
	alert(1);
});

十一月 6th, 2010

安装eric5.0.3失败……

4 Comments, python, by army.

IDLE用得实在难受,而我又不喜欢eclipse,目前支持python3.1的也只有eric了。pyqt4.8.1安装好了,eric5也提示完成,然后双击.bat……没反应……
我勒个去,google下发现也有相同的例子存在,虽然小版本号和我的不一致。有其它的啥IDE用啊……

十一月 3rd, 2010

更新了JAcld

No Comments, JAcld, 前端开发, by army.

两点修改:

  1. 国产加壳浏览器延迟初始化,防止报异常。这点要感谢阿里的工程师们:http://www.aliued.cn/?p=3169
  2. 记录了scrollTop的值,防止要验证的input输入框在聚焦、验证切换焦点过程中发生的滚动条位移。

暂且放在土豆的passport测试系统上交给测试工程师测试,hoho,我在占测试mm的小便宜么~~

function(js, swf, target, tip) {
	var isIE = (navigator.appName.indexOf("Microsoft") != -1);
	var isWebkit = (navigator.userAgent.indexOf("AppleWebKit") != -1);
	var cssCompat = document.compatMode == "CSS1Compat";
	var doc = cssCompat ? document.documentElement : document.body;
	var bDetecting = false, bCapsLock;
	var oSwf, oTarget, oTip, sDisplay;

	function $(id) {
		return document.getElementById(id);
	}
	function getSwf(name) {
		if(isIE) {
			return window[name];
		}
		else {
			return document[name];
		}
	}
	function addEvent(oNode, sType, fn) {
		if(window.addEventListener) {
			oNode.addEventListener(sType, fn, false);
		}
		else if(window.attachEvent) {
			oNode.attachEvent("on" + sType, fn);
		}
	}
	function show() {
		oTip.style.display = sDisplay;
	}
	function hide() {
		oTip.style.display = "none";
	}
	function scrollTop(v) {
		if(typeof v !== "undefined") {
			doc.scrollTop = v;
			if(!doc.scrollTop) document.body.scrollTop = v;
		}
		return window.pageYOffset || doc.scrollTop;
	}

	var jacld = {
		exec: function() {
			oSwf = getSwf(swf);
			oTarget = $(target);
			oTip = $(tip);
			//加载的swf、指定的检测对象、提示框缺一不可
			if(!oSwf || !oTarget || !oTip) {
				return;
			}
			//记住原先的tip的display
			sDisplay = oTip.style.display || "block";
			this.init();
		},
		init: function() {
			//除webkit外的其它浏览器
			addEvent(oTarget, "focus", function() {
				//防止循环死机
				if(bDetecting) {
					bDetecting = false;
					return;
				}
				var v = scrollTop();
				bDetecting = true;
				oSwf.focus();
				oSwf.detect();
				//模拟线程同步,使其最后执行
				setTimeout(function() {
					oTarget.focus();
					scrollTop(v);
				}, 0);
			});
			addEvent(oTarget, "blur", function() {
				if(bDetecting) {
					return;
				}
				hide();
			});
			addEvent(oTarget, "keydown", function(event) {
				event = event || window.event;
				var keyCode = event.keyCode;
				var v = scrollTop();
				//不在检测并且案件是capslock时才进行
				if(!bDetecting && keyCode == 20) {
					bDetecting = true;
					oSwf.focus();
					oSwf.detect();
					oTarget.focus();
					scrollTop(v);
				}
			});
		},
		isCapsLock: function(b) {
			if(b) show();
			else hide();
		}
	}

	window[js] = jacld;
}

主要是在flash聚焦之前记录scrollTop的值,离开后再主动设定回来。过程会有瞬间的闪烁,修改最终版完成后会传至google code

http://logintest.tudou.com/login.do?noreg=ok

土豆的passport系统使用了jacld来提供密码输入框大写锁定提示功能——一款基于flash的东东。由于存放flash的域名和页面域名不一致,所以导致了跨域问题。

不过很简单,flash也是支持跨域的。jacld代码中也加入了Security.allowDomain(“*”)并且嵌入时allowScriptAccess被设置为了always。在所有主流浏览器下都没问题。

可惜的是,360和tt虽然是基于ie的,但是跨域方面却没有做好。倘若使用debugger版本会报SecurityError: Error #2060: 安全沙箱冲突的异常,普通版本自然会因此失效。

为了防止报错,只能捕获这一异常并防止它干扰用户了。很是可惜。

在百度偶像内测的时候我得到了邀请码并且也试用了一阵子,目前偶像已进入beta阶段,我给我的一些坛子好友们也发了些邀请,过程中遇到几个细节上可以更完善的地方。于是乎一一述来。

先贴一曲:

如上图,外链的播放器这一点的确很让人享用,但是当歌名太长的时候,文字信息就会超出显示到外边。普通的桌面MP3播放器等,经常会有那种“文字滚动”的效果,即一行之类显示不完全,会让文字慢慢向左方滚动,如此展示完整。我在想偶像是否也可以用类似的或者其它的方式来解决这个问题呢?

再是外链链接方面,比如点击上图中的人名“西国の海妖”,应该是打开这位歌手的主页的,偶像确实也这样做了。但是遗憾的是,链接地址写的是相对地址,那么外链播放器这个链接也太……总之是个很低级的失误……

还有上传方面。我发现偶像在很多方面使用了flash,比如邀请注册的复制code那里,这与我们土豆很像,基本认定了这样一个准则:使用或浏览网站的用户一定是安装了必需版本的flash的。那么在上传方面,批量上传歌曲这种功能的出现就显得很人性化了。譬如从别的站点迁移过来的用户,有许多首歌要上传,一首首点岂不是要累死?以flash做个“高级上传”的页面功能,便可简化许多。

最后是歌词敏感过滤方面,比如我的lrc中如是填写:[01:18.99]上那繁盛帝国的荣耀与辉煌 转眼成荒芜坟场,会提示包含敏感词不能通过。仔细检查,原来是[01:18.99]这里有问题,难道是18.9被认为九月十八号比较敏感?类似的[02:08.96]也会报错,希望验证时对于lrc的[]时间部分可以忽略,毕竟一首歌那么多时间点,一一排查起来很恐怖。敏感提示的话最好也给个范围什么的,我用二分查找都很累……