ユニットテスト
PrototypeライブラリをSubversionからcheckoutすると,
今回ライブラリ内に疑問点などがあったり,
assertEnumEqual([], $$('#p + #p'));
というような行を追加するだけで、ブラウザから簡単にテストが行えます。
では,
2277: handlers: {
2278: // UTILITY FUNCTIONS
2279: // joins two collections
2280: concat: function(a, b) {
2281: for (var i = 0, node; node = b[i]; i++)
2282: a.push(node);
2283: return a;
2284: },
2285:
2286: // marks an array of nodes for counting
2287: mark: function(nodes) {
2288: for (var i = 0, node; node = nodes[i]; i++)
2289: node._counted = true;
2290: return nodes;
2291: },
2292:
2293: unmark: function(nodes) {
2294: for (var i = 0, node; node = nodes[i]; i++)
2295: node._counted = undefined;
2296: return nodes;
2297: },
2298:
2277行目からは,
concat()は,
mark(), unmark() は,
- 基準となるノード配列全体をunmark()する
- ルールに該当する要素を抽出する
- 抽出された配列をmark()する
- 必要に応じて2, 3を繰り返す
- 基準となるノード配列から_countedにマークされたものだけを抽出する
とするためなどに使われます。これで順番を保ったまま,
2299: // mark each child node with its position (for nth calls)
2300: // "ofType" flag indicates whether we're indexing for nth-of-type
2301: // rather than nth-child
2302: index: function(parentNode, reverse, ofType) {
2303: parentNode._counted = true;
2304: if (reverse) {
2305: for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
2306: node = nodes[i];
2307: if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2308: }
2309: } else {
2310: for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
2311: if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2312: }
2313: },
2314:
2302行目からはindex()関数です。pesudos.
ofTypeが偽の場合は,
ofTypeが真の場合はnth-of-type()系の擬似クラスであり,
2315: // filters out duplicates and extends all nodes
2316: unique: function(nodes) {
2317: if (nodes.length == 0) return nodes;
2318: var results = [], n;
2319: for (var i = 0, l = nodes.length; i < l; i++)
2320: if (!(n = nodes[i])._counted) {
2321: n._counted = true;
2322: results.push(Element.extend(n));
2323: }
2324: return Selector.handlers.unmark(results);
2325: },
2326:
2316行目からはunique()関数です。
フィルタ処理で蓄積されたノードの配列に対して,
これはSelector.
_countedを使う関数は使用後にクリアすることになっているので,
2327: // COMBINATOR FUNCTIONS
2328: descendant: function(nodes) {
2329: var h = Selector.handlers;
2330: for (var i = 0, results = [], node; node = nodes[i]; i++)
2331: h.concat(results, node.getElementsByTagName('*'));
2332: return results;
2333: },
2334:
2327行目からは"COMBINATOR FUNCTIONS"すなわちCSSセレクタの結合子に対するハンドラ関数です。
descendant()は,
CSSセレクタのパーサがdescendant combinatorにマッチするタイミングは,
そのための実装は,
2335: child: function(nodes) {
2336: var h = Selector.handlers;
2337: for (var i = 0, results = [], node; node = nodes[i]; i++) {
2338: for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
2339: if (child.nodeType == 1 && child.tagName != '!') results.push(child);
2340: }
2341: return results;
2342: },
2343:
2335行目からはchild()ハンドラです。
セレクタの表記では"要素A > 要素B"となり,
渡された各ノードに対してchildNodesプロパティの配列をループで取得し,
IEではコメントノードのnodeTypeが1 (ELEMENT_
2338行目のchildrenは定義はされていますが使われていないようです。
2344: adjacent: function(nodes) {
2345: for (var i = 0, results = [], node; node = nodes[i]; i++) {
2346: var next = this.nextElementSibling(node);
2347: if (next) results.push(next);
2348: }
2349: return results;
2350: },
2351:
2344行目からはadjacent()です。
CSSセレクタでは"要素A + 要素B"とする部分です。隣接するふたつのノードが対象ですので,
これでresultsで返すのは"要素B"にあたるものの集合,
2352: laterSibling: function(nodes) {
2353: var h = Selector.handlers;
2354: for (var i = 0, results = [], node; node = nodes[i]; i++)
2355: h.concat(results, Element.nextSiblings(node));
2356: return results;
2357: },
2358:
2352行目からはlaterSibling()です。
原典がどこかわかりませんが,
実装は,
2359: nextElementSibling: function(node) {
2360: while (node = node.nextSibling)
2361: if (node.nodeType == 1) return node;
2362: return null;
2363: },
2364:
2359行目からはnextElementSibling()です。
これは主に他のハンドラ関数から呼ばれるヘルパ関数で,
2365: previousElementSibling: function(node) {
2366: while (node = node.previousSibling)
2367: if (node.nodeType == 1) return node;
2368: return null;
2369: },
2370:
previousElementSibling()は nextElementSibling()の逆で,