1 /**
  2  * @fileOverview definition for node and nodelist
  3  * @author yiminghe@gmail.com, lifesinger@gmail.com
  4  */
  5 KISSY.add("node/base", function (S, DOM, undefined) {
  6 
  7     var AP = Array.prototype,
  8         slice = AP.slice,
  9         push = AP.push,
 10         makeArray = S.makeArray,
 11         isNodeList = DOM._isNodeList;
 12 
 13     /**
 14      * @class The NodeList class provides a wrapper for manipulating DOM Node.
 15      * use KISSY.all/one to retrieve NodeList instances
 16      * @name NodeList
 17      */
 18     function NodeList(html, props, ownerDocument) {
 19         var self = this,
 20             domNode;
 21 
 22         if (!(self instanceof NodeList)) {
 23             return new NodeList(html, props, ownerDocument);
 24         }
 25 
 26         // handle NodeList(''), NodeList(null), or NodeList(undefined)
 27         if (!html) {
 28             return undefined;
 29         }
 30 
 31         else if (S.isString(html)) {
 32             // create from html
 33             domNode = DOM.create(html, props, ownerDocument);
 34             // ('<p>1</p><p>2</p>') 转换为 NodeList
 35             if (domNode.nodeType === DOM.DOCUMENT_FRAGMENT_NODE) { // fragment
 36                 push.apply(this, makeArray(domNode.childNodes));
 37                 return undefined;
 38             }
 39         }
 40 
 41         else if (S.isArray(html) || isNodeList(html)) {
 42             push.apply(self, makeArray(html));
 43             return undefined;
 44         }
 45 
 46         else {
 47             // node, document, window
 48             domNode = html;
 49         }
 50 
 51         self[0] = domNode;
 52         self.length = 1;
 53         return undefined;
 54     }
 55 
 56     S.augment(NodeList,
 57         /**
 58          * @lends NodeList#
 59          */
 60         {
 61 
 62             /**
 63              * length of nodelist
 64              * @type Number
 65              */
 66             length:0,
 67 
 68 
 69             /**
 70              * Get one node at index
 71              * @param {Number} index Index position.
 72              * @return {NodeList}
 73              */
 74             item:function (index) {
 75                 var self = this;
 76                 if (S.isNumber(index)) {
 77                     if (index >= self.length) {
 78                         return null;
 79                     } else {
 80                         return new NodeList(self[index]);
 81                     }
 82                 } else {
 83                     return new NodeList(index);
 84                 }
 85             },
 86 
 87             /**
 88              * Add existing node list.
 89              * @param {String|HTMLElement[]|NodeList} selector Selector string or html string or common dom node.
 90              * @param {String|Array<HTMLElement>|NodeList|HTMLElement|Document} [context] Search context for selector
 91              * @param {Number} [index] Insert position.
 92              * @return {NodeList}
 93              */
 94             add:function (selector, context, index) {
 95                 if (S.isNumber(context)) {
 96                     index = context;
 97                     context = undefined;
 98                 }
 99                 var list = NodeList.all(selector, context).getDOMNodes(),
100                     ret = new NodeList(this);
101                 if (index === undefined) {
102                     push.apply(ret, list);
103                 } else {
104                     var args = [index, 0];
105                     args.push.apply(args, list);
106                     AP.splice.apply(ret, args);
107                 }
108                 return ret;
109             },
110 
111             /**
112              * Get part of node list.
113              * @param {Number} start Start position.
114              * @param {number} end End position.
115              * @return {NodeList}
116              */
117             slice:function (start, end) {
118                 // ie<9 : [1,2].slice(-2,undefined) => []
119                 // ie<9 : [1,2].slice(-2) => []
120                 // fix #85
121                 return new NodeList(slice.apply(this, arguments));
122             },
123 
124             /**
125              * Retrieves the DOMNodes.
126              */
127             getDOMNodes:function () {
128                 return slice.call(this);
129             },
130 
131             /**
132              * Applies the given function to each Node in the NodeList.
133              * @param fn The function to apply. It receives 3 arguments: the current node instance, the node's index, and the NodeList instance
134              * @param [context] An optional context to apply the function with Default context is the current NodeList instance
135              */
136             each:function (fn, context) {
137                 var self = this;
138 
139                 S.each(self, function (n, i) {
140                     n = new NodeList(n);
141                     return fn.call(context || n, n, i, self);
142                 });
143 
144                 return self;
145             },
146             /**
147              * Retrieves the DOMNode.
148              */
149             getDOMNode:function () {
150                 return this[0];
151             },
152 
153             /**
154              * return last stack node list.
155              * @return {NodeList}
156              */
157             end:function () {
158                 var self = this;
159                 return self.__parent || self;
160             },
161 
162             /**
163              * Get node list which are descendants of current node list.
164              * @param {String} selector Selector string
165              * @return {NodeList}
166              */
167             all:function (selector) {
168                 var ret, self = this;
169                 if (self.length > 0) {
170                     ret = NodeList.all(selector, self);
171                 } else {
172                     ret = new NodeList();
173                 }
174                 ret.__parent = self;
175                 return ret;
176             },
177 
178             one:function (selector) {
179                 var self = this, all = self.all(selector),
180                     ret = all.length ? all.slice(0, 1) : null;
181                 if (ret) {
182                     ret.__parent = self;
183                 }
184                 return ret;
185             }
186         });
187 
188     S.mix(NodeList,
189         /**
190          * @lends NodeList
191          */
192         {
193             /**
194              * Get node list from selector or construct new node list from html string.
195              * Can also called from KISSY.all
196              * @param {String|HTMLElement[]|NodeList} selector Selector string or html string or common dom node.
197              * @param {String|Array<HTMLElement>|NodeList|HTMLElement|Document} [context] Search context for selector
198              * @returns {NodeList}
199              */
200             all:function (selector, context) {
201                 // are we dealing with html string ?
202                 // TextNode 仍需要自己 new Node
203 
204                 if (S.isString(selector)
205                     && (selector = S.trim(selector))
206                     && selector.length >= 3
207                     && S.startsWith(selector, "<")
208                     && S.endsWith(selector, ">")
209                     ) {
210                     if (context) {
211                         if (context.getDOMNode) {
212                             context = context.getDOMNode();
213                         }
214                         if (context.ownerDocument) {
215                             context = context.ownerDocument;
216                         }
217                     }
218                     return new NodeList(selector, undefined, context);
219                 }
220                 return new NodeList(DOM.query(selector, context));
221             },
222             one:function (selector, context) {
223                 var all = NodeList.all(selector, context);
224                 return all.length ? all.slice(0, 1) : null;
225             }
226         });
227 
228     S.mix(NodeList, DOM.NodeTypes);
229 
230     return NodeList;
231 }, {
232     requires:["dom"]
233 });
234 
235 
236 /**
237  * Notes:
238  * 2011-05-25
239  *  - 承玉:参考 jquery,只有一个 NodeList 对象,Node 就是 NodeList 的别名
240  *
241  *  2010.04
242  *   - each 方法传给 fn 的 this, 在 jQuery 里指向原生对象,这样可以避免性能问题。
243  *     但从用户角度讲,this 的第一直觉是 $(this), kissy 和 yui3 保持一致,牺牲
244  *     性能,以易用为首。
245  *   - 有了 each 方法,似乎不再需要 import 所有 dom 方法,意义不大。
246  *   - dom 是低级 api, node 是中级 api, 这是分层的一个原因。还有一个原因是,如果
247  *     直接在 node 里实现 dom 方法,则不大好将 dom 的方法耦合到 nodelist 里。可
248  *     以说,技术成本会制约 api 设计。
249  */
250