2 * XMLWrapper_find 周りの オリジナルコードに関する情報
\r
3 * Original code by pettanR team
\r
4 * - https://osdn.jp/projects/pettanr/scm/git/clientJs/blobs/master/0.6.x/js/02_dom/08_XNodeSelector.js
\r
6 * Original code by ofk ( kQuery, ksk )
\r
7 * - http://d.hatena.ne.jp/ofk/comment/20090106/1231258010
\r
8 * - http://d.hatena.ne.jp/ofk/20090111/1231668170
\r
10 * TODO X.Class で作り、kill を強要する
\r
12 X[ 'XML' ] = XMLWrapper;
\r
15 * XML ツリーを探索して読み出す用のラッパークラスです.XML を操作する機能はありません、あしからず…
\r
17 * @class XML 探索用のラッパークラスです
\r
19 * @param {XMLElement}
\r
21 function XMLWrapper( xml ){
\r
24 this.tagName = xml.tagName;
\r
29 * tagName または nodeName
\r
30 * @alias X.XML.prototype.tagName
\r
33 XMLWrapper.prototype.tagName = '';
\r
35 * ラップした xml の数 常に1または0, XMLList の場合2以上
\r
36 * @alias X.XML.prototype.length
\r
39 XMLWrapper.prototype.length = 1;
\r
40 XMLWrapper.prototype[ 'parent' ] = XMLWrapper_parent;
\r
41 XMLWrapper.prototype[ 'has' ] = XMLWrapper_has;
\r
42 XMLWrapper.prototype[ 'get' ] = XMLWrapper_get;
\r
43 XMLWrapper.prototype[ 'val' ] = XMLWrapper_val;
\r
44 XMLWrapper.prototype[ 'find' ] = XMLWrapper_find;
\r
47 * 親要素を返す、ルート要素の場合 null を返す
\r
48 * @alias X.XML.prototype.parent
\r
49 * @return {X.XML} 親要素
\r
51 function XMLWrapper_parent(){
\r
52 if( this.length === 1 ) return this._rawXML && this._rawXML.parentNode ? new XMLWrapper( this._rawXML.parentNode ) : null;
\r
53 if( this.length === 0 ) return null;
\r
55 return this[ 0 ].parentNode ? ( new XMLWrapper( this[ 0 ].parentNode ) ) : null;
\r
60 * @alias X.XML.prototype.has
\r
61 * @param {string} queryString XML セレクター文字列
\r
64 function XMLWrapper_has( queryString ){
\r
65 return !!this.find( queryString ).length;
\r
69 * <p>X.XML では常に自信を返す
\r
70 * <p>X.XMLList ではラップした xml 群から index のものを返す
\r
71 * @alias X.XML.prototype.get
\r
72 * @param {number} index
\r
73 * @return {X.XML} X.XML では自身、X.XMLList では index の X.XML
\r
75 function XMLWrapper_get( index ){
\r
76 if( this.length === 1 ) return this;
\r
77 if( this.length === 0 ) return null;
\r
78 // 一度発行した XMLWrapper は控えて置いて再発行する。
\r
79 if( this._wraps && this._wraps[ index ] ) return this._wraps[ index ];
\r
80 if( !this._wraps ) this._wraps = [];
\r
81 return this[ index ] ?
\r
82 ( this._wraps[ index ] = new XMLWrapper( this[ index ] ) ) :
\r
87 * セレクターにヒットした要素の内容を指定されたデータ型で返す。複数要素にヒットした場合、0番目の要素の内容を使用する。
\r
88 * @alias X.XML.prototype.val
\r
89 * @param {string} queryString XML セレクター文字列
\r
90 * @param {string} type 'number','int','boolean','string'
\r
91 * @return {boolean|number|string} 内容を型変換した値
\r
93 function XMLWrapper_val( queryString, type ){
\r
94 var //attr_textContent = X_UA[ 'IE' ] < 9 || X_UA[ 'Opera' ] ? 'innerText' : X_UA[ 'IE9' ] ? 'text' : 'textContent',
\r
97 switch( queryString ){
\r
103 type = queryString;
\r
107 wrapper = queryString ? this.find( queryString ) : this;
\r
108 xml = wrapper.length === 1 ? wrapper._rawXML : wrapper[ 0 ];
\r
124 v = xml.nodeType === 1 ? xml.innerText || xml.text || xml.textContent : xml.nodeValue;
\r
128 return parseFloat( v );
\r
130 return parseFloat( v ) | 0;
\r
132 return !!X_String_parse( v );
\r
140 * セレクターにヒットした要素を返す。0~1個の要素がヒットした場合は X.XML を、それ以上の場合は X.XMLList を返す。
\r
141 * @alias X.XML.prototype.find
\r
142 * @param {string} queryString セレクター文字列
\r
143 * @return {X.XML|X.XMLList}
\r
145 function XMLWrapper_find( queryString ){
\r
147 var scope = this.constructor === XMLListWrapper ? this : [ this._rawXML ],
\r
148 parents = scope, // 探索元の親要素 xmlList の場合あり
\r
149 ARY_PUSH = Array.prototype.push,
\r
152 isMulti = 1 < scope.length,// 要素をマージする必要がある
\r
155 isAll, isNot, hasRoot,
\r
158 merge, // 要素がコメントノードで汚染されている場合使う
\r
159 combinator, selector, name, tagName,
\r
160 uid, tmp, xml, filter, key, op, val, toLower, useName,
\r
161 links, className, attr, flag;
\r
164 if( !X_Type_isString( queryString ) ) return XMLListWrapper_0;
\r
169 for( ; queryString.length; ){
\r
170 //console.log( 'queryString[' + queryString + ']' );
\r
174 parsed = X_Node_Selector__parse( queryString );
\r
176 if( X_Type_isNumber( parsed ) ){
\r
178 return XMLListWrapper_0;
\r
181 queryString = queryString.substr( parsed[ 0 ] );
\r
182 parsed = parsed[ 1 ];
\r
184 if( parsed === 5 ){
\r
187 xmlList && xmlList.length && ARY_PUSH.apply( ret, xmlList );
\r
195 combinator = parsed[ 0 ];
\r
196 selector = parsed[ 1 ];
\r
197 name = parsed[ 2 ];
\r
198 tagName = selector === 1 ? name : '*';
\r
199 isAll = tagName === '*';
\r
202 if( !xmlList.length ){
\r
206 if( combinator !== 0 ){
\r
209 //console.log( 'cobinator !== 0 ' + parents.length + ' : ' + xmlList.length );
\r
214 l = parents.length;
\r
216 isMulti = isMulti || 1 < l;
\r
218 //console.log( 'combinator ' + combinator );
\r
220 switch( combinator ){
\r
223 for( ; i < l; ++i ){
\r
224 for( xml = parents[ i ].firstChild; xml; xml = xml.nextSibling ){
\r
225 if( xml.nodeType === 1 && ( isAll || tagName === xml.tagName ) ) xmlList[ ++n ] = xml;
\r
231 for( ; i < l; ++i ){
\r
232 for( xml = parents[ i ].nextSibling; xml; xml = xml.nextSibling ){
\r
233 if( xml.nodeType === 1 ){
\r
234 if( isAll || tagName === xml.tagName ) xmlList[ ++n ] = xml;
\r
243 for( ; i < l; ++i ){
\r
244 for( xml = parents[ i ].nextSibling; xml; xml = xml.nextSibling ){
\r
245 if( xml.nodeType === 1 && ( isAll || tagName === xml.tagName ) ){
\r
246 if( merge.indexOf( xml ) !== -1 ){
\r
249 merge[ merge.length ] = xml;
\r
250 xmlList[ ++n ] = xml;
\r
261 for( ; i < l; ++i ){
\r
262 if( xml = parents[ i ].getAttributeNode( name ) ){
\r
263 xmlList[ ++n ] = xml;
\r
268 if( combinator === 1 || ( isStart && selector < 7 ) ){
\r
269 //console.log( l + ' > ' + xmlList.length + ' tag:' + tagName );
\r
270 for( ; i < l; ++i ){
\r
271 xml = parents[ i ];
\r
272 xml.childNodes && xml.childNodes.length && XMLWrapper_fetchElements( xmlList, xml, isAll ? null : tagName );
\r
274 //console.log( l + ' >> ' + xmlList.length + ' tag:' + tagName );
\r
280 //alert( 'pre-selector:' + ( xmlList && xmlList.length ) )
\r
282 switch( selector ){
\r
285 filter = [ 'id', 1, name ]; break;
\r
288 filter = [ 'class', 3 /*'~='*/, name ]; break;
\r
291 if( !( filter = XMLWrapper_filter[ name ] ) ){
\r
292 return XMLListWrapper_0;
\r
297 filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;
\r
301 parsed = parsed[ 2 ];
\r
302 name = parsed[ 2 ];
\r
303 switch( parsed[ 1 ] ) {
\r
305 filter = [ 'tag', 1, name ]; break;
\r
308 filter = [ 'id', 1, name ]; break;
\r
311 filter = [ 'class', 3, name ]; break;
\r
314 if( !( filter = XMLWrapper_filter[ name ] ) ){
\r
320 filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;
\r
325 xmlList = scope; break;
\r
329 xmlList = [ HTML ]; break;
\r
332 if( links = document.links ){
\r
333 for( xmlList = [], i = links.length; i; ){
\r
334 xmlList[ --i ] = new Node( links[ i ] );
\r
337 // area[href],a[href]
\r
341 if( filter && xmlList.length ){
\r
344 xmlList = filter.m(
\r
350 parsed[ 3 ], parsed[ 4 ]
\r
354 if( X_Type_isFunction( filter ) ){
\r
356 for( i = 0, n = -1; xml = xmlList[ i ]; ++i ){
\r
357 if( ( !!filter( xml ) ) ^ isNot ) tmp[ ++n ] = xml;
\r
368 if( op === 3 ) val = _ + val + _;
\r
370 for( i = 0, n = -1, l = xmlList.length; i < l; ++i ){
\r
371 xml = xmlList[ i ];
\r
372 attr = xml.getAttribute( key, 2 );
\r
373 flag = attr != null;// && ( !useName || attr !== '' );
\r
375 //if( toLower ) attr = attr.toLowerCase();
\r
379 flag = attr === val;
\r
382 flag = attr !== val;
\r
385 flag = ( _ + attr + _ ).indexOf( val ) !== -1;
\r
388 flag = attr.indexOf( val ) === 0;
\r
391 flag = attr.lastIndexOf( val ) + val.length === attr.length;
\r
394 flag = attr.indexOf( val ) !== -1;
\r
397 flag = attr === val || attr.substring( 0, val.length + 1 ) === val + '-';
\r
401 if( !!flag ^ isNot ) tmp[ ++n ] = xml;
\r
411 //console.log( '//end :' + ( xmlList && xmlList.length ) );
\r
413 //console.log( 'multi:' + ( xmlList && xmlList.length ) );
\r
415 // tree 順に並び替え、同一要素の排除
\r
417 xmlList && xmlList.length && ARY_PUSH.apply( ret, xmlList );
\r
419 if( l === 0 ) return XMLListWrapper_0;
\r
420 if( l === 1 ) return new XMLWrapper( ret[ 0 ] );
\r
424 for( i = 0, n = -1; i < l; ++i ){
\r
425 //alert( 'multi:' + i )
\r
427 if( xmlList.indexOf( xml ) === -1 ){
\r
428 //merge[ merge.length ] = xml;
\r
429 xmlList[ ++n ] = xml;
\r
432 XMLWrapper_sortElementOrder( ret = [], xmlList, this._rawXML.childNodes );
\r
435 for( i = 0, l = xmlList.length; i < l; ++i ){
\r
436 if( ret.indexOf( xml = xmlList[ i ] ) === -1 ){
\r
437 ret[ ret.length ] = xml;
\r
444 return xmlList.length === 1 ? new XMLWrapper( xmlList[ 0 ] ) : new XMLListWrapper( xmlList );
\r
447 function XMLWrapper_sortElementOrder( newList, list, xmlList ){
\r
448 var l = xmlList.length,
\r
450 j, child, _xmlList;
\r
451 for( ; i < l; ++i ){
\r
452 child = xmlList[ i ];
\r
453 //if( child.nodeType !== 1 ) continue;
\r
454 //console.log( child.tagName );
\r
455 if( ( j = list.indexOf( child ) ) !== -1 ){
\r
456 newList[ newList.length ] = child;
\r
457 list.splice( j, 1 );
\r
458 if( list.length === 1 ){
\r
459 newList[ newList.length ] = list[ 0 ];
\r
463 if( list.length === 0 ) return true;
\r
465 if( ( _xmlList = child.childNodes ) && XMLWrapper_sortElementOrder( newList, list, _xmlList ) ){
\r
471 function XMLWrapper_fetchElements( list, parent, tag ){
\r
472 var xmlList = parent.childNodes,
\r
473 l = xmlList.length,
\r
476 for( ; i < l; ++i ){
\r
477 child = xmlList[ i ];
\r
478 if( child.nodeType === 1 ){
\r
479 ( !tag || child.tagName === tag ) && ( list[ list.length ] = child );
\r
480 //console.log( parent.tagName + ' > ' + child.tagName + ' == ' + tag+ ' l:' + list.length );
\r
481 child.childNodes && child.childNodes.length && XMLWrapper_fetchElements( list, child, tag );
\r
486 function XMLWrapper_funcSelectorChild( type, flag_all, flags, xmlList ){
\r
488 flag_not = flags.not,
\r
489 i = 0, n = -1, xml, node,
\r
491 for( ; xml = xmlList[ i ]; ++i ){
\r
492 tagName = flag_all || xml.tagName;
\r
494 if( /* tmp === null && */ type <= 0 ){
\r
495 for( node = xml.previousSibling; node; node = node.previousSibling ){
\r
496 if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){
\r
502 if( tmp === null && 0 <= type ){
\r
503 for( node = xml.nextSibling; node; node = node.nextSibling ){
\r
504 if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){
\r
510 if( tmp === null ) tmp = true;
\r
511 if( tmp ^ flag_not ) res[ ++n ] = xml;
\r
515 function XMLWrapper_funcSelectorNth( pointer, sibling, flag_all, flags, xmlList, a, b ){
\r
516 var uids = X_Array_copy( xmlList ),
\r
519 flag_not = flags.not,
\r
521 c, xml, tmp, node, tagName, uid;
\r
523 for( ; xml = xmlList[ i ]; ++i ){
\r
524 tmp = checked[ i ];
\r
525 if( tmp === undefined ){
\r
526 for( c = 0, node = xml.parentNode[ pointer ], tagName = flag_all || xml.tagName; node; node = node[ sibling ] ){
\r
527 if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){
\r
529 uid = uids.indexOf( node );
\r
530 if( uid === -1 ) uids[ uid = uids.length ] = node;
\r
531 checked[ uid ] = a === 0 ? c === b : (c - b) % a === 0 && (c - b) / a >= 0;
\r
534 tmp = checked[ i ];
\r
536 if( tmp ^ flag_not ) res[ ++n ] = xml;
\r
541 function XMLWrapper_funcSelectorProp( prop, flag, flags, xmlList ){
\r
543 flag_not = flag ? flags.not : !flags.not,
\r
544 i = 0, n = -1, xml;
\r
545 for( ; xml = xmlList[ i ]; ++i ){
\r
546 if( xml.getAttributeNode( prop ) ^ flag_not ) res[ ++n ] = xml;
\r
551 var XMLWrapper_filter = {
\r
553 m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( -1, true, flags, xmlList ); }
\r
556 m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 1, true, flags, xmlList ); }
\r
559 m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 0, true, flags, xmlList ); }
\r
561 'first-of-type' : {
\r
562 m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( -1, false, flags, xmlList ); }
\r
565 m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 1, false, flags, xmlList ); }
\r
568 m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 0, false, flags, xmlList ); }
\r
571 m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'firstChild', 'nextSibling', true, flags, xmlList, a, b ); }
\r
573 'nth-last-child' : {
\r
574 m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'lastChild', 'previousSibling', true, flags, xmlList, a, b ); }
\r
577 m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'firstChild', 'nextSibling', false, flags, xmlList, a, b ); }
\r
579 'nth-last-of-type' : {
\r
580 m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'lastChild', 'previousSibling', false, flags, xmlList, a, b ); }
\r
583 m : function( flags, xmlList ){
\r
585 flag_not = flags.not,
\r
586 i = 0, n = -1, xml, tmp, node;
\r
587 for( ; xml = xmlList[i]; ++i ){
\r
589 for( node = xml.firstChild; node; node = node.nextSibling ){
\r
590 if( node.nodeType === 1 || ( node.nodeType === 3 && node.nodeValue ) ){
\r
595 if( tmp ^ flag_not ) res[ ++n ] = xml;
\r
601 m : function( flags, xmlList, arg ){
\r
603 flag_not = flags.not,
\r
604 i = 0, n = -1, xml, text = '';
\r
606 for( ; xml = xmlList[ i ]; ++i ){
\r
607 switch( xml.nodeType ){
\r
609 text = xml.innerText || xml.text || xml.textContent;
\r
613 text = xml.nodeValue;
\r
616 if ( ( -1 < text.indexOf( arg ) ) ^ flag_not ) res[ ++n ] = xml;
\r
624 * XML配列を扱う XML 探索用のラッパークラスです
\r
626 * @class XMLList XML配列を扱う XML 探索用のラッパークラスです
\r
630 function XMLListWrapper( xmlList ){
\r
631 var i = 0, l = xmlList ? xmlList.length : 0;
\r
632 for( ; i < l; ++i ){
\r
633 this[ i ] = xmlList[ i ];
\r
638 var XMLListWrapper_0 = new XMLListWrapper();
\r
640 XMLListWrapper.prototype.length = 0;
\r
641 XMLListWrapper.prototype._wraps = null;
\r
642 XMLListWrapper.prototype[ 'parent' ] = XMLWrapper_parent;
\r
643 XMLListWrapper.prototype[ 'has' ] = XMLWrapper_has;
\r
644 XMLListWrapper.prototype[ 'get' ] = XMLWrapper_get;
\r
645 XMLListWrapper.prototype[ 'val' ] = XMLWrapper_val;
\r
646 XMLListWrapper.prototype[ 'find' ] = XMLWrapper_find;
\r