论坛首页 Web前端技术论坛

QUI的开工 -- 打造一个简单实用的UI库 , 征集LOGO

浏览 5045 次
精华帖 (0) :: 良好帖 (8) :: 新手帖 (11) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-04-22   最后修改:2009-04-22

官方的JUI和EXT , MT的UI组件比起来实在没有多少可比性,Jquery的插件中有很多优秀的好东西,但是却都像个散兵游勇一样的,这样的优点是灵活,强大,有各自的专人维护,做到某一点上的极品.但是缺点也有,有些难以搭配,得仔细思量....

 

就像买电脑一样,除非你是绝对的高手,才能配置到各方面都很优秀的东西.

 

比如我的本本,TCL的,配置很低,4年半之前买的,到现在还在用,很好用,中间出来开机加了条内存,没有修过一次.只能说整体性能还行.

 

怎么说呢,QUI就是想做成一套比较中庸的,朴实的,简单的JQUERY UI框架,简单就是美,使用jquery来写代码很轻松也很方便,这点不用我来说,在这里我就不再议论它与其他框架的优劣了,适用就是最优了.我始终认为: JS是无需继承,多态的,这些名词其实是JAVA开发者故意为其加上去的,说白了就是就是 精简代码,复用代码. 仅此而已,而且在JS中,根本就没有继承的

说法,大家所云的JS继承只是属性的复制与替换.

这星期工作比较忙,所以在昨天和大前天的晚上写了些初步代码,[前天谈个项目到晚上12点,不过蹭了顿九头鸟].

 

征集LOGO -- 色色的图片处理确实惨不忍睹,请群友或者博友予以帮助,弄个Logo啊

 

QUI当前版本命名为0.1,仅仅实现了以下功能:

  1. 资源文件的自动定位
  2. JS的动态加载
  3. 命名空间定义
  4. 类定义
  5. 提供了一个扩展的alert方法 $qui.alert
  6. 命名空间对象的快捷方式的创建 比如 $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);

 就可以查看到改方法的实现

  • 大小: 7.3 KB
  • 大小: 8.7 KB
  • 大小: 10.4 KB
  • 大小: 12.1 KB
   发表时间:2009-04-22  
中午的时候参阅了Ucren1.7的代码,学到一些东西,从中借鉴了一些代码.在QUI.js文件中添加和修改了如下代码.目前对于CSS样式决定借鉴Ucren的早期版本

以下代码主要实现两个功能:
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\"/>");
	}
0 请登录后投票
   发表时间:2009-04-22  
新增一个模块 页面样板[QUI.Page.Template],用于HTML片段代码的动态生成.
在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上调试通过
0 请登录后投票
   发表时间:2009-04-22  
支持下~~~~~
0 请登录后投票
   发表时间:2009-04-23  
没有继承这些概念写UI代码复用率比较低哦.
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics