‘背光JS框架’ 所有文章

Background JavaScript UI 库发布 2.0.8 版

十一月 3rd, 2009 由 Rock 发表

这次版本修正同时也改变了部分设计.

主要修改 :

1. 优化容器布局,使得在容器显示时才布局.

容器在显示时才布局,使得容器在一些情况下, 最常见的是在Resize后, 忽略对容器布局从而提高整个应用的性能.

由于在显示时才布局,所以一开始就隐藏的容器如果存在子项并不立即渲染,因为对于容器来说,子项的渲染处于容器的 布局过程中触发,所以很多时候要等到容器显示时子项才得以渲染.

这种行为对于非IE浏览器,几乎没什么弊端,因为对于非IE浏览器来说,DOM文档添加结点的操作响应是非常快速的,对于控件渲染时才把结点添加到DOM文档中这操作来说,几乎不影响用户的体验.

但IE浏览器就不同了,即便IE8也是如此,DOM文档添加结点这操作是非常昂贵的,所以在IE下,容器在显示时才布局的弊端就是引发用户操作的短暂停顿,这种方法并不够友好!

可以在IE和非IE浏览器下对比一下这个菜单中,IE下首次显示菜单的时候操作有些少的停顿.

2. 优化类生成函数,实现原型链继承方式,减少类创建时引起的内存消耗.

这个是当前版本中库结构最关键的更新.

先前考虑到类属性查找速度问题,库中类继承是采用属性复制而不是基于原型链的方式实现.

如原库中:

    var ClassA.prototype.key = 'value';

    继承采用:

    extend(ClassB.prototype, ClassA.prototype);

   当前版类继承方式采用:

   var ClassA = function() {};

   //桥接类
   var Bridge= function(){};

   Bridge.prototype = ClassA.prototype;

   ClassB.prototype = new Bridge();

第一种的利弊:

利:运行时查找基类属性可在类自身的原型上找到,并不存在基类形成的原型链.

弊:在类生成过程中复制父类属性会引起时间上的损耗.
并不是真正意义上的继承,即在修改基类原型属性时在子类没法体现出,所以对于类原型的扩展有很大的局限.

第二种利弊刚好与第一种相反,运行时查找属性要遍历原型链,最大可能直至基类原型;在类创建时须新增一个桥接类.

本次采用第二种主要是考虑到能在修改基类原型属性的情况下,在子类可体现出来,便于控件行为与属性的扩展.

3. 以扩展方式增加表格视图统计功能,可向每个视图添加多个统计行.

经建议增加表格统计功能.

4. 新增浮动导航条控件和浮动布局管理器.

新增一个浮动布局管理器,它是依赖浏览器来检测布局,无需设置容器的具体宽高,这点与以往的布局管理器不同,布局速度上更胜一畴. 库中并为该布局设计了一个浮动导航条控件.


建立基本的页面框架 — 第二步丰富西部面板

七月 29th, 2009 由 Rock 发表

最近有点忙,打算上周写完这篇的,但近几天来几乎没碰过电脑了..非常难得少见的一次-_-!!

上次讲了创建页面框架最基本的一步:利用简单有效的BorderLayoutd布局管理器作为整体的客户区域中南西北中布局,总体框架出来了,但具体的面板内容还比较空虚(或许是寂寞更适合点^-^),这一节讲解怎么利用库来丰富页面框架中西部面板.

西部面板一般是放分组菜单,可同时加入多个分组,分组可以是一个树控件,或列表控件,宽度不大,把这些分组看成行状放置,可以用库的行布局管理器布局这些分组.

库中目前的行布局管理器共有两个,CRowLayout类和CRowLayout2类,它们对容器子控件布置方式不同之处主要体现在控件的position属性,前者是static布置,后者是absolute,各有各的优点,这里主要讲解一下第二个,即CRowLayout2布局管理器,因为它比较适合用来布置西部面板.

RowLayout2布局管理器

