XPath对象选择器

3.16.1  XPath对象选择器架构说明
XPath、CSS选择器的设计比较复杂,因此笔者这里先给出它的架构说明,如图3-31所示。
 
图3-31  XPath对象选择器架构说明

 

3.16.2  模式匹配函数定义说明

由于我们将处理和正则表达式模式定义在Array里,所以我们使用的都是匿名函数。

    // a是初始化的document.all或者上一个模式筛选后的HTML DOM对象Array集合
// m 是正则表达式匹配到的结果,通常我们从结果的m[1]开始使用
function(a, m)
{
return 返回筛选后的结果集,没有筛选到就返回空的Array对象;
}

3.16.3  基本正则表达式模式详解

这里,我们的每个模式都不区分大小写,都松散的允许在非关键字之间加任意的空格,这使得我们的匹配筛选更加灵活。另外,我们规定每个模式必须从XPath字符串的开头匹配,不允许有全局的匹配。因此,模式的框架是:

    /^\s*(这里是你写的模式)\s*/i
1.所有对象获取模式:*!![全角]
如图3-32所示,本模式相当于是将document. getElementsByTagName(“*”)的结果作为当前的选择器结果。
2.根节点的匹配
正则表达式一:匹配开始的“//”两个字符,字符的前后和中间允许有任意个不可见字符,如图3-33所示。
  
图3-32  所有对象获取模式:*!![全角]
 
图3-33  匹配开头的“//”
正则表达式二:匹配开始的“/descendent-or-self::node()/”字符串,其中“/”、“:”、“-”前后允许有任意个不可见字符,如图3-34所示。
 
图3-34  匹配开头的“/descendent-or-self::node()/”
3.谓词、伪模式的匹配
带参数模式匹配,匹配not(“[!type!=’text’]”)这样的字符串形式,其中非字母符号之间或前后允许有任意个不可见字符,如图3-35所示。
不带参数模式匹配,匹配“:input”、”:button”这样的字符串形式,其中冒号前后允许有任意个不可见字符,如图3-36所示。
 
图3-35  带参数的谓词、伪模式的匹配模式说明
 
图3-36  不带参数的谓词、伪模式的匹配
4.一次筛选多个结果并合并在一起
这个模式将匹配“input,select,button”、“#myDivId,button”、“<div><span>”这样的字符串形式,同样,非字母字符之间、前后允许有任意个不可见字符。如图3-37所示。
 
图3-37  一次筛选多个结果并合并在一起
5.对象和class的缩写形式匹配模式
本模式匹配“div.myClassName/”这样的字符串形式,同样,非字母字符之间、前后允许有任意个不可见字符。如图3-38所示。
 
图3-38  对象和class的缩写形式匹配模式
6.匹配属性的“有””、“没有”关系
这里匹配“[@class]”或者“[myvalue]”、“[!class]”的字符串形式,同样,非字母字符之间、前后允许有任意个不可见字符。如图3-39所示。
 
图3-39  匹配属性的“有”、“没有”关系
7.匹配属性的关系运算符号
这里匹配属性的比较关系,包括:等于[=]、不等于[!=或< >]、大于[>]、小于[<]、~=、^=、$=、*=、|=等。关于包含等关系,我们在伪模式里也实现了他们。
匹配的形式如:“[div @styleFloat='left']”或者“[@styleFloat!='left']”,其中“@”和引号可有可无,而非字母字符和值之外的符号之间可以有任意的不可见字符,并且关键字不区分大小写。如图3-40所示。
 
图3-40  匹配属性的关系运算符号

3.16.4  可扩展的转义及可扩展模块说明

本节中描述的定义和转义,多是支持可扩展的方式而设计的,这给使用的人员提供了更大的发展、创造的空间。
1.Object.opts操作符号的转义
是的,笔者也打算支持CSS3的相关属性选择器的功能,请看来自W3C的图片,如图3-41所示。

 
图3-41  W3C的CSS3 selectors
请参考http://www.w3.org/TR/css3-selectors/#selectors
本节属性选择运算符号的定义,请见表3-3。
表3-3  属性运算符号的定义

// 操作符号转换

Object.opts = {

       "=" : "=="      // 是否相等的逻辑符号

      , "<>" : "!="        // 不等号的转义

      , "gt" : ">"         // 大于的转义

      , "lt" : "<"         // 小于的转义

      , "eq" : "=="       // 等于的转义

    , "~=" : function(szParm, szAtt)

           {// 匹配属性szAtt,以空格分隔的其中一个等于,或者全等于szParm

             szParm = szParm.toLowerCase(), szAtt = szAtt.toLowerCase();

             return szParm == szAtt || -1 < szAtt.split (/\s+/).indexOf(szParm);

           }

 , "^=" : function(szParm, szAtt)

           {// 匹配属性szAtt,以szParm开头的节点

             return 0 == szAtt.toLowerCase().indexOf(szParm.toLowerCase());

           }

 , "$=" : function(szParm, szAtt)

           {// 匹配属性szAtt,以szParm结尾的节点

             szParm = szParm.toLowerCase(), szAtt = szAtt.toLowerCase();

             return (szAtt.length - szParm.length) == szAtt.lastIndexOf(szParm);

           }

 , "*=" : function(szParm, szAtt)

           {// 匹配属性szAtt,包含szParm的节点

             return -1 <

szAtt.toLowerCase(). indexOf(szParm.toLowerCase());

           }        

 , "|=" : function(szParm, szAtt)

           {// 匹配属性szAttszParm + _-】连接符号开始的节点

             szParm = szParm.toLowerCase(), szAtt = szAtt.toLowerCase();

             return 0 == szAtt.indexOf(szParm + "-") || 0 == szAtt.indexOf(szParm + "_");

           }

      };

这里主要定义几个一般人理解的操作符号到程序设计中的代码运算符号的转换,没有定义的,就沿用使用者传递进来的运算符号

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.Object.props属性名称的转义
表3-4  属性转义表

Object.props = {

                   "for": "htmlFor",

                   "class": "className",

                   "float": Object.styleFloat,

                   cssFloat: Object.styleFloat,

                   styleFloat: Object.styleFloat,

                   Float: Object.styleFloat,

                   innerHTML: "innerHTML",

                   value: "value",

                   readonly: "readOnly",

                   maxlength: "maxLength",

                   tagname:"tagName"

};

这里我们只定义几个常见的html代码的属性名和javascript中操作HTML DOM时的名字不一致的转义。也就是说,左边代码里冒号前的是html代码中的描述属性,冒号右边是JavaScript操作这个属性时的对应属性名。关于大小写的转换,我们再使用代码来完成。后面的代码解释中有描述。这里就不做更多的讲解了

 

 

 

 

 

 

 

3.Object.RegExps定义基本的模式单元
Object.RegExps = 数组[
数组的每一个单元又是一个数组[ 第一个元素是函数function(当前待过滤的对象数组,本模式匹配的结果)

    {
返回过滤后的结果数组。
},匹配到结果m的正则表达式一, 匹配到结果m的正则表达式N,….],
…..
];
做成数组形式的好处是,使用的人员就可以更好的给它扩展、增添更多的基本模式单元。其中,这里的谓词、伪模式匹配单元的函数里使用了下面小节中的定义描述。本书所讲的模式单元定义了如表3-5所示的几种模式。
表3-5  匹配模式说明

匹配的模式举例

   

/*/

/!/

/![全角]/

匹配当前节点【第一次使用时默认是document.all】下所有对象,包括注释、文本对象,代码中虽然没有支持深度迭代,但是支持了这个模式,其实已经等同于深度迭代了,比如://div:input,则匹配所有div下的所有输入对象。

后面两个主要匹配当前节点下的注释对象

//

/descendent-or-self::node()/

这两个模式用在XPath选择字符串的开始位置,用来选择当前页面中的所有对象,包括文本和注释对象,以便进入后面的模式匹配

select,button

#myDivId,button

<div><span>

匹配的多个结果合并为一个结果,以便进入下一个筛选。这里的每一个单词可以是TagName或者是id的名称

div.myClassName/

匹配当前结果集中的tagName是给定的TagName,有className为给定名称的所有对象。左边的形式是:tagName.className

[@class]

[myvalue]

[4]

匹配包含了给定属性的所有对象;

id名称为给定值的对象;

回结果中的第5个对象,下标是从0开始的,所以4就是第5的意思

[div @styleFloat='left']

[@styleFloat!='left']

 

在当前结果集中匹配指定TagName对象的属性满足表达式情况的所有对象;

或在当前结果集中匹配属性满足表达式情况的所有对象。另外,属性的选择支持CSS3中这样运算符号:

运算符号

   

~=

匹配属性szAtt,以空格分隔的其中一个等于,或者全等于szParm

例如:

J("table [class ~ = 'other']")

.each(function()

{

    alert(this.outerHTML)

});

^=

匹配属性szAtt,以szParm开头的对象。例如:

J("table [class ^ = MyTaBleClajss]")

.each(function()

{

    alert(this.outerHTML)

});

续表 

运算符号

   

$=

匹配属性szAtt,以szParm结尾的对象。例如:

J("table [class $= endClass]")

.each(function()

{

    alert(this.outerHTML)

});

*=

¹配属性szAtt,包含szParm的对象。例如:

J("table [class * = myTestGood]")

.each(function()

{

    alert(this.outerHTML)

});

|=

¹配属性szAttszParm + _-】连接符号开始的节点对象。例如:

J("table/td[lang | = \"en\"]")

.each(function()

{

    alert(this.outerHTML)

});

:input

:not('[tagName!=button]')

:not(:contains('事件'))

:get(' > 0 , < 100 ')

匹配上一个模式结果集中TagNameinputselecttextareabutton的所有对象;关于这样的伪模式后面还有详细介绍

/../p

匹配当前结果集中父对象是指定的TagName的所有对象,左边表示匹配TagNamep的所有对象

 

 

4.Object.mtps谓词、伪模式定义单元

    Object.mtps = Object对象{
小写的属性名字(见表3-6):处理谓词、伪模式的匿名函数function(当前集合对象o [, 模式传递进来的参数,如果有的话])
{
 返回处理后的结果集合Array对象。
}
};
如果这里的模式是第一个模式,则默认从当前页面中所有对象【包括文本和注释对象】里进行选择和迭代。
表3-6  谓词、伪模式说明

谓词、伪模式名称

   

:input

等于你写这样的模式"input,select,textarea,button"

匹配当前集合中的所有inputselecttextareabutton对象

:text

相当于当前集合中的"[@type=text]"模式的选择处理

: password

相当于当前集合中的"[@type= password]"模式的选择处理

: radio

相当于当前集合中的"[@type= radio]"模式的选择处理

:checkbox

相当于当前集合中的"[@type= checkbox]"模式的选择处理

: submit

相当于当前集合中的"[@type= submit]"模式的选择处理

: image

相当于当前集合中的"[@type= image]"模式的选择处理

: reset

相当于当前集合中的"[@type= reset]"模式的选择处理

: button

相当于当前集合中的"[@type= button]"模式的选择处理

: hidden

相当于当前集合中的"[@type= hidden]"模式的选择处理

: last

获取当前集合中的最后一个元素

:first

获取当前集合中的第一个元素

: even

从匹配的元素集中取序数为偶数的元素,包含第一个元素

: odd

从匹配的元素集中取序数为奇数的元素

:parent

: parent(n)

选择包含有子元素的元素,或者,如果指定为parent(3)则表示选择元素父节点的父节点的父节点,也就是往上3级的节点

:checked

选择checked属性为真的元素

: enabled

选择enabled属性为真的元素

:disabled

选择disabled属性为真的元素

:empty

选择没有子元素(包括text节点)的元素

: not(s)

将当前集合里符合not模式的元素排除掉,并返回新的集合。这个功能是相当强大的。例如:

"#myTestDiv/:input:not( '[tagName!=button]' )"

"#myTestDiv/:input:not(:contains('事件'))"

:get(s)

:get(n)

功能更强大的获取方式,s可以是下面形式中的一种,注意,下面说的位置都是从零开始。匹配的形式:

13-74~8,表示获取第三个到第七个,第四个到第八个,它们都包含三和七,四和八这个位置的对象,这样形式的也可以多对组合;

23,6,8或者[3,6,8],表示你要获取下标为3、6、8的对象。

3>6&<3,表示,获取下标大于6和小于3的对象,中间可以用"&,;|"来分隔。

 

使用举例:

"#myTestDiv/:input:get(' 2 - 3 ')"

"#myTestDiv/:input:get('1, 7,8;9')"

"#myTestDiv/:input:get(' > 0 , < 100 '):attct(['value','测试'])"

 "#myTestDiv/:input:get(' > 7 , < 1 ')"

 

续表 

谓词、伪模式名称

   

: gt(n)

返回序号大于n的对象的集合

:lt(n)

返回序号小于n的对象的集合

: attct(['属性名', '要包含的值'])

选择所有指定属性含有指定文本的元素

: contains(s)

选择所有含有指定文本的元素,主要指textvaluetextContentinnerText的内容

:hide

返回隐藏的对象,主要指style或者currentStyle里的displayvisibility属性的判断是否隐藏

: visible

返回style或者currentStyle里的displayvisibility属性设置为非“隐藏”的对象集合,当然包括未设置的对象

:frame

查找frameiframe对象,例如:

var a = J("//:frame");

// 设置不再对frameiframe对象进行深度迭代

   a.bNs = true;

   a.each(function()

   {

       // 对当前已经打开的页面进行绑定鼠标事件和键盘事件

       this.g_bKM = true;

   }).load(function()

   {

          // 在这里绑定鼠标事件

               alert(this.g_bKM || 'good')

   });

: ecnd(s)

用参数s中的XPath在当前集合中的每个元素里执行,然后再返回最后的结果集合。例如代码:

J("table[class=ecnds] :ecnd('tr:gt(1):even')").each(function()

{

 this.bgColor = 'red';

});

求当前页面中class等于ecnds的每个table里,从第2行【注意,行下标也是从0开始的】开始,也就是从除开标题行的奇数行TR对象进行迭代,并设置其背景颜色,也就是给当前页面的一些table设置隔行换色的功能

 

3.16.5  选择后的对象集合如何支持HTML事件的动态绑定
这里,我们从MSDN中抽取了一些事件的名称,采用一种简便的方式给Object原型增加绑定事件功能的支持,读者请注意,动态绑定事件本身是很耗时间的,那么笔者这里设计的XPath选择器为什么能更快地完成这个动作呢?请看下面的代码:
new function(){
['abort','activate','afterprint','afterupdate','beforeactivate','before
copy','beforecut','beforedeactivate','beforeeditfocus','beforepaste','
beforeprint','beforeunload','beforeupdate','blur','bounce','cellchange','
change','click','contextmenu','controlselect','copy','cut','dataavailable','
datasetchanged','datasetcomplete','dblclick','deactivate','drag','dragend','
dragenter','dragleave','dragover','dragstart','drop','error','errorupdate','
filterchange','finish','focus','focusin','focusout','help','keydown','keypress
','keyup','layoutcomplete','load','losecapture','mousedown','mouseenter','
mouseleave','mousemove','mouseout','mouseover','mouseup','mousewheel','move
','moveend','movestart','paste','propertychange','readystatechange','reset','
resize','resizeend','resizestart','rowenter','rowexit','rowsdelete','rowsinserted','
scroll','select','selectionchange','selectstart','start','stop','submit','unload']
.each(function()
{
try{
// 这里的this就等于前面串中的一个事件名
var k = "on" + this, szT = this;
   Object.prototype[szT] || (Object.prototype[szT] = (function(func)
   {
          // 这里上下文中的this就是当前的Object对象,
// 通常当前的this是需要绑定事件的HTML DOM多个对象的Array
        // 这里提取出来的目的是下面的迭代器里将要用到它
        // 迭代所有元素,都绑定func行为响应函数
        return this.each(function()
        {
          // 这里的this则是迭代进来的HTML对象
                // 而这里的匿名函数可以访问它的容器或更外面N层容器function
// 对象里的变量,除非像this这样
          // 的特殊对象需要先赋予一个变量外,其他的都可
// 以直接访问,注意,这里的技巧就是采用了:
window.setTimeout(fnAddEvent.apply(this,[this, k, func]), 10);
// 延迟10毫秒钟后进行绑定,这样就不影响主程序的运行性能了
        });
// 这里将当前的对象和HTML对象以及绑定行为的事件名一起bind进去,以便于操作
   }));
}catch(e){}
});
};

 

3.16.6  实现代码及使用举例
1.代码
JavaScript部分代码,请读者多看看其中的注释,这部分的代码将全部移到Jcore.js里,以便后面章节直接使用,请见下面的代码:

    // 我们为了让正则表达式工作得更快,因此笔者实现一个方便的
// 正则表达式编译方法,使得它更加简单易用
// 使得你不必再使用复杂、烦琐的compile方法
// 使用举例:var r /\s[a-z]\s/.cmp();
RegExp.reg = /(?:\/)([gmi]*$)/.compile("(?:\\/)([gmi]*$)", ""); 
RegExp.prototype.cmp = function()
{
var s = String(this).substr(1), m = RegExp.reg.exec(s);
return this.compile(s.substr(0, s.length - m[0].length), m[1] || "");
};
// 正则表达式集中管理和维护,
// 最主要的是进行预编译,使得运行更快
// 不过这里只集中那些在function中的,不是程序[js]装载时能编译的正则表达式
Object.jcoreRegs = [
/*00*/ /[^\x00-\xFF]/gm.cmp(),
/*01*/ /^[\da-z_\u4E00-\u9FA5]+[\da-z_\.-\u4E00-\u9FA5]*@[\da-z\u4E00- \u9FA5]+[\da-z\u4E00-\u9FA5_-]+(\.[\da-z\u4E00-\u9FA5_-]+)+$/i.cmp(),
/*02*/ /^[_a-z][_a-z0-9\.-]*@[_a-z0-9\.-]+(\.[_a-z0-9\.-]+)+$/i.cmp(),
/*03*/ /^(\d+)\.(\d+).(\d+).(\d+)$/.cmp(),
/*04*/ /^(?:\s*[,;\|]?\s*)(\d+)(?:\s*[-~]\s*)(\d+)(?:\s*)/.cmp(),
/*05*/ /^\s*\[?\s*\d+(\s*[,;\|&]\s*\d+)*\]?\s*/.cmp(),
/*06*/ /[\s\[\]]/g.cmp(),
/*07*/ /[,;\|&]/.cmp(),
/*08*/ /^(?:\s*)([><])(?:\s*)(\d+)(?:(?:\s*[;,\|&]\s*)([><])(?:\s*)(\d+))?/.cmp(),
/*09*/ /\s/gm.cmp(),
/*10*/ /(^['"])|(['"]$)/.cmp(),
/*11*/ /(\s*>\s*<\s*)|(^\s*<\s*)|(\s*>\s*$)/gm.cmp(),
/*12*/ /\s{2,}/gm.cmp(),
/*13*/ /(\s*>.<\s*)|(\s*[,;\|]\s*)/.cmp(),
/*14*/ /^[!!]/.cmp(),
/*15*/ /\d/g.cmp(),
/*16*/ /(^\s*)|(\s*$)/gm.cmp()
];
// 这段代码只在这里出现一次,然后就移进Jcore.js里
// 获取当前浏览器信息
var b = navigator.userAgent.toLowerCase();
// 浏览器
Object.browser = {
version: (b.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
safari: /webkit/.test(b),
opera: /opera/.test(b),
msie: /msie/.test(b) && !/opera/.test(b),
mozilla: /mozilla/.test(b) && !/(compatible|webkit)/.test(b)
};
// 特殊的Float属性处理
Object.styleFloat = Object.browser.msie ? "styleFloat" : "cssFloat",
// 属性转换对照,你可以扩展它来支持更多的特别属性,如果
Object.props = {
"for": "htmlFor",
"class": "className",
"float": Object.styleFloat,
cssFloat: Object.styleFloat,
styleFloat: Object.styleFloat,
Float: Object.styleFloat,
innerHTML: "innerHTML",
value: "value",
readonly: "readOnly",
maxlength: "maxLength",
tagname:"tagName"
};
// 浏览器类型检测
// 操作符号转换
Object.opts = {
"="  : "=="
, "<>" : "!="
, "gt" : ">"
, "lt" : "<"
, "eq" : "=="
, "~=" : function(szParm, szAtt)
{
// 匹配属性szAtt,以空格分隔的其中一个等于,或者全等于szParm
szParm = szParm.toLowerCase, szAtt = szAtt.toLowerCase;
return szParm == szAtt || -1 < szAtt.split(/\s+/).indexOf(szParm);
}
, "^=" : function(szParm, szAtt)
{
// 匹配属性szAtt,以szParm开头的节点
return 0 == szAtt.toLowerCase().indexOf(szParm.toLowerCase());
}
, "$=" : function(szParm, szAtt)
{// 匹配属性szAtt,以szParm结尾的节点
szParm = szParm.toLowerCase, szAtt = szAtt.toLowerCase;
return (szAtt.length - szParm.length) == szAtt.lastIndexOf(szParm);
}
, "*=" : function(szParm, szAtt)
{
// 匹配属性szAtt,包含szParm的节点
return -1 < szAtt.toLowerCase().indexOf(szParm.toLowerCase());
}        
, "|=" : function(szParm, szAtt)
{
// 匹配属性szAtt以szParm + 【_-】连接符号开始的节点
szParm = szParm.toLowerCase, szAtt = szAtt.toLowerCase;
return 0 == szAtt.indexOf(szParm + "-") || 0 == szAtt.indexOf (szParm + "_");
}
};
// 谓词、伪模式定义
Object.mtps = {
// 查找frame和iframe对象
"frame":function(o)
{
var aRst = [];
// 设置不再对frame和iframe对象进行深度迭代
aRst.bNs = true;
// 考虑到性能问题,这些方法我们在迭代的时候都取消了深度迭代
// 因为默认我们已经取所有的HTML对象进行迭代了,因此没有必要加深度迭代的加了后,
// 反而导致有的对象迭代多次,而push不得不改为add,这时候性能就更受到影响了
// 因为add为了防止将重复的对象加进去,虽然做了最大的优化始终不如不用深度迭代
return o.each(function()
{
switch(this.nodeName)
{
case 'FRAME':
case 'IFRAME':
aRst.push(this);
break;
}
}), aRst;
},
// 筛选输入对象
"input":function(o)
{
var aRst = [];
// 考虑到性能问题,这些方法我们在迭代的时候都取消了深度迭代
// 因为默认我们已经取所有的HTML对象进行迭代了,因此没有必要加深度迭代的加了后,
// 反而导致有的对象迭代多次,而push不得不改为add,这时候性能就更受到影响了
// 因为add为了防止将重复的对象加进去,虽然做了最大的优化始终不如不用深度迭代
return o.each(function()
{
switch(this.nodeName)
{
case 'INPUT':
case 'SELECT':
case 'TEXTAREA':
case 'BUTTON':
aRst.push(this);
break;
}
}), aRst;
},
// 筛选文本输入对象
"text":function(o)
{
var aRst = [], rg = /^text$/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.push(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: return XPath.bind({obj:o}, "[type=text]")()
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 筛选密码输入对象
"password":function(o)
{
var aRst = [], rg = /^password$/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.push(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: return XPath.bind({obj:o}, "[type=password]")()
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 筛选radio单选输入对象
"radio":function(o)
{
var aRst = [], rg = /^radio$/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.push(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: return XPath.bind({obj:o}, "[type=radio]")()
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 筛选checkbox多选输入对象
"checkbox":function(o)
{
var aRst = [], rg = /^checkbox$/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.push(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: return XPath.bind({obj:o}, "[type=checkbox]")()
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 筛选提交按钮输入对象
"submit":function(o)
{
var aRst = [], rg = /^submit$/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.push(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: return XPath.bind({obj:o}, "[type=submit]")()
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 筛选图形按钮输入对象
"image":function(o)
{
var aRst = [], rg = /^image$/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.push(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: return XPath.bind({obj:o}, "[type=image]")()
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 筛选reset重置按钮输入对象
"reset":function(o)
{
var aRst = [], rg = /^reset$/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.push(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: return XPath.bind({obj:o}, "[type=reset]")()
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 筛选按钮输入对象
"button":function(o)
{
var aRst = [], rg = /^button^/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.add(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: XPath.bind({obj:o}, "[type=button]")();
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 筛选不可见输入对象
"hidden":function(o)
{
var aRst = [], rg = /^hidden$/i.cmp();
o.each(function()
{
if(rg.exec(this['type']))aRst.add(this);
});
delete rg;
return aRst;
// 实际上我们可以这样: XPath.bind({obj:o}, "[type=hidden]")();
// 但是,往往有时候代码空间却能置换性能的空间,也就是说,多写些代码性能反而会更高
},
// 从匹配的元素集中取第一个元素
"last":function(o)
{
return o.get(o.size() - 1) || null;
},
// 从匹配的元素集中取第一个元素
"first":function(o)
{
return o.get(0) || null;
},
// 从匹配的元素集中取序数为偶数的元素,包含第一个元素
"even":function(o)
{
var aRst = [], i = 0;
o.each(function()
{
if(0 == i % 2)aRst.add(this);
i++;
});
return aRst;
},
// 从匹配的元素集中取序数为偶数的元素,不包含第一个元素
"odd":function(o)
{
var aRst = [], i = 1;
o.each(function()
{
if(0 == i % 2)aRst.add(this);
i++;
});
return aRst;
},
// 选择包含有子元素的元素,或者,如果指定为parent(3)则表示选择元素的父节点的
// 父节点的父节点,也就是往上3级的节点
"parent":function(o, n)
{
var aRst = [];
if(n)
{
o.each(function()
{
var oTmp = this, i = 0;
for(;n > i; i++)
{
if(!oTmp.parentNode)break;
oTmp = oTmp.parentNode;
}
if(i == n)aRst.add(oTmp);
});
}
else
{
o.each(function()
{
if((this.hasChildNodes && this.hasChildNodes()) || (this.childNodes && this.childNodes.length))aRst.add(this);
});
}
return aRst;
},
// 选择checked属性为真的元素。
"checked":function(o)
{
var aRst = [];
o.each(function()
{
// !!将表达式结果转换为有效的Boolean对象
if(true == !!this.getAttribute('checked'))aRst.add(this);
});
return aRst;
},
// 选择enabled属性为真的元素
"enabled":function(o)
{
var aRst = [];
o.each(function()
{
// !!将表达式结果转换为有效的Boolean对象
if(true != !!this.getAttribute('disabled'))aRst.add(this);
});
return aRst;
},
// 选择disabled的元素
"disabled":function(o)
{
var aRst = [];
o.each(function()
{
if(true == !!this.getAttribute('disabled'))aRst.add(this);
});
return aRst;
},
// 没有子元素(包括text节点)的元素
"empty":function(o)
{
var aRst = [];
o.each(function()
{
if((this.hasChildNodes && !this.hasChildNodes()) || !this.childNodes)aRst.add(this);
});
return aRst;
},
// 排除not(s)里指定的s规则的集合
"not":function(o, s)
{
var a = XPath.bind({obj:o},s)();
var aRst = [];
if(0 < a.length)
o.each(function()
{
if(-1 == a.indexOf(this))aRst.add(this);
});
else return o;
return aRst;
},
// 用参数s中的XPath在当前集合中每个元素里执行,然后再返回最后的结果集合
// each node
"ecnd":function(o, s)
{
var a = [];
if(0 < o.length)
s && o.each(function()
{
[].push.apply(a, [this].J(s));
}, false);
return a;
},
// 功能更强大的获取方式,s可以是下面形式中的一种,注意,下面说的位置都是从零开始
// 3-7或4~8,表示获取第三个到第七个,第四个到第八个,他们都包含三和七,
// 四和八这个位置的对象,这样的也可以多对组合
// 3,6,8或者[3,6,8],表示你要下标为3、6、8的对象
// >6&<3,表示,获取下标大于6和小于3的对象,中间可以用"&,;|"来分隔
"get":function(o, s)
{
var aRst = [], i = 0, m = null, r;
              // 特别需要注意的是,下面的if判断的模式是要注意顺序的,
// 认为第二个if中的模式也能匹配第二个
// 处理 3-7或4~8这样的形式
// 这里看上去似乎是不正确的写法,正确的应该是null != (m = /^(?:\s*[,;\|]?\s*)(\d+)(?:\s*[-~]\s*)(\d+)(?:\s*)/.exec(s))
// 不过,我们大可以像下面这样简化它的
// 我们先给模式r变量赋予值,然后用逗号运算符号连接,使得m是最终的if判断的对象
if(r = Object.jcoreRegs[4],m = r.exec(s))
{
var bStart = false;
if(m[2] > m[1])
{
o.each(function()
{
if(i >= m[1] && i <= m[2])bStart = true,aRst.add(this);
// 加了标志是为了减少没必要的迭代
else if(bStart)throw {message:'调处each', jname:"J"};
i++;
});
}
s = s.replace(r, '');
while(m = r.exec(s))
{
bStart = false;
if(m[2] > m[1])
{
o.each(function()
{
if(i >= m[1] && i <= m[2])bStart = true,aRst.add(this);
// 加了标志是为了减少没必要的迭代
else if(bStart)throw {message:'调处each', jname:"J"};
i++;
});
}
s = s.replace(r, '');
if(0 == s.length)break;
}
}
// 处理3,6,8或者[3,6,8],表示你要下标为3、6、8的对象
else if(Object.jcoreRegs[5].exec(s))
{
s = s.replace(Object.jcoreRegs[6], '').split(Object.jcoreRegs[7]);
o.each(function()
{
if(-1 < s.indexOf(i))aRst.add(this);
i++;
});
}
// 处理这样的形式:>6&<3
else if(r = Object.jcoreRegs[8], m = r.exec(s))
{
var szStr = '';
for(i = 1; i < m.length - 1; i += 2)
{
if(1 < i)szStr += "||";
szStr += "(i" + m[i] + m[i + 1] + ")";
}
var oFun = new Function("i", "return " + szStr.replace(Object.jcoreRegs[9], ''));
i = 0;
o.each(function()
{
if(oFun(i))aRst.add(this);
i++;
});
delete oFun;
}
delete r;
delete m;
              return aRst;
},
// 返回序号大于s的
"gt":function(o, s)
{
return o.slice(parseInt(s));
},
// 返回序号小于s的
"lt":function(o, s)
{
return o.slice(0, parseInt(s));
},
// 选择所有指定属性含有指定文本的元素。
// 因此s的格式是: ['属性名', '要包含的值']
"attct":function(o, s)
{
// 这里我们将串行的Array对象转换到Array对象,当然用eval也是可以的
s = s.replace(/[\[\'\"\]\s]/g.cmp(), "").split(",");
var aRst = [], rg = new RegExp(s[1], "gi").cmp();
if(s[0] = s[0] || null)
o.each(function()
{
// Object.props[s[0]] || s[0]如果没找到转义的属性名就用原来的名字
if(rg.exec(this.getAttribute(Object.props[s[0]] || s[0]) || null))aRst.add(this);
});
              return aRst;
},
// 选择所有含有指定文本的元素,主要指text和innerText的内容
"contains":function(o, s)
{
var aRst = [], rg = new RegExp(s, "gmi").cmp();
o.each(function()
{
// 如果节点是注释对象,则有text,如果是非IE浏览器,则可能有innerText
if(rg.exec(this.innerText || this.value || this.text || this.textContent || ''))aRst.add(this);
});
delete s;
delete rg;
return aRst;
},
// 返回隐藏的对象
"hide":function(o)
{
var aRst = [];
o.each(function()
{
var oStyle = this.style || this.currentStyle || {};
var szHd = oStyle.display || oStyle.visibility || '';
if("hidden" == szHd || "none" == szHd)aRst.add(this);
});
return aRst;
},
// 返回可见的对象
"visible":function(o)
{
var aRst = [];
o.each(function()
{
var oStyle = this.style || this.currentStyle || {};
var szHd = oStyle.display || oStyle.visibility || '';
if("hidden" != szHd && "none" != szHd)aRst.add(this);
});
return aRst;
}
};
// 正则表达式集合,集中写在这里便于扩展
// 第一个元素是要执行的函数代码,方法的第一个参数a表示当前可迭代的对象,
// m是正则表达式执行后的结果
// 函数返回的结果将作为下一次的传入a,因此返回的结果必须是可迭代的对象
// 每个模式力求功能单一,这样便于维护
// 后面则是支持的多种正则表达式对象
// 注意,每个正则表达式会循环使用,因此请注意,每个必须从头开始匹配
// 一旦匹配到了这段匹配到的串就会从源中删除,进入下轮处理,所以每个匹配不能使用全局
// 另外,每个正则表达式会在while里进行处理,每次使用匹配到的位置为1的结果,
// 直到没有匹配才停止
// a默认就是当前的HTML Dom, 初始化是document.all
Object.RegExps = [
// 包括html前的<!-- ... -->注释和!DOCTYPE在内的匹配
// 匹配模式是:开头[*!!]中任一个
[function(a, m)
{
// 匹配:/*/
var aRst = [];
return a.each(function()
{
this['getElementsByTagName'] && aRst.add(_A(this.getElementsByTagName(m[1]) || this)) ||
this.childNodes && aRst.add(_A(this.childNodes));
}), aRst;
}, /^(?:\s*\/?\s*)([\*!!])(?:\s*\/?\s*)/.cmp()]
// 匹配: // , /descendent-or-self::node()/,等于document.documentElement,包含html在内的节点
, [function(a, m)
{
return _A(document.getElementsByTagName("*"), true);
}, /^(\s*\/\s*){2}/.cmp(), /^\s*\/\s*descendent\s*-\s*or\s*-\s*self\s*:\s*:\s*node\s*\(\s*\)\s*\/\s*/i.cmp()]
// 匹配: ":input"这种形式的谓词
, [function(a, m)
{
// 匹配到的临时结果
var aMyTmp = [], szParm = m[3] || "";
m[1] = Object.mtps[m[1].trim().toLowerCase()];
if(m[1])
{
aMyTmp = m[1](a, (szParm || '').replace(Object.jcoreRegs[10], ''));
delete m[1];
}
return aMyTmp;
}, /^(?:\s*:\s*)([\w-]+)(?:\s*\(\s*)('?|"?)(?:\s*)(.*)(?:\s*)\2(?:\s*\)\s*)(?:\s*\/?\s*)/i.cmp(), /^(?:\s*:\s*)([\w-]+)(?:\s*\/?\s*)/i.cmp()]
// 匹配select,button或#myDivId,button或<div><span>
, [function(a, m)
{
// 匹配到的临时结果
var aMyTmp = [], aM;
aM = m[1].trim().replace(Object.jcoreRegs[11], '').replace(Object.jcoreRegs[12], ' ').split(Object.jcoreRegs[13]);
delete m[1];
// 如果传进来的是个数组,则对它进行迭代
if(0 < aM.length)
{
aM.each(function()
{
var _this = this, rg = new RegExp("^(" + _this + ")$", "i").cmp();
// 对要获取的同级对象进行获取并合并结果
a.each(function(o, i)
{
!!rg.exec([ this.nodeName || '',
this.getAttribute && (this.getAttribute('id') || this.getAttribute('uniqueID')) || '',
this.className || ""].join("|")) && aMyTmp.add(this);
// 改变fnGetObjs的当前对象为_this,
//  Array的add方法兼容了HTML DOM的一些操作,请参加Array章节
aMyTmp.add(fnGetObjs.call(this, _this));
});
});
}
return aMyTmp;
}, /^(?:\s*\/?\s*#?)((?:\s*<?\s*[\w-]+\s*>?\s*[;,\|]?\s*)+)(?:\s*\/?\s*)/i.cmp()]
// 匹配:../,当前集合的父节点对象的集合
, [function(a, m)
{
var aMyTmp = [];
a.each(function()
{
aMyTmp.add(this.parentNode);
});
delete a;
return aMyTmp;
}, /^(\s*\/?\s*\.\s*\.\s*\/?\s*)/i.cmp()]
// 匹配:div.myClassName/
, [function(a, m)
{
m[1] = !!m[1] && m[1] || m[2] || "";
var aMyTmp = [];
// 如果传进来的是个数组,则对它进行迭代
if(0 < m[1].length)
{
var rg = new RegExp(m[1], "i").cmp();
a.each(function()
{
if(!!rg.exec((this.nodeName || ",") + "|" +
(this.id || this.uniqueID || ',') + "|" +
(this.className || ",")))
aMyTmp.add(this);
// 将执行结果合并到临时数组里
aMyTmp.add(fnGetObjs.call(this, m[1]));
});
if(0 < aMyTmp.length)a = aMyTmp;
}
// 对class属性进行过滤
var szPpt = Object.props["class"] || "className";
m[2] = m[2].toLowerCase();
delete aMyTmp, aMyTmp = [];
a.each(function()
{
// 这里过滤掉了文本节点
// if(3 != this.nodeType)
if(this.getAttribute && this.getAttribute(szPpt).toLowerCase() == m[2])aMyTmp.add(this);
});
return aMyTmp;
}, /^(?:\s*\/?\s*)([\w-]*)(?:\s*\.\s*)([\w-]*)(?:\s*\/?\s*)/i.cmp()]
// 匹配:[@class]或者[myvalue],匹配有指定属性但是却没有指定值对象或nodeName,或ID
, [function(a, m)
{
var aMyTmp = [], bNot = false, rg = Object.jcoreRegs[14];
// HTML 对象 nodeName、ID、属性名
m[1] = m[1].trimAll();
// 支持div[4]这样的形式
if(0 == m[1].replace(Object.jcoreRegs[15], "").length)
{
// 匹配到的临时结果
if(0 <= m[1] && m[1] <= a.length)
return [a[m[1]]];
return [];
}
     if(rg.exec(m[1]))
bNot = true, m[1] = m[1].replace(rg, "");
a.each(function()
{
// 将执行结果合并到临时数组里
aMyTmp.add(fnGetObjs.apply({obj:this}, [m[1]]));
});
// 如果不是选择属性模式就返回了
if(0 < aMyTmp.length)return aMyTmp;
// 再做初始化
delete aMyTmp, aMyTmp = [];
// 对象属性名,如果转义存在则转义,否则直接使用原来的名字
var szPpt = Object.props[m[1]] || m[1];
a.each(function()
{
// 不包含给定属性或者需要包含
// 这里过滤掉了文本节点 if(3 != this.nodeType)
if((!bNot && this.getAttribute(szPpt)) || (bNot && !this.getAttribute(szPpt)))aMyTmp.push(this);
});// 这里本来必须启用深度迭代,不过由于我们在外边已经准备好要迭代的元素,
// 为了防止深度递归调用而影响性能,我们取消了所有的深度迭代
// 释放内存是很好的习惯
delete szPpt;
return aMyTmp;
}, /^(?:\s*\/?\s*\[\s*@?\s*)(!?\s*[\w-]+)(?:\s*\]\s*\/?\s*)/i.cmp()]
// 匹配:[div @styleFloat='left']或者[@styleFloat!='left']
, [function(a, m)
{
var aMyTmp = [];
if(4 > m.length)return a;
// HTML 对象 nodeName
m[1] = m[1].trimAll();
// HTML 对象属性名
m[2] = m[2].trimAll();
// 如果没取到属性名,表示属性名已经落到对象名上
m[2] || (m[2] = m[1], m[1] = "");
// HTML 对象属性操作符号
m[3] = m[3].trimAll();
// 操作符号需要转义,如果没有定义相应的转义就直接使用
m[3] = Object.opts[m[3]] || m[3];
// HTML 对象属性操作的值
m[4] = m[4].toLowerCase();
// 如果只是[@styleFloat!='left']/span这样的形式,m[1]是可能为空的
// 如果传进来的是个数组,则对他进行迭代
if(0 < m[1].length)
{
a.each(function()
{
// 将执行结果合并到临时数组里
aMyTmp.add(fnGetObjs.apply({obj:this}, [m[1]]));
});
if(0 < aMyTmp.length)a = aMyTmp;
// aMyTmp2 = null;也可以,不过我们测试发现delete性能比直接赋予null更优秀
}
// "? (expression): (expression);"三目运算符号中的(expression)
// 可以随便给个字符,
// 表示给一个为定义的变量,等于用 undefined,或者用''一对引号也可以
// 对属性进行过滤,m[2] 属性名称, m[3] 是运算符号, m[4] 是属性值
delete aMyTmp, aMyTmp = [];
// 对象属性名,如果转义存在则转义,否则直接使用原来的名字
var szPpt = Object.props[m[2]] || m[2];
// 定义操作函数对象,注意new Function这样的形式的上下文是不同它的容器的,
// 这个匿名函数不一样
// 因为这里需要组装代码,所以我们必须用new Function这样的动态形式
var szCode = "return (s||'').toLowerCase()" + m[3] + "'" + m[4] + "'";
try
{
var oFunc = Function == m[3].constructor && m[3] || new Function("szParm", "s", szCode);
a.each(function()
{
// 这里过滤掉了文本节点 if(3 != this.nodeType)
if(oFunc(m[4], this.getAttribute(szPpt)))aMyTmp.push(this);
});
}catch(e){}
// 释放内存是很好的习惯
delete oFunc;
delete szPpt;
return aMyTmp;
}, /^(?:\s*\/?\s*\[\s*)([\w-]*)(?:\s*@?\s*)([\w-]*)(?:\s*)([=!<>\s\^~$\*\|]+)(?:\s*['"]?\s*)([\w-\(\)]+)(?:\s*["']?\s*\]\s*\/?\s*)/i.cmp()]
];
// 功能描述:将当前对象toString或者传入的第一个参数toString或转换为
//  html中能正确显示的文本,也就是把html代码转换为能在页面上正确显示的文本
// 返回信息:元素的个数
// 使用指南:[].size(); // 等于0
// 应用范围:各种Web客户端
Object.prototype.sanitizeHTML = function(s)
{
var d = document.createElement('div');
d.appendChild(document.createTextNode(s || this.toString()));
return d.innerHTML;
};
// 功能描述:将当前对象传递到callback回调函数里执行
// 使得XPath选择器更加方便地对临时结果进行处理,然后再进行其他
//  XPath选择器处理,这样就使得每个XPath选择不必都从所有对象中进行检索
// 从而提高了性能,它区别于every的是,它将整个对象作为一个参数传递给回调函数
// 而不像every,它接收的是对象的单个元素。every是第4章的内容
// 返回信息:返回当前的对象
// 使用指南:[12,33.98, 887].getAll(function(){}).each(function(){});
// 应用范围:各种Web客户端和服务器端
Object.prototype.getAll = function(callback)
{
callback && callback.apply(this, [this]);
return this;
};
// Xpath选择器的设计
var XPath = Object.prototype.XPath = function(s1)
{
// if(0 == arguments.length)return new XPath(this.toString());
// 正则表达式
var s = (s1 || this.toString() || "").trim(), a, m, x, i, oT;
if(0 == s.length)return [];
// 结果数组a, 模式匹配结果m, this.obj是为了支持指定开始选择的接点对象
var atmp = [];
// 如果节点太多,建议使用更详细的xpath选择器,而不应该依赖智能的选择,
// 因为越智能也就越消耗性能
// 因此这里当节点大于300就把根作为默认开始节点
a = this.length && this ||
_A( this.obj ||
(atmp = (this.win || window).document.getElementsByTagName("*"),
300 < atmp.length ? (this.win || window).document.documentElement : atmp)
);

// 定义一个标签,表示我们将在多重循环的最里面停止我们最外面的循环
var nF = Object.RegExps.length % 8, n1, oN = nF, j, oMyRg, oFn;
// 注意,JavaScript也支持Labeled 语句,不过在firefox中不支持,
// 因此真正的示例中我们没有这样做
ExitAllDo:
do
{
 i = 0;
 n1 = nF, j = 0;
 // 执行 取8余数的零头的循环
 do
{
  // 初始化
  if(j >= Object.RegExps.length)j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     oFn = oMyRg[0];
     if( null != (m = oMyRg[x].exec(s)))
        {
        i++;
        // 模式方法执行返回的结果
        oT = new oFn(a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
}while(j++,0 < n1--);

 n1 = (Object.RegExps.length - oN) / 8;
 // 八分法开始
 do
{
  // 初始化: 1
  if(j >= Object.RegExps.length) j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     if(null != (m = oMyRg[x].exec(s)))
        {
        i++;
        // 模式方法执行返回的结果
        oT = new Object.RegExps[j][0](a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
   j++;
  
   // 初始化: 2
   if(j >= Object.RegExps.length) j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     if( null != (m = oMyRg[x].exec(s)))
        {
        i++;
        // 模式方法执行返回的结果
        oT = new Object.RegExps[j][0](a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
   j++;
  
   // 初始化: 3
   if(j >= Object.RegExps.length) j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     if( null != (m = oMyRg[x].exec(s)))
        {
        i++;
        // 模式方法执行返回的结果
        oT = new Object.RegExps[j][0](a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
   j++;
  
   // 初始化: 4
   if(j >= Object.RegExps.length) j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     if( null != (m = oMyRg[x].exec(s)))
        {
        i++;
        // 模式方法执行返回的结果
        oT = new Object.RegExps[j][0](a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
   j++;
  
   // 初始化: 5
   if(j >= Object.RegExps.length) j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     if( null != (m = oMyRg[x].exec(s)))
        {
        i++;
        // 模式方法执行返回的结果
        oT = new Object.RegExps[j][0](a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
   j++;
  
   // 初始化: 6
   if(j >= Object.RegExps.length) j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     if( null != (m = oMyRg[x].exec(s)))
        {
        i++;
        // 模式方法执行返回的结果
        oT = new Object.RegExps[j][0](a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
   j++;
  
   // 初始化: 7
   if(j >= Object.RegExps.length) j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     if( null != (m = oMyRg[x].exec(s)))
       {
        i++;
        // 模式方法执行返回的结果
        oT = new Object.RegExps[j][0](a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
   j++;
  
   // 初始化: 8
   if(j >= Object.RegExps.length) j = 0;
   x = 1;
   // 如果当前是多个组合:text,select这样的形式,则认为需要合并结果
   // 每个匹配允许有多个正则表达式的匹配
   do
   {
     oMyRg = Object.RegExps[j];
     if( null != (m = oMyRg[x].exec(s)))
        {
        i++;
        // 模式方法执行返回的结果
        oT = new Object.RegExps[j][0](a, m);
        // 下一轮的结果从它开始选择
        switch(oT.constructor)
        {
          case Array:
             a = oT;
             break;
          default: (a = []).push(oT);
        }
        s = s.replace(oMyRg[x], "");
        delete oT;
        break;
     }
     if(0 == s.length || 0 == a.length)break ExitAllDo;
   }while(++x < oMyRg.length)
   j++;
}while(0 < n1--);
// 如果匹配完成,或者整个模式都没有任何匹配就退出
 // 防止死循环是设计中应该常常考虑的
if(0 == s.length || 0 == i || 0 == a.length)break;
}while(s.length > 0)
return a;
};
测试的iframe文件“ifrm.jsp”的内容:
<body>
<button class="myTestGood" name="myTest" id="myTest">测试绑定事件1</button> <br><br>
<button name="myTest1" id="myTest1" >绑定前面按钮的onmouseover1</button><br>
<button name="myTest1" class="myTestGood">卸载onmouseover前面按钮的onmouseover1</button><br>
</body>
2.测试用例
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><html>
<head><meta http-equiv=content-type content="text/html; charset=utf-8">
<title>测试”打造“完美中的完美“的HTML DOM 对象XPath对象选择器”</title>
</head>
<body>
<div id="myTestDiv">
<input type=text name=testtext>
<input type=text name=text2>
<input type=radio name=dfgdsgdg value=3242424 checked='true'>
<select><option>test Option</select>
<textarea>textarea value</textarea>
<button class="myTestGood" name="myTest" id="myTest" onclick="kkkkkkk()">测试绑定事件</button><br><br>
<button name="myTest1" id="myTest1" onclick="fnAddEvent(J('myTest')[0], 'onmouseover', fnTestOnmouseover)">绑定前面按钮的onmouseover</button><br>
<button name="myTest1" class="myTestGood" onclick="fnDelEvent(J('myTest')[0], 'onmouseover',fnTestOnmouseover)">卸载onmouseover前面按钮的onmouseover</button><br>
这里不难可以看出,我们可以多次点这个按钮,多次绑定相同的函数,<br>
而这个事件也会同时多次响应,这完全符合我们的要求
<iframe src="ifrm.jsp" style="width:100%;height:150px"></iframe>
</div>
<button onclick="fnMyTest()">点这里开始测试</button>
<script type="text/javascript" language="JavaScript" src="/jcore/resource/javascript/Jcore.js"></script>
<script type="text/javascript" language="JavaScript">
<!--
function kkkkkkk()
{
var oIpt = J("//:input");
var oText = oIpt.XPath(":text");
alert(oText[0].outerHTML)
alert([oIpt.length, oText.length].join("\n"))
}
function fnTestOnmouseover()
{
alert("good");
}
"//textarea".XPath()[0].value = "我的测sdf试"; // .each(function(){this.value = "我的测试"});
// 统一给筛选出来的对象加事件响应
// 更好的是,你的函数里的this,也就是上下文,我已经帮你设置为触发事件的对象了
// 这对你来说是非常好的哦,同时还支持链式的调用
"#myTestDiv/*/:input:get('1, 7,8;9')".XPath().click(function()
{
alert(this.outerHTML);
}).mouseover(function()
{
this.style.color = "red";
}).mouseout(function()
{
this.style.color = "";
});
J("input[@type=radio][@checked]").each(function()
{
alert(this.outerHTML)
});
// 测试函数
function fnMyTest()
{
// 这里我们定义了一个临时的数组,每个元素是我们定义的一个文本传
// 每个都是一个典型的选择器测试
[
// 匹配所有button对象的上一级父对象的集合
"bUtToN:parent(1)",

// 选择id为myTestDiv下的所有元素中,name等于myTest1
// 的元素集合中,有class属性的对象集合
"#myTestDiv/*/[name='myTest1']/[@class]",

// 选择id为myTestDiv下的所有元素中,tagName为input的集合里,
// type属性等于text的对象的集合
"#myTestDiv/*/:text",

// 选择id为myTestDiv下的所有元素中,tagName为button,input,
// textarea,select的所有对象的集合
"#myTestDiv/*/button,input,textarea,select",

// 选择当前页面[不包含iframe]中所有button集合里,
// name等于myTest1的对象中有class属性的对象集合
"button/[name='myTest1']/[@class]",

// 功能同上,不过要包含iframe和frame里的对象
"//button/[name='myTest1']/[@class]",

// 功能同上面的:"#myTestDiv/button,input,textarea,select"
// "#myTestDiv/*/:input",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]中没有class属性的对象集合,包含iframe和frame里的对象
"#myTestDiv/*/:input/[!class]",

// 松散匹配所有button对象中class等于myTestGood的对象集合中,名字等于myTest的集合
"bUtToN.myTestGood/[ @ name = \" myTest \" ]",     

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]中排除开not里模式匹配的集合,特别注意的是
// 这里的not里等于所有不是button的的对象,因此在加他前面的排除
// 就等于选择所有的button对象了,当然这里也支持iframe和frame的
"#myTestDiv/*/:input:not( '[tagName!=button]' )",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]中可见的对象
"#myTestDiv/*/:input:visible",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]中文本里包含了'事件'的对象集合
"#myTestDiv/*/:input:contains('事件')",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]中文本里不包含了'事件'的对象集合
"#myTestDiv/*/:input:not(:contains('事件'))",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]中属性class里包含了'Good'的对象集合
"#myTestDiv/*/:input:attct(['class','Good'])",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]集合数组里,下标为2到3的对象集合
"#myTestDiv/*/:input:get(' 2 - 3 ')",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]集合数组里,下标为1,7,8,9的对象集合
"#myTestDiv/*/:input:get('1, 7,8;9')",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]集合数组里,下标大于0小于100的集合里属性value包含了'测试'的所有对象的集合
"#myTestDiv/*/:input:get(' > 0 ,  < 100 '):attct(['value','测试'])",

// 选择id为myTestDiv下所有的输入对象[button,input,textarea,
// select]集合数组里,下标大于7小于1的分段集合
"#myTestDiv/*/:input:get(' > 7 ,  < 1 ')",

// 支持button[3]这样的形式
"//div/*/button[1]"
].each(function()
{
   alert("开始:" + this);
   this.XPath().each(function()
{
 var oStyle = this.style || {}, szCur = this.currentStyle.cssText;
 oStyle.border = "1px solid red";
     alert(this.outerHTML);
     oStyle.cssText = szCur;
});
});
}
-->
</script>
</body>
</html>

posted @ 2009-12-19 13:03  awp110  阅读(714)  评论(0编辑  收藏  举报