官方的JUI和EXT , MT的UI组件比起来实在没有多少可比性,Jquery的插件中有很多优秀的好东西,但是却都像个散兵游勇一样的,这样的优点是灵活,强大,有各自的专人维护,做到某一点上的极品.但是缺点也有,有些难以搭配,得仔细思量....
?
就像买电脑一样,除非你是绝对的高手,才能配置到各方面都很优秀的东西.
?
比如我的本本,TCL的,配置很低,4年半之前买的,到现在还在用,很好用,中间出来开机加了条内存,没有修过一次.只能说整体性能还行.
?
怎么说呢,QUI就是想做成一套比较中庸的,朴实的,简单的JQUERY UI框架,简单就是美,使用jquery来写代码很轻松也很方便,这点不用我来说,在这里我就不再议论它与其他框架的优劣了,适用就是最优了.我始终认为: JS是无需继承,多态的,这些名词其实是JAVA开发者故意为其加上去的,说白了就是就是 精简代码,复用代码. 仅此而已,而且在JS中,根本就没有继承的
说法,大家所云的JS继承只是属性的复制与替换.
这星期工作比较忙,所以在昨天和大前天的晚上写了些初步代码,[前天谈个项目到晚上12点,不过蹭了顿九头鸟].
?
征集LOGO -- 色色的图片处理确实惨不忍睹,请群友或者博友予以帮助,弄个Logo啊
?
QUI当前版本命名为0.1,仅仅实现了以下功能:
- 资源文件的自动定位
- JS的动态加载
- 命名空间定义
- 类定义
- 提供了一个扩展的alert方法 $qui.alert
- 命名空间对象的快捷方式的创建 比如 $qui.alert
以下是代码和实例,组件代码都是使用JS动态加载来实现的,包括必需组件和自加载组件
?
Common.js -- 这个文件是必需的
/** * @file: Common.js * @author: 色色[vb2005xu] * @date: 2009年4月21日9:34:06 * @description: * 通用JS文件,里面定义了常用的一些函数 * 其中一些函数摘自 FF浏览器自带组件 */ /** * Returns true if the specified value is |null| */ function isUndefined(val) { return typeof val == "undefined"; } /** * Returns true if the specified value is |null| */ function isNull(val) { return val === null; } /** * Returns true if the specified value is an array */ function isArray(val) { return isObject(val) && val.constructor == Array; } /** * Returns true if the specified value is a string */ function isString(val) { return typeof val == "string"; } /** * Returns true if the specified value is a boolean */ function isBoolean(val) { return typeof val == "boolean"; } /** * Returns true if the specified value is a number */ function isNumber(val) { return typeof val == "number"; } /** * Returns true if the specified value is a function */ function isFunction(val) { return typeof val == "function"; } /** * Returns true if the specified value is an object */ function isObject(val) { return val && typeof val == "object"; } /** * Returns an array of all the properties defined on an object */ function getObjectProps(obj) { var ret = []; for (var p in obj) { ret.push(p); } return ret; } /** * Returns true if the specified value is an object which has no properties * defined. */ function isEmptyObject(val) { if (!isObject(val)) { return false; } for (var p in val) { return false; } return true; } var getHashCode; var removeHashCode; (function() { var hashCodeProperty = "lang_hashCode_"; /** * Adds a lang_hashCode_ field to an object. The hash code is unique for * the given object. * * @param obj * {Object} The object to get the hash code for * @returns {Number} The hash code for the object */ getHashCode = function(obj) { // In IE, DOM nodes do not extend Object so they do not have this // method. // we need to check hasOwnProperty because the proto might have this // set. if (obj.hasOwnProperty && obj.hasOwnProperty(hashCodeProperty)) { return obj[hashCodeProperty]; } if (!obj[hashCodeProperty]) { obj[hashCodeProperty] = ++getHashCode.hashCodeCounter_; } return obj[hashCodeProperty]; }; /** * Removes the lang_hashCode_ field from an object. * * @param obj * {Object} The object to remove the field from. */ removeHashCode = function(obj) { obj.removeAttribute(hashCodeProperty); }; getHashCode.hashCodeCounter_ = 0; })(); /** * Fast prefix-checker. */ String.prototype.startsWith = function(prefix) { if (this.length < prefix.length) { return false; } if (this.substring(0, prefix.length) == prefix) { return true; } return false; } /** * Removes whitespace from the beginning and end of the string */ String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); } /** * Does simple python-style string substitution. "foo%s hot%s".subs("bar", * "dog") becomes "foobar hotdot". For more fully-featured templating, see * template.js. */ String.prototype.subs = function() { var ret = this; for (var i = 0; i < arguments.length; i++) { ret = ret.replace(/\%s/, String(arguments[i])); } return ret; } /** * Returns the last element on an array without removing it. */ Array.prototype.peek = function() { return this[this.length - 1]; } /** * Repeat a string -- 2.1的为3 * * @param double number * @return str * * "1\n".repeat(2.1).alert(); */ String.prototype.repeat = function(number){ var str = '' ; number = isNumber(number) ? number : 1 ; for (var i = 0; i < number; i++) { str += this.toString(); } return str ; } /** * alert string using debug -- sese */ String.prototype.alert = function(){ alert(this.toString()); }
?
QUI.js -- 核心文件
/** * 定义QUI命名空间 * * 设计: 继承,多态的目的说白了就是 精简代码,复用代码 * 对JS产品而言则是易用性,快速性最优 * * QUI计划是作为UCREN的Jquery版本的衍生品,不知道最后会变成什么,(*^__^*) 嘻嘻…… * */ function QUI(){ this.version = '0.1' ; this.appPath = this.getAppPath(); /** * {}对象命名空间 -- 摘自EXT JS * 这个就相当于定义一个包 */ this.ns = function(){ var a=arguments, o=null, i, j, d, rt; for (i=0; i<a.length; ++i) { d=a[i].split("."); rt = d[0]; eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';'); for (j=1; j<d.length; ++j) { o[d[j]]=o[d[j]] || {}; o=o[d[j]]; } } }; /** * 定义类方法,如果前面的命名空间不存在则创建之 */ this.Class = function(){ function cfn(fn){ var tpl = 'if (typeof #fn# == "undefined"){#fn# = function(){};}' ; tpl = tpl.replace(/#fn#/g,fn); eval(tpl); }; var a=arguments ,ns_str=null ,d, fn; for (var i=0; i<a.length; ++i) { ns_str=arguments[i]; //确保前面的命名空间有效 d=ns_str.split("."); if (d.length === 1) cfn(ns_str); else { var dd = ns_str.replace(d[d.length - 1],''); this.ns(dd); cfn(ns_str); } } }; /** * 创建命名空间对象的快捷方式 * @param String quickIndex * @param String destNS * 例如: * $qui.nsQuickIndex('$qui.alert','QUI.Widget.Alert.prototype.show'); * 将这个方法绑定到$qui.alert */ this.nsQuickIndex = function(quickIndex,destNS){ eval(quickIndex + '=' + destNS + ";"); } } QUI.prototype = { /** * 返回QUI所在的路径 , 用于加载资源文件 */ getAppPath: function(){ var script = document.getElementsByTagName("script"); for (var i = 0; i < script.length; i++) { var match = script[i].src.match(/.*QUI.js($|\?.*)/); if (match) { return script[i].src.replace(/QUI\.js.*/, ''); } } } , /** * 动态加载脚本文件,非异步加载 * 仅限于QUI内部脚本 * @type {} */ include: function(){ $.ajaxSetup({async:false}); for (i=0; i<arguments.length; ++i) { $.getScript($qui.appPath + arguments[i].replace(/\./g,'/') + '.js'); } $.ajaxSetup({async:true}); } }; var $qui = new QUI(); $qui.include('Common'); $qui.include('Widget.Alert'); $qui.nsQuickIndex('$qui.alert','QUI.Widget.Alert.prototype.show');
?
?
Widget/Alert.js -- 这个不用我说了吧
/** * 提供多功能的alert函数 * 接收 变量,数组,对象,混合对象数组的alert * * 依赖 {QUI.appPath}/Common.js文件 */ $qui.Class('QUI.Widget.Alert'); jQuery.extend(QUI.Widget.Alert.prototype,{ _array2str: function(arr,tnum){ //tnum \t的数量 tnum = isNumber(tnum) ? tnum : 0 ; for (var i = 0; i < arr.length; i++) { if (isArray(arr[i])) { arr[i] = arguments.callee(arr[i],tnum + 1); } else if (isObject(arr[i])){ arr[i] = QUI.Widget.Alert.prototype._obj2str(arr[i],tnum+1); } } return "[" + "\n" + "\t".repeat(tnum+1) + arr.join(",\n" + "\t".repeat(tnum+1)).toString() + "\n" + "\t".repeat(tnum) + "]" ; } , _obj2str: function(o,tnum){ var s = '{' ; tnum = isNumber(tnum) ? tnum : 0 ; for(var p in o){ if (isFunction(o[p])){ //不显示函数对象体 s+= '\n' + "\t".repeat(tnum+1) + p + ":" + ' Function' + ','; } else if (isArray(o[p])){ //不显示函数对象体 s+= '\n' + "\t".repeat(tnum+1) + p + ":" + QUI.Widget.Alert.prototype._array2str(o[p],tnum+1) + ','; } else if (isObject(o[p])){ s+= '\n' + "\t".repeat(tnum+1) + p + ":" + arguments.callee(o[p],tnum + 1) + ','; } else s+= '\n' + "\t".repeat(tnum+1) + p + ":" + o[p] + ','; } s = s.replace(/,$/, "") ; s += "\n" + "\t".repeat(tnum) + '}' ; return s; }, show: function(){ var a = arguments[0]; if (isBoolean(a) || isNumber(a)) (a + "").alert(); else if (isString(a)) a.alert(); else if (isArray(a)) //a.alert(); QUI.Widget.Alert.prototype._array2str(a).alert(); else if (isFunction(a)) a.toString().alert(); else if (isObject(a)) //Objalert(a); QUI.Widget.Alert.prototype._obj2str(a).alert(); } });
?
?
?
TestCase/QUI/Widget/Alert.js -- 顾名思义也知道了吧
//file: TestCase.QUI.Widget.Alert.js //($qui.appPath + 'test/' + 'TestCase.QUI.Widget.Alert.js').alert(); $qui.Class('TestCase.QUI.Widget.Alert'); TestCase.QUI.Widget.Alert.prototype.test = function(){ var a = ["bb","cc",1,{a: 1}]; //var b = ["bb","cc",1,[1,3,4]]; var c = ["bb","cc",1,[1,['x',['u',['i','a','o']]],{a: 4},4]]; //a.alert(); //b.alert(); //c.alert(); $qui.alert(a); $qui.alert(c); var d = { a:1.0 , v:2 , d:function(){ } , m: { a: 1 , b: 2 , c: ['1',[5,8,[7,{'mm':{bb:1234567890}}]],3] , d: { a: 1 , b: 2 } } } $qui.alert(d); } //以下代码将会定义在代码规范里面, $qui.nsQuickIndex('$qui.alert.test','TestCase.QUI.Widget.Alert.prototype.test');
?
?
TestCase/Main.js -- 测试的主文件,主要方便测试使用的
/** * 测试主文件 */ $qui.include('TestCase.QUI.Widget.Alert'); $qui.alert.test();
?
?
以下是使用实例:
QUI是基于Jquery的,本人确实没有实力重建一套底层代码,所以在导入QUI.js之前需要导入Jquery.js,我是以的是jquery1.2.6. 很郁闷,上次在 程序员杂志上发现JQUERY1.6呵呵,不知道这1.6从何而来啊
?
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>测试例子</title></head> <body> </body> </html> <script src='jq-1.2.6.min.js' type='text/javascript'></script> <script src='qui/QUI.js' type='text/javascript'></script> <script src='BJE.js' type='text/javascript'></script>
?
BJE.js -- 如下所示
//定义顶块命名空间 function BJE(){ this.v = '1.0' ; } BJE.prototype = { }; var instance = new BJE(); $qui.include('TestCase.Main');
?
其中$qui.include('TestCase.Main'); 这个里面暂时只有
$qui.include('TestCase.QUI.Widget.Alert'); $qui.alert.test();
?
对浏览器自带的alert方法,我做了两点扩充,传统的功能对数组,对象都不能明确显示,很麻烦,有时候必须借助辅助工具才能查看,比较麻烦,我这里扩展了这两项: 不仅仅是数组,对象的显示扩展,包括数组对象的混合也可以,你可以见Alert的测试实例那个文件中的代码 [注意,对于复杂的对象请不要使用它来做,比如$('BODY')]这样的东西,第一是系统这个alert框装不下,第二是很占内存,第三是这个功能会在后面的调试日志功能中实现]
?
$qui.alert.test(); 注意这个的写法,之后的一些测试将会遵循 功能.test() 来呵呵呵
以下是一些截图 Alert功能的:
?
如果你要查看 某个方法的实现,只需这样:
$qui.alert($qui.alert);
?就可以查看到改方法的实现
以下代码主要实现两个功能:
1. CSS样式风格的快速切换,可以快速切换,通过$qui.useStyle()方法
2. 通过在<script src='qui/QUI.js?skin=xp'></script>这里指定skin属性来加载特定的皮肤效果,
代码修改处说明:
1. 新增CSS切换功能,用于快速切换CSS样式的功能
/** * 界面使用的皮肤 -- 缺省使用xp风格的 * @type {String} */ this.skin = this.skin || 'xp';
2.对 getAppPath方法修改如下,用于设置skin属性
getAppPath: function(){ var script = document.getElementsByTagName("script"); for (var i = 0; i < script.length; i++) { var match = script[i].src.match(/.*QUI.js($|\?.*)/); if (match) { /** * 皮肤匹配 -- 用户设置了才设置这个属性 */ var matchSkin = script[i].src.match(/.*QUI\.js\?skin=/); if(matchSkin) this.skin = script[i].src.replace(/.*QUI\.js\?skin=/, ''); return script[i].src.replace(/QUI\.js.*/, ''); } } }
3.添加CSS切换函数
/** * 使用的样式表文件 -- 抽取自Ucren * @param {} s */ useStyle: function(s) { s = s || this.skin ; //未定义使用default风格 document.write("<link rel=\"stylesheet\" skin=\"" + s + "\" href=\"" + this.appPath + "resources/" + s + "/interface.css\" type=\"text/css\" media=\"all\"/>"); }
在Ucren中,控件的HTML片段都是写到JS文件中的,个人感觉相当的麻烦.所以想到将其单独
抽取到一个xml文件中.
该xml文件的实例[qui/Widget/Template.xml]格式如下:
<?xml version="1.0" encoding="UTF-8"?> <data> <widget id='QUI.Widget.Alert'> <![CDATA[ 测试例子 ]]> </widget> </data>
这样在程序中可以通过id来查找这些样板字符串,以后也更好修改
现在将qui/Page/Template.js文件代码,基本完成:
/** * 页面模板对象 -- 支持样板代码替换 * 每个样板代码均写到一个XML文件中 * 这样也方便更改 */ $qui.Class('QUI.Page.Template'); QUI.Page.Template.prototype = { datasource: ( $qui.appPath + 'Widget.Template'.replace(/\./g,'/') + ".xml") , /** * xmlobj的对象,解析数据源的 * @type {} */ list: {} , /** * 解析数据源成XML对象 * @param String datasource */ init: function(datasource){ datasource = datasource || ( this.datasource) ; var tpls = this.list ; $.ajax({ async:false ,type: 'POST' ,url: datasource , dataType : 'xml',timeout:200, success: function(data){ var k = '',v ; $('data widget',data).each(function(){ //这里将.改为- k = $(this).attr('id').replace(/\./g,'-'); v = $(this).text() ; eval('o={"k":v}'.replace('k',k)) ; jQuery.extend(tpls,o); }); } , error: function(){ $qui.alert('Cannot load xml: ' + this.url); } }); } , /** * 根据tpl的id来查找指定的样板字符串 * @param {} id */ get: function(id){ id = (id || 'sese.no').replace(/\./g,'-'); if (this.list.hasOwnProperty(id)){ return this.list[id]; } return '' ; } };
并在 QUI.js下面添加如下代码,创建简洁的快捷方式
//页面代码模板代码 $qui.include('Page.Template'); $qui.nsQuickIndex('$qui.tpl','QUI.Page.Template');
测试代码[qui/TestCase/QUI/Page/Template.js]如下:
/** * 页面模板对象 -- 支持样板代码替换 * 每个样板代码均写到一个XML文件中 * 这样也方便更改 */ $qui.Class('TestCase.QUI.Page.Template'); TestCase.QUI.Page.Template.prototype.test = function(){ var tpl = new QUI.Page.Template(); tpl.init(); $qui.alert(tpl.list); $qui.alert(tpl.get('QUI.Widget.Alert')); } ; $qui.nsQuickIndex('$qui.tpl.test','TestCase.QUI.Page.Template.prototype.test');
最后在测试入口文件[qui/TestCase/Main.js]中,添加如下行:
$qui.include('TestCase.QUI.Page.Template'); $qui.tpl.test();
在浏览器窗口刷新 index.html 页面即可看到哦 -- 在FF和IE6上调试通过