RowLayout2布局方式与BorderLayout极为相似,纯JavaScript计算方式布局子控件,同样有分隔条.RowLayout2把子控件由上而下布局,每个子控件宽度与容器宽度一致,它们高度与分隔条高度之和始终等于容器高度,除最下边一个行组控件没分隔条外,其它的组都允许有分隔条灵活控制分组高度,而且当容器宽度改变时,各分组将获得均等的高度份额.

利用RowLayout2布局管理器布局西部面板的分组

要指定面板布局管理器为RowLayout2只需设置容器面板的layout属性为’row2′即可,非常简单!

  //此时西部面板的布局设为RowLayout2
  var westPanel = new CPanel({view : 'westPanel', width:191, maxW:450, layout:'row2'});

布局管理器已经生成了,下面来为面部面板添加两个分组,为了好看点,这两个分组都用CTitlePanel类控件,即具有标题栏的面板,并且其内容可以收缩或展开.

  var firstTitlePanel = new CTitlePanel({title:'顶部面板' ,height:210});
  westPanel.add(firstTitlePanel, {split:true});

上面那个高度:120只是提示布局管理器最好用这个高度来设置控件高度,实际中RowLayout2布局管理器设定的并一定是这个,视布局取用的策略而定.
add方法的第二个参数是该控件用于布局管理器布局的配置信息,这里split:true向RowLayout2指明该分组有可拖动的分隔条.
添加了一个标题栏面板控件,再往这面板里面添加分组内容,这里加的是库中的一个组控件.

  //增加一个组控件
  var f = CUtil.createFolder({title:'My Folders', array:[
    {title:'收件箱',icon:'icoIbx'},
    {title:'发件箱',icon:'icoDft'},
    {title:'草稿箱',icon:'icoNote'},
    {title:'清除记录',icon:'icoDel',disabled:true},
    {title:'粉红色',icon:'icoDft'},
    {title:'蓝色',icon:'icoNote'},
    {title:'清除记录',icon:'icoDel'}
 ]});
 
  //指定组f的滚动面板为firstTitilePanel,
  //这样组内选择后的子项会自动滚动到可视范围内.
  f.scrollor = firstTitlePanel;
 
 //添加到标题栏面板中
firstTitlePanel.add(f);

第二个分组是一个树型控件,添加方式与上面一致,只是由组控件换成树型控件.

  var secondTitlePanel = new CTitlePanel({title:'树型面板' });
  westPanel.add(secondTitlePanel);
  //往标题栏面板中增加一个树型控件 
  //先要创建根结点
  var tree1Root = new CTreeItem({
     //nodes=true说明是一个有子结点的结点
    nodes: true
   });
 
  var tree = new CTree({
      root: tree1Root,
      title: '树根目录',
      //加上下面两句就可以利用ajax加载子结点了
      //url:'/q?bg_q=test_group', 
      //autoConnect:true
   });
 
   tree.scrollor = secondTitlePanel;
   secondTitlePanel.add(tree);

同样道理,无需额外的工作,就可以添加任意多个分组,这里就不一一列出了.

演示地址:http://www.bgscript.com/bgjs/samples/basicframe2.html

建立基本的页面框架 — 第一步小试牛刀

七月 21st, 2009 由 Rock 发表

可分为几个步骤:

  • 创建HTML页面
  • 创建Viewport
  • 利用BorderLayout布局viewport
  • 放置tab或其它控件

什么是viewport?
viewport从结构上可以看成一个层,它布满整个浏览器客户区,当浏览器宽高(size)改变时会自动适应.从这个意义上来说,与EXT的viewport功能是一致的.在库中一个viewport是一个DIV结点,绝对方式定位,放在body下.
viewport的作用就是,当宽高改变时,可通知内部的布局管理器重新布局子控件.众所周知,当用CSS改变一个元素的宽高时在JavaScript端是得不到任何通知的,例如将一个CSS类作用到一个元素时,你不作测试的话没法得知其高度已经改变,它不像事件监听那样,可以给元素添加事件监听器,当有动作发生时得到通知.除非所有元素都通过CSS布局,但这是不够灵活的,试想用CSS实现BorderLayout布局,并具有其特性,始终还得通过脚本控制CSS属性来实现,此时就会遇到一个问题,既然容器宽高已经改变,就得重新布局子控件,如上所说,CSS改变脚本得不会通知,要重新布局子控件就必须自定容器的事件机制,当通过脚本来改变容器宽高时(这里再次强调是通过脚本,而非直接设置CSS),容器就发送一个表明宽高已改变的事件,并通知布局管理器重新布局子控件.

了解了viewport,清楚为什么要用viewport,就开始搭建一个最简单的最常用到的页面框架,简单起见,元素不多,北面一个面板+tab,西面一面板和中间一个面板.

1.创建HTML页面
HTML页面是添加面板的HTML内容,具体可参见示例,这里不一一列出.

1.创建viewport,指定布局为border layout

  var viewport = new CViewport({layout:'border'});

2.利用布局viewport,这里主要创建北,西,中三个面板,并在北面板中添加一个tab控件

  //添加北,西,中面板
  //
  var northPanel = new CPanel({view : 'northPanel', maxH:70,height:70});
  var westPanel = new CPanel({view : 'westPanel', width:191, maxW:450});
  //card布局:容器里面的元素宽高与窗口一致.
  var centerPanel = new CPanel({view : 'centerPanel', layout:'card'});
  //在北面板中添加Tab控件
  //itemAutoConnect : true TabItem当选择时自动加载
  //contentPanel:存放TabItem内容面板的父容器,
  //当TabItem未指定Panel时,自动生成的Panel就被添加到该容器中.
  var tab = new CTab({
      id:'fr-tab', 
      itemAutoConnect:true,
      contentPanel:centerPanel, 
      array:[
       {title:'桌面 - DIV', panel:CC.$('deskPanel')},
       {title:'谷歌 - Google - IFRAME', src:'http://www.google.com'},
       {title:'百度 - Baidu - IFRAME', src:'http://www.baidu.com'},
       {title:'背光脚本', src:'http://www.bgscript.com'},
       {title:'通过Ajax载入内容', url:'http://www.bgscript.com/q?bg_q=changelog'}
  ]});
 
  northPanel.add(tab);
  //
  // dir:north,指明为北边容器,gap:1,空隙为1, split:true指明允许分隔条
  //
  viewport.add(northPanel, {dir:'north', gap:0})
             .add(westPanel, {dir:'west', gap:1, split:true})
	     .add(centerPanel, {dir:'center'});
  //集中渲染显示
  viewport.render();

3.利用tab加载IFRAME页面示例

  //选择第一项
  tab.select(0);

至此,一个最基本的页面框架已经建好了!

演示地址:http://www.bgscript.com/bgjs/samples/basicframe.html

库JavaScript Tab控件应用文档

七月 20th, 2009 由 Rock 发表

说明:
Tab派生于选择容器类,由多个TabItem作为子项,TabItem有个panel作为显示页面,这个panel可以随意指定,在任意地方显示,除此以外,panel与Tab子项也没什么关系了.

依赖类文件:
如果打散发布,Tab类依赖以下文件类:
库:bglib.js
容器基类:container.js
Tab类:tab.js

Tab关键事件:
itemclick : 子项点击时触发
select : 子项选择前触发
selected : 子项选择后触发
close : 子项关闭前触发
closed : 子项关闭后触发

创建一个Tab:

  var tab = new CTab({showTo:document.body, autoRender:true});

添加TabItem项:

  tab.add(new CTabItem({title:'小强'}));

或通过数组添加子项:

  tab.fromArray([
    {title:'旺财'},
    {title:'小强', closeable:false}
  ]);

或在创建时通过指定array来添加子项:

  var tab = new CTab(
    showTo:document.body, 
    autoRender: true,
    array:[
    {title:'旺财'},
    {title:'小强'}
   ]);

或通过ajax json加载子项, 返回的json格式与array添加子项时格式一致:

  var tab = new CTab(
    showTo:document.body, 
    autoRender: true,
    autoConnect : true,
    url : 'http://www.bgscript/q?type=getTabItems'
   ]);

响应tab子项选择事件例子:

  tab.on('selected', function(item){
     alert('您选择了'+item.title+'项');
  });

响应tab子项关闭事件例子:

  tab.on('closed', function(item){
     alert('您关闭了'+item.title+'项');
  });

根据子项下标或子项ID选择子项:

  tab.select(0);
  tab.select('tabitemId');

结合子项panel例子,

<html>
<body>
  <div id="tab"></div>
  <div id="layer">
  <!-- 第一个tab子项的面板-->
  <div id="panel1">Tab 子项1的面板</div>
  <div id="panel2">Tab 子项2的面板</div>
  <div id="panel3">Tab 子项3的面板</div>
  </div>
</body>
<script>
  Event.ready(function(){
     var tab = new CTab(
        view:'tab' ,
        array:[
          {title:'子项1'}, panel:CC.$('panel1')},
          {title:'子项2'}, panel:CC.$('panel2')},
          {title:'子项3'}, panel:CC.$('panel3')}
        ]
     );
 
    tab.render();
  });
</script>
</html>

子项内容面板自动Ajax或IFRAME加载,只需在Tab控件中指定内容面板,设置itemAutoConnect即可:

   var contentPanel = new CPanel({view:'tabItemPanels'});
   var tab = new CTab({itemAutoConnect:true, contentPanel:contentPanel});
  //为Tab子项加多一个src属性,表明IFrame加载,url属性表示Ajax加载.
  tab.fromArray([
    {title:'Google', src:'http://www.google.com'},
    {title:'Bgscript', src:'http://www.bgscript.com'},
    {title:'Ajax Load', url:'http://www.bgscript.com/q?bg_q=changelog'}
  ]);

关于IFrame自动高度与滚动条问题
平时做WEB开发的时候,IFRAME在IE下自动高度通过CSS设置不了,(如果您有很好的方案,请告诉我,谢谢),不得不用脚本控制,如果IFRAME用多了控制就麻烦了. 在背光库中,解决方法是通过布局管理器来管理IFRAME的宽高,原理是当父容器宽高改变时更新子容器宽高.
就上面的例子,id为’layer’的DIV元素作为父容器, 引入CardLayout布局管理器,该布局管理器使得所有子控件在父容器宽高改变时同步自身宽高,使得子控件宽高始终与父容器保持一致.

   var contentPanel = new CPanel({view:'tabItemPanels', layout:'card'});
   var tab = new CTab({itemAutoConnect:true, contentPanel:contentPanel});
  //为Tab子项加多一个src属性,表明IFrame加载,url属性表示Ajax加载.
  tab.fromArray([
    {title:'Google', src:'http://www.google.com'},
    {title:'Bgscript', src:'http://www.bgscript.com'},
    {title:'Ajax Load', url:'http://www.bgscript.com/q?bg_q=changelog'}
  ]);

以后改变contentPanel宽高时,容器中的IFRAME就自动适应其高度.

查看演示:http://www.bgscript.com/bgjs/samples/tab.html

JavaScript日期选择控件

七月 12th, 2009 由 Rock 发表

一个简单,轻量级的日期选择控件实现类,也是背光控件库的默认类,解释一下它的构成和功能。
它继承CPanel类,所以很容易获得阴影效果,在改变位置时阴影也会跟随。

该控件不对日期过滤作过多的处理,但预留了一个接口回调函数,利用该接口可以自定义对日期的过滤。

支持mm/dd/yy,yy-mm-dd等多种格式转换.
日期控件已与表单集成了,CDatepickerField类作为表单元素以提供日期选择,具体可查看演示。

应用也简单,例如

//生成日期选择控件实例
var d = new CDatepicker({value: '2012/12/1',showTo: document.body, autoRender:true});
 
//生成用于表单日期选择的日期选择文本域
var field = new CDatepickerField({showTo: document.body, width: 300, autoRender: true});

查看演示:http://www.bgscript.com/bgjs/samples/datepicker.html

JavaScript - Grid表格控件

七月 6th, 2009 由 Rock 发表

表格控件,
列置换,列宽变,列排序,分组等等.
可自由扩展,如增加列选择,编辑功能等.
查看演示:

http://www.bgscript.com/bgjs/samples/grid.html

JavaScript树型控件

七月 1st, 2009 由 Bgser 发表

这是一个基于背光JS库设计的功能完整的JavaScript树型控件,并作为库基本控件的组成部分.
它结构设计分为三部分:树体,树根和树项.
实现类基于选择容器,外观与html模板参考Ext风格,可以自由定制其它html模板与外观,主要功能有:

  • 可方便扩展,与基于控件库的其它UI集成,combox, checkbox, radio, form等等
  • Ajax动态加载
  • 键盘选择导航
  • Drag & Drop,自定
  • 演示:http://www.bgscript.com/bgjs/samples/tree.html

    BackLight JS库实现Window窗体控件

    六月 11th, 2009 由 Bgser 发表

    用JavaScript编写一个完整功能Window窗体不容易,具有逼真阴影效果更不容易,
    现在发布一个由背光JS库来实现的一个CWin窗体类,基于CResizer,实现了一般窗体应有的功能.

    • 拖动
    • 四维缩放
    • 窗体客户区为CPanel,可应用各种布局
    • 事件列表

    预览地址:http://www.bgscript.com/bgjs/samples/window.html

    BackLight实现 Select - Combox 控件

    六月 11th, 2009 由 Bgser 发表

    • 支持键盘导航
    • 支持滚动跟进
    • 条目过滤
    • Ajax动态加载
    • 与Form集成,可直接提交
    • 与CSelectedContainer结合,可自定任意下拉框

    演示地址:http://www.bgscript.com/bgjs/samples/combo.html

    JavaScript代码在线格式化

    六月 9th, 2009 由 Bgser 发表



    JavaScript在线格式化工具不少,但实用的不多,现在提供一个不错的JavaScript代码格式化工具,出自jsbeautifier.org,已把它集成到网站上,并用BackLight库重写了界面,
    可点击这里查看效果,格式化想要的代码.

    利用库实现126邮箱框架界面原型

    五月 28th, 2009 由 Bgser 发表

    背光JS库控件(Beta)预览

    网易设计的界面是不错的,既花哨又具中国风,相信很多同学都喜欢.
    Demo地址:http://www.bgscript.com/bgjs/samples/126/126.html

    在Firefox下打开firebug可查看控件发出的各种事件.

    前端UI实践:背光(Background) UI Framework, 控件展示效果.

    背光JS库控件不限于126邮箱界面的实现,它可以在不改变其它库CSS的情况下,改写控件类的一些JavaScript便可实现其它库,诸如jQueryUI, Ext等控件展现效果.

    主要特性有:

    • 既可用于一般的WEB页面开发,也可构建相对复杂的RIA应用
    • 以面向对象的组件开发方式,结构清晰,尽量重用现有代码
    • 可以基于布局管理器的容器布局,自由布置容器控件
    • 采用控件行为,结构与外观相分离的设计模式,而不单是通常意义的行为与外观相分离
    • 在熟悉JS的情况下,即使不大会CSS,也可以利用现有资源实现所想控件
    • 在现有的应用基础上,自由方便扩展并开发出新的应用
    • 控件与后台Ajax加载原生集成,只需一个URL就可以完成所愿
    • 多数控件的实现是基于事件驱动的

    JavaScript常用函数库详解

    五月 22nd, 2009 由 Bgser 发表

    在WEB开发中,javascript提供了许多函数供开发人员使用,这些函数在Ajax流行前足够了,但要构建一个交互性强些的应用恐怕就麻烦了,为此,收集了自己平时常用到一些JavaScript函数,它们在其它的JS库也常见,现在整理并附上注释,方便查阅,希望对大家有所帮助。
    注:假设以下所有函数都放在一个CC对象中,方便引用。

      //这个方法相信是最常用的了,
      //它虽然没有选择器那么强大,但也有个小增强版,可查指定结点下ID所在的子元素
      function $(id, p) {
         //id是否是字符串,还是一个HTML结点
         var iss = id instanceof String || typeof id == "string";
         if (iss && !p)
             return document.getElementById(id);
         //如果是结点的话就直接返回该结点
         if(!iss)
            return id;
         //如果id与p是同一个元素,直接返回
         if(p.id == id)
            return p;
         //往父结点搜索
         var child = p.firstChild;
         while (child) {
            if (child.id == id)
                return child;
            //递归搜索
            var v = this.$(id, child);
            if (v)
               return v;
            child = child.nextSibling;
           }
        //的确找不到就返回null
        return null;
    }
      //Prototype.js里经典的一个函数,将一个可枚举对象数据组成一个数组返回。
      //如arguments参数数组这对象,返回后就可用数组的方法操纵这些数据。
      function $A(iterable){
         if (!iterable) return [];
         if (iterable.toArray) {
            return iterable.toArray();
         } else {
            var results = [];
            for (var i = 0, length = iterable.length; i < length; i++)
                results.push(iterable[i]);
             return results;
         }
      }
    each: function(object, callback, args) {
     
      if (!object) {
        return object;
      }
     
      if (args) {
        if (object.length === undefined) {
          for (var name in object)
          if (callback.apply(object[name], args) === false) break;
        } else for (var i = 0, length = object.length; i < length; i++) 
                    if (callback.apply(object[i], args) === false) break;
      } else {
        if (object.length == undefined) {
          for (var name in object)
          if (callback.call(object[name], name, object[name]) === false) break;
        } else for (var i = 0, length = object.length, value = object[0];
                        i < length && callback.call(value, i, value) !== false; 
                        value = object[++i]) {}
      }
     
      return object;
    }
    //数组
    function isArray(obj) {
      return (typeof obj === "array" || obj instanceof Array);
    },
    //字符串
    function isString(obj) {
      return (typeof obj === "string" || obj instanceof String);
    },
    //函数
    function isFunction(obj) {
      return (typeof obj === "function" || obj instanceof Function);
    },
    //数字类型
    function isNumber(ob) {
      return (typeof ob === "number" || ob instanceof Number );
    }
    // 返回表单可提交元素的提交字符串.
    // 例如 
    //  <form>
    //  <input type="text" name="user" value="rock" />
    //  <input type="text" name="password" value="123" />
    //  </form>
    // 调用后就返回 user=rock&password=123
    // 这些数据已经过encodeURIComponent处理,对非英文字符友好.
    // form元素中如果没有name,则以id作为提供字符名.
    function formQuery(f){
      // f,一个Form表单.
      var formData = "", elem = "", f = CC.$(f);
      var elements = f.elements;
      var length = elements.length;
      for (var s = 0; s < length; ++s) {
         elem = elements[s];
         if (elem.tagName == 'INPUT') {
             if ( (elem.type == 'radio' || elem.type == 'checkbox') && !elem.checked) {
                  continue;
             }
         }
         if (formData != "") {
            formData += "&";
         }
         formData += encodeURIComponent(elem.name||elem.id) + "=" 
                      + encodeURIComponent(elem.value);
       }
        return formData;
     }
    /**
     * 移除数组指定元素.
     * 参数既可传递一个整形下标,也可传递一个数组数据.
     */
    Array.prototype.remove = (function(p) {
      //参数为下标
      if (CC.isNumber(p)) {
        if (p < 0 || p >= this.length) {
          throw "Index Of Bounds:" + this.length + "," + p;
        }
        this.splice(p, 1)[0];
        return this.length;
      }
      //参数为数组数据,最终要找到下标来操作
      if (this.length > 0 && this[this.length - 1] == p) {
        this.pop();
      } else {
        var pos = this.indexOf(p);
        if (pos != -1) {
          this.splice(pos, 1)[0];
        }
      }
      return this.length;
    });
    Array.prototype.indexOf = (function(obj) {
      for (var i = 0, length = this.length; i < length; i++) {
        if (this[i] == obj) return i;
      }
      return - 1;
    });
    /**
     * 万能而简单的表单验证函数,这个函数利用了JS动态语言特性,看上去很神秘,
     * 实际是很形象的,查看个例子就清楚了.
     */
    validate: function() {
      var args = CC.$A(arguments),
      form = null;
      //form如果不为空元素,应置于第一个参数中.
      if (!CC.isArray(args[0])) {
        form = CC.$(args[0]);
        args.remove(0);
      }
      //如果存在设置项,应置于最后一个参数中.
      //cfg.queryString = true|false;
      //cfg.callback = function
      //cfg.ignoreNull
      //nofocus:true|false
      var b = CC.isArray(b) ? {}: args.pop(),
      d;
      var queryStr = b.queryString,
      ignoreNull = b.ignoreNull,
      cb = b.callback;
      var result = queryStr ? '': {};
      CC.each(args,
      function(i, v) {
        //如果在fomr中不存在该name元素,就当id来获得
        var obj = v[0].tagName ? v[0] : form ? form[v[0]] : CC.$(v[0]);
        //console.debug('checking field:',v, 'current value:'+obj.value);
        var value = obj.value,
        msg = v[1],
        d = CC.isFunction(v[2]) ? v[3] : v[2];
        //选项
        if (!d || typeof d != 'object') d = b;
     
        //是否忽略空
        if (!d.ignoreNull && (value == '' || value == null)) {
          //如果不存在回调函数,就调用alert来显示错误信息
          if (!d.callback) CC.alert(msg, obj, form);
          //如果存在回调,注意传递的三个参数
          //msg:消息,obj:该结点,form:对应的表单,如果存在的话
          else d.callback(msg, obj, form);
          //出错后是否聚集
          if (!d.nofocus) obj.focus();
          result = false;
          return false;
        }
        //自定义验证方法
        if (CC.isFunction(v[2])) {
          var ret = v[2](value, obj, form);
          var pass = (ret !== false);
          if (CC.isString(ret)) {
            msg = ret;
            pass = false;
          }
     
          if (!pass) {
            if (!d.callback) CC.alert(msg, obj, form);
            //同上
            else d.callback(msg, obj, form);
     
            if (!d.nofocus) obj.focus();
            result = false;
            return false;
          }
        }
        //如果不设置queryString并通过验证,不存在form,就返回一个对象,
        //该对象包含形如{elementName|elementId:value}的数据.
        if (queryStr && !form) {
          result += (result == '') ? 
             ((typeof obj.name == 'undefined' || obj.name == '') ? obj.id: obj.name) +
                      '=' + value: '&' + v[0] + '=' + value;
        } else if (!form) {
          result[v[0]] = value;
        }
      });
      //如果设置的queryString:true并通过验证,就返回form的提交字符串.
      if (result !== false && form && queryStr) result = CC.formQuery(form);
      return result;
    }
    /**
     * 应用对象替换模板内容
     * templ({name:'Rock'},'<html><title>{name}</title></html>');
     * st:0,1:未找到属性是是否保留
     */
    templ: function(obj, str, st) {
      return str.replace(/\{([\w_$]+)\}/g, function(c, $1) {
        var a = obj[$1];
        if (a === undefined || a === null) {
          if (st === undefined) return '';
          switch (st) {
          case 0:
            return '';
          case 1:
            return $1;
          default:
            return c;
          }
        }
        return a;
      });
    }

    2009年06月16日已作微调