ayril 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +4 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +19 -0
- data/LICENSE +19 -0
- data/README.md +88 -0
- data/Rakefile +40 -0
- data/ayril.gemspec +55 -0
- data/lib/ayril.rb +48 -0
- data/lib/ayril/core_ext/core_ext.rb +96 -0
- data/lib/ayril/selector.rb +250 -0
- data/lib/ayril/version.rb +30 -0
- data/lib/ayril/xml_document.rb +54 -0
- data/lib/ayril/xml_element.rb +75 -0
- data/lib/ayril/xml_element/element_attribute_manipulation.rb +61 -0
- data/lib/ayril/xml_element/element_classname_manipulation.rb +34 -0
- data/lib/ayril/xml_element/element_manipulation.rb +85 -0
- data/lib/ayril/xml_element/element_style_manipulation.rb +11 -0
- data/lib/ayril/xml_element/xml_attribute_hash.rb +60 -0
- data/lib/ayril/xml_element/xml_css_hash.rb +47 -0
- data/lib/ayril/xml_node.rb +118 -0
- data/lib/ayril/xml_node/node_manipulation.rb +73 -0
- data/lib/ayril/xml_node/node_traversal.rb +158 -0
- data/test/invoke_ayril_selector.rb +3 -0
- data/test/invoke_prototype_selector.js +4 -0
- data/test/sanity.xml +124 -0
- data/test/selector.js +521 -0
- data/test/test_sanity.rb +27 -0
- data/test/test_selector.rb +78 -0
- metadata +129 -0
data/test/selector.js
ADDED
@@ -0,0 +1,521 @@
|
|
1
|
+
|
2
|
+
Object.extend = function(destination, source) {
|
3
|
+
for (var property in source)
|
4
|
+
destination[property] = source[property];
|
5
|
+
return destination;
|
6
|
+
};
|
7
|
+
|
8
|
+
Object.keys = function(object) {
|
9
|
+
var results = [];
|
10
|
+
for (var property in object)
|
11
|
+
results.push(property);
|
12
|
+
return results;
|
13
|
+
};
|
14
|
+
|
15
|
+
|
16
|
+
function $A(iterable) {
|
17
|
+
if (!iterable) return [];
|
18
|
+
// Safari <2.0.4 crashes when accessing property of a node list with property accessor.
|
19
|
+
// It nevertheless works fine with `in` operator, which is why we use it here
|
20
|
+
if ('toArray' in iterable) return iterable.toArray();
|
21
|
+
var length = iterable.length || 0, results = new Array(length);
|
22
|
+
while (length--) results[length] = iterable[length];
|
23
|
+
return results;
|
24
|
+
}
|
25
|
+
|
26
|
+
|
27
|
+
var Class = (function() {
|
28
|
+
function create() {
|
29
|
+
var parent = null, properties = $A(arguments);
|
30
|
+
if (Object.isFunction(properties[0]))
|
31
|
+
parent = properties.shift();
|
32
|
+
|
33
|
+
function klass() {
|
34
|
+
this.initialize.apply(this, arguments);
|
35
|
+
}
|
36
|
+
|
37
|
+
Object.extend(klass, Class.Methods);
|
38
|
+
klass.superclass = parent;
|
39
|
+
klass.subclasses = [];
|
40
|
+
|
41
|
+
if (parent) {
|
42
|
+
var subclass = function() {};
|
43
|
+
subclass.prototype = parent.prototype;
|
44
|
+
klass.prototype = new subclass;
|
45
|
+
parent.subclasses.push(klass);
|
46
|
+
}
|
47
|
+
|
48
|
+
for (var i = 0; i < properties.length; i++)
|
49
|
+
klass.addMethods(properties[i]);
|
50
|
+
|
51
|
+
if (!klass.prototype.initialize)
|
52
|
+
klass.prototype.initialize = Prototype.emptyFunction;
|
53
|
+
|
54
|
+
klass.prototype.constructor = klass;
|
55
|
+
return klass;
|
56
|
+
}
|
57
|
+
|
58
|
+
function addMethods(source) {
|
59
|
+
var ancestor = this.superclass && this.superclass.prototype;
|
60
|
+
var properties = Object.keys(source);
|
61
|
+
|
62
|
+
if (!Object.keys({ toString: true }).length) {
|
63
|
+
if (source.toString != Object.prototype.toString)
|
64
|
+
properties.push("toString");
|
65
|
+
if (source.valueOf != Object.prototype.valueOf)
|
66
|
+
properties.push("valueOf");
|
67
|
+
}
|
68
|
+
|
69
|
+
for (var i = 0, length = properties.length; i < length; i++) {
|
70
|
+
var property = properties[i], value = source[property];
|
71
|
+
if (ancestor && Object.isFunction(value) &&
|
72
|
+
value.argumentNames().first() == "$super") {
|
73
|
+
var method = value;
|
74
|
+
value = (function(m) {
|
75
|
+
return function() { return ancestor[m].apply(this, arguments); };
|
76
|
+
})(property).wrap(method);
|
77
|
+
|
78
|
+
value.valueOf = method.valueOf.bind(method);
|
79
|
+
value.toString = method.toString.bind(method);
|
80
|
+
}
|
81
|
+
this.prototype[property] = value;
|
82
|
+
}
|
83
|
+
|
84
|
+
return this;
|
85
|
+
}
|
86
|
+
|
87
|
+
return {
|
88
|
+
create: create,
|
89
|
+
Methods: {
|
90
|
+
addMethods: addMethods
|
91
|
+
}
|
92
|
+
};
|
93
|
+
})();
|
94
|
+
|
95
|
+
|
96
|
+
Object.extend(Object, {
|
97
|
+
isFunction: function(object) {
|
98
|
+
return typeof object === "function";
|
99
|
+
},
|
100
|
+
|
101
|
+
isString: function(object) {
|
102
|
+
function getClass(object) {
|
103
|
+
return Object.prototype.toString.call(object)
|
104
|
+
.match(/^\[object\s(.*)\]$/)[1];
|
105
|
+
}
|
106
|
+
|
107
|
+
return getClass(object) === "String";
|
108
|
+
}
|
109
|
+
});
|
110
|
+
|
111
|
+
|
112
|
+
RegExp.escape = function(str) {
|
113
|
+
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
|
114
|
+
};
|
115
|
+
|
116
|
+
|
117
|
+
String.interpret = function(value) {
|
118
|
+
return value == null ? '' : String(value);
|
119
|
+
};
|
120
|
+
|
121
|
+
Object.extend(String.prototype, {
|
122
|
+
strip: function() {
|
123
|
+
return this.replace(/^\s+/, '').replace(/\s+$/, '');
|
124
|
+
},
|
125
|
+
|
126
|
+
startsWith: function(pattern) {
|
127
|
+
return this.indexOf(pattern) === 0;
|
128
|
+
},
|
129
|
+
|
130
|
+
gsub: function(pattern, replacement) {
|
131
|
+
function prepareReplacement(replacement) {
|
132
|
+
if (Object.isFunction(replacement)) return replacement;
|
133
|
+
var template = new Template(replacement);
|
134
|
+
return function(match) { return template.evaluate(match) };
|
135
|
+
}
|
136
|
+
|
137
|
+
var result = '', source = this, match;
|
138
|
+
replacement = prepareReplacement(replacement);
|
139
|
+
|
140
|
+
if (Object.isString(pattern))
|
141
|
+
pattern = RegExp.escape(pattern);
|
142
|
+
|
143
|
+
if (!(pattern.length || pattern.source)) {
|
144
|
+
replacement = replacement('');
|
145
|
+
return replacement + source.split('').join(replacement) + replacement;
|
146
|
+
}
|
147
|
+
|
148
|
+
while (source.length > 0) {
|
149
|
+
if (match = source.match(pattern)) {
|
150
|
+
result += source.slice(0, match.index);
|
151
|
+
result += String.interpret(replacement(match));
|
152
|
+
source = source.slice(match.index + match[0].length);
|
153
|
+
} else {
|
154
|
+
result += source, source = '';
|
155
|
+
}
|
156
|
+
}
|
157
|
+
return result;
|
158
|
+
}
|
159
|
+
});
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
var Template = Class.create({
|
164
|
+
initialize: function(template, pattern) {
|
165
|
+
this.template = template.toString();
|
166
|
+
this.pattern = pattern || Template.Pattern;
|
167
|
+
},
|
168
|
+
|
169
|
+
evaluate: function(object) {
|
170
|
+
if (Object.isFunction(object.toTemplateReplacements))
|
171
|
+
object = object.toTemplateReplacements();
|
172
|
+
|
173
|
+
return this.template.gsub(this.pattern, function(match) {
|
174
|
+
if (object == null) return '';
|
175
|
+
|
176
|
+
var before = match[1] || '';
|
177
|
+
if (before == '\\') return match[2];
|
178
|
+
|
179
|
+
var ctx = object, expr = match[3];
|
180
|
+
var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
|
181
|
+
match = pattern.exec(expr);
|
182
|
+
if (match == null) return before;
|
183
|
+
|
184
|
+
while (match != null) {
|
185
|
+
var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
|
186
|
+
ctx = ctx[comp];
|
187
|
+
if (null == ctx || '' == match[3]) break;
|
188
|
+
expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
|
189
|
+
match = pattern.exec(expr);
|
190
|
+
}
|
191
|
+
|
192
|
+
return before + String.interpret(ctx);
|
193
|
+
});
|
194
|
+
}
|
195
|
+
});
|
196
|
+
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
|
197
|
+
|
198
|
+
|
199
|
+
var Selector = Class.create({
|
200
|
+
initialize: function(expression) {
|
201
|
+
this.expression = expression.strip();
|
202
|
+
this.compileXPathMatcher();
|
203
|
+
},
|
204
|
+
|
205
|
+
|
206
|
+
compileXPathMatcher: function() {
|
207
|
+
var e = this.expression, ps = Selector.patterns,
|
208
|
+
x = Selector.xpath, le, m, len = ps.length, name;
|
209
|
+
|
210
|
+
if (Selector._cache[e]) {
|
211
|
+
this.xpath = Selector._cache[e]; return;
|
212
|
+
}
|
213
|
+
|
214
|
+
this.matcher = ['.//*'];
|
215
|
+
while (e && le != e && (/\S/).test(e)) {
|
216
|
+
le = e;
|
217
|
+
for (var i = 0; i<len; i++) {
|
218
|
+
name = ps[i].name;
|
219
|
+
if (m = e.match(ps[i].re)) {
|
220
|
+
this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
|
221
|
+
new Template(x[name]).evaluate(m));
|
222
|
+
e = e.replace(m[0], '');
|
223
|
+
break;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
this.xpath = this.matcher.join('')
|
229
|
+
this.xpath = this.xpath.gsub(/\*?\[name\(\)='([a-zA-Z]+)'\]/, '#{1}');
|
230
|
+
Selector._cache[this.expression] = this.xpath;
|
231
|
+
},
|
232
|
+
|
233
|
+
/**
|
234
|
+
* Selector#findElements(root) -> [Element...]
|
235
|
+
* - root (Element || document): A "scope" to search within. All results will
|
236
|
+
* be descendants of this node.
|
237
|
+
*
|
238
|
+
* Searches the document for elements that match the instance's CSS
|
239
|
+
* selector.
|
240
|
+
**/
|
241
|
+
findElements: function(root) {
|
242
|
+
root = root || document;
|
243
|
+
return document._getElementsByXPath(this.xpath, root);
|
244
|
+
},
|
245
|
+
|
246
|
+
/**
|
247
|
+
* Selector#match(element) -> Boolean
|
248
|
+
*
|
249
|
+
* Tests whether a `element` matches the instance's CSS selector.
|
250
|
+
**/
|
251
|
+
match: function(element) {
|
252
|
+
this.tokens = [];
|
253
|
+
|
254
|
+
var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
|
255
|
+
var le, p, m, len = ps.length, name;
|
256
|
+
|
257
|
+
while (e && le !== e && (/\S/).test(e)) {
|
258
|
+
le = e;
|
259
|
+
for (var i = 0; i<len; i++) {
|
260
|
+
p = ps[i].re;
|
261
|
+
name = ps[i].name;
|
262
|
+
if (m = e.match(p)) {
|
263
|
+
// use the Selector.assertions methods unless the selector
|
264
|
+
// is too complex.
|
265
|
+
if (as[name]) {
|
266
|
+
this.tokens.push([name, Object.clone(m)]);
|
267
|
+
e = e.replace(m[0], '');
|
268
|
+
} else {
|
269
|
+
// reluctantly do a document-wide search
|
270
|
+
// and look for a match in the array
|
271
|
+
return this.findElements(document).include(element);
|
272
|
+
}
|
273
|
+
}
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
var match = true, name, matches;
|
278
|
+
for (var i = 0, token; token = this.tokens[i]; i++) {
|
279
|
+
name = token[0], matches = token[1];
|
280
|
+
if (!Selector.assertions[name](element, matches)) {
|
281
|
+
match = false; break;
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
285
|
+
return match;
|
286
|
+
},
|
287
|
+
|
288
|
+
toString: function() {
|
289
|
+
return this.expression;
|
290
|
+
},
|
291
|
+
|
292
|
+
inspect: function() {
|
293
|
+
return "#<Selector:" + this.expression.inspect() + ">";
|
294
|
+
}
|
295
|
+
});
|
296
|
+
|
297
|
+
|
298
|
+
Object.extend(Selector, {
|
299
|
+
_cache: { },
|
300
|
+
|
301
|
+
xpath: {
|
302
|
+
descendant: "//*",
|
303
|
+
child: "/*",
|
304
|
+
adjacent: "/following-sibling::*[1]",
|
305
|
+
laterSibling: '/following-sibling::*',
|
306
|
+
tagName: function(m) {
|
307
|
+
if (m[1] == '*') return '';
|
308
|
+
//return "[local-name()='" + m[1].toLowerCase() +
|
309
|
+
// "' or local-name()='" + m[1].toUpperCase() + "']";
|
310
|
+
return "[name()='" + m[1] + "']";
|
311
|
+
},
|
312
|
+
className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
|
313
|
+
id: "[@id='#{1}']",
|
314
|
+
attrPresence: function(m) {
|
315
|
+
m[1] = m[1].toLowerCase();
|
316
|
+
return new Template("[@#{1}]").evaluate(m);
|
317
|
+
},
|
318
|
+
attr: function(m) {
|
319
|
+
m[1] = m[1].toLowerCase();
|
320
|
+
m[3] = m[5] || m[6];
|
321
|
+
return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
|
322
|
+
},
|
323
|
+
pseudo: function(m) {
|
324
|
+
var h = Selector.xpath.pseudos[m[1]];
|
325
|
+
if (!h) return '';
|
326
|
+
if (Object.isFunction(h)) return h(m);
|
327
|
+
return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
|
328
|
+
},
|
329
|
+
operators: {
|
330
|
+
'=': "[@#{1}='#{3}']",
|
331
|
+
'!=': "[@#{1}!='#{3}']",
|
332
|
+
'^=': "[starts-with(@#{1}, '#{3}')]",
|
333
|
+
'$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
|
334
|
+
'*=': "[contains(@#{1}, '#{3}')]",
|
335
|
+
'~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
|
336
|
+
'|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
|
337
|
+
},
|
338
|
+
pseudos: {
|
339
|
+
'first-child': '[not(preceding-sibling::*)]',
|
340
|
+
'last-child': '[not(following-sibling::*)]',
|
341
|
+
'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
|
342
|
+
'empty': "[count(*) = 0 and (count(text()) = 0)]",
|
343
|
+
'checked': "[@checked]",
|
344
|
+
'disabled': "[(@disabled) and (@type!='hidden')]",
|
345
|
+
'enabled': "[not(@disabled) and (@type!='hidden')]",
|
346
|
+
'not': function(m) {
|
347
|
+
var e = m[6], p = Selector.patterns,
|
348
|
+
x = Selector.xpath, le, v, len = p.length, name;
|
349
|
+
|
350
|
+
var exclusion = [];
|
351
|
+
while (e && le != e && (/\S/).test(e)) {
|
352
|
+
le = e;
|
353
|
+
for (var i = 0; i<len; i++) {
|
354
|
+
name = p[i].name
|
355
|
+
if (m = e.match(p[i].re)) {
|
356
|
+
v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m);
|
357
|
+
exclusion.push("(" + v.substring(1, v.length - 1) + ")");
|
358
|
+
e = e.replace(m[0], '');
|
359
|
+
break;
|
360
|
+
}
|
361
|
+
}
|
362
|
+
}
|
363
|
+
return "[not(" + exclusion.join(" and ") + ")]";
|
364
|
+
},
|
365
|
+
'nth-child': function(m) {
|
366
|
+
return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
|
367
|
+
},
|
368
|
+
'nth-last-child': function(m) {
|
369
|
+
return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
|
370
|
+
},
|
371
|
+
'nth-of-type': function(m) {
|
372
|
+
return Selector.xpath.pseudos.nth("position() ", m);
|
373
|
+
},
|
374
|
+
'nth-last-of-type': function(m) {
|
375
|
+
return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
|
376
|
+
},
|
377
|
+
'first-of-type': function(m) {
|
378
|
+
m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
|
379
|
+
},
|
380
|
+
'last-of-type': function(m) {
|
381
|
+
m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
|
382
|
+
},
|
383
|
+
'only-of-type': function(m) {
|
384
|
+
var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
|
385
|
+
},
|
386
|
+
nth: function(fragment, m) {
|
387
|
+
var mm, formula = m[6], predicate;
|
388
|
+
if (formula == 'even') formula = '2n+0';
|
389
|
+
if (formula == 'odd') formula = '2n+1';
|
390
|
+
if (mm = formula.match(/^(\d+)$/)) // digit only
|
391
|
+
return '[' + fragment + "= " + mm[1] + ']';
|
392
|
+
if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
|
393
|
+
if (mm[1] == "-") mm[1] = -1;
|
394
|
+
var a = mm[1] ? Number(mm[1]) : 1;
|
395
|
+
var b = mm[2] ? Number(mm[2]) : 0;
|
396
|
+
predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
|
397
|
+
"((#{fragment} - #{b}) div #{a} >= 0)]";
|
398
|
+
return new Template(predicate).evaluate({
|
399
|
+
fragment: fragment, a: a, b: b });
|
400
|
+
}
|
401
|
+
}
|
402
|
+
}
|
403
|
+
},
|
404
|
+
|
405
|
+
patterns: [
|
406
|
+
// combinators must be listed first
|
407
|
+
// (and descendant needs to be last combinator)
|
408
|
+
{ name: 'laterSibling', re: /^\s*~\s*/ },
|
409
|
+
{ name: 'child', re: /^\s*>\s*/ },
|
410
|
+
{ name: 'adjacent', re: /^\s*\+\s*/ },
|
411
|
+
{ name: 'descendant', re: /^\s/ },
|
412
|
+
|
413
|
+
// selectors follow
|
414
|
+
{ name: 'tagName', re: /^\s*(\*|[\w\-]+)(\b|$)?/ },
|
415
|
+
{ name: 'id', re: /^#([\w\-\*]+)(\b|$)/ },
|
416
|
+
{ name: 'className', re: /^\.([\w\-\*]+)(\b|$)/ },
|
417
|
+
{ name: 'pseudo', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ },
|
418
|
+
{ name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
|
419
|
+
{ name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
|
420
|
+
],
|
421
|
+
|
422
|
+
// for Selector.match and Element#match
|
423
|
+
assertions: {
|
424
|
+
tagName: function(element, matches) {
|
425
|
+
return matches[1].toUpperCase() == element.tagName.toUpperCase();
|
426
|
+
},
|
427
|
+
|
428
|
+
className: function(element, matches) {
|
429
|
+
return Element.hasClassName(element, matches[1]);
|
430
|
+
},
|
431
|
+
|
432
|
+
id: function(element, matches) {
|
433
|
+
return element.id === matches[1];
|
434
|
+
},
|
435
|
+
|
436
|
+
attrPresence: function(element, matches) {
|
437
|
+
return Element.hasAttribute(element, matches[1]);
|
438
|
+
},
|
439
|
+
|
440
|
+
attr: function(element, matches) {
|
441
|
+
var nodeValue = Element.readAttribute(element, matches[1]);
|
442
|
+
return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
|
443
|
+
}
|
444
|
+
},
|
445
|
+
operators: {
|
446
|
+
'=': function(nv, v) { return nv == v; },
|
447
|
+
'!=': function(nv, v) { return nv != v; },
|
448
|
+
'^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
|
449
|
+
'$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
|
450
|
+
'*=': function(nv, v) { return nv == v || nv && nv.include(v); },
|
451
|
+
'~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
|
452
|
+
'|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
|
453
|
+
'-').include('-' + (v || "").toUpperCase() + '-'); }
|
454
|
+
},
|
455
|
+
|
456
|
+
/**
|
457
|
+
* Selector.split(expression) -> [String...]
|
458
|
+
*
|
459
|
+
* Takes a string of CSS selectors separated by commas; returns an array
|
460
|
+
* of individual selectors.
|
461
|
+
*
|
462
|
+
* Safer than doing a naive `Array#split`, since selectors can have commas
|
463
|
+
* in other places.
|
464
|
+
**/
|
465
|
+
split: function(expression) {
|
466
|
+
var expressions = [];
|
467
|
+
expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
|
468
|
+
expressions.push(m[1].strip());
|
469
|
+
});
|
470
|
+
return expressions;
|
471
|
+
},
|
472
|
+
|
473
|
+
/**
|
474
|
+
* Selector.matchElements(elements, expression) -> [Element...]
|
475
|
+
*
|
476
|
+
* Filters the given collection of elements with `expression`.
|
477
|
+
*
|
478
|
+
* The only nodes returned will be those that match the given CSS selector.
|
479
|
+
**/
|
480
|
+
matchElements: function(elements, expression) {
|
481
|
+
var matches = $$(expression), h = Selector.handlers;
|
482
|
+
h.mark(matches);
|
483
|
+
for (var i = 0, results = [], element; element = elements[i]; i++)
|
484
|
+
if (element._countedByPrototype) results.push(element);
|
485
|
+
h.unmark(matches);
|
486
|
+
return results;
|
487
|
+
},
|
488
|
+
|
489
|
+
/**
|
490
|
+
* Selector.findElement(elements, expression[, index = 0]) -> Element
|
491
|
+
* Selector.findElement(elements[, index = 0]) -> Element
|
492
|
+
*
|
493
|
+
* Returns the `index`th element in the collection that matches
|
494
|
+
* `expression`.
|
495
|
+
*
|
496
|
+
* Returns the `index`th element overall if `expression` is not given.
|
497
|
+
**/
|
498
|
+
findElement: function(elements, expression, index) {
|
499
|
+
if (Object.isNumber(expression)) {
|
500
|
+
index = expression; expression = false;
|
501
|
+
}
|
502
|
+
return Selector.matchElements(elements, expression || '*')[index || 0];
|
503
|
+
},
|
504
|
+
|
505
|
+
/**
|
506
|
+
* Selector.findChildElements(element, expressions) -> [Element...]
|
507
|
+
*
|
508
|
+
* Searches beneath `element` for any elements that match the selector
|
509
|
+
* (or selectors) specified in `expressions`.
|
510
|
+
**/
|
511
|
+
findChildElements: function(element, expressions) {
|
512
|
+
expressions = Selector.split(expressions.join(','));
|
513
|
+
var results = [], h = Selector.handlers;
|
514
|
+
for (var i = 0, l = expressions.length, selector; i < l; i++) {
|
515
|
+
selector = new Selector(expressions[i].strip());
|
516
|
+
h.concat(results, selector.findElements(element));
|
517
|
+
}
|
518
|
+
return (l > 1) ? h.unique(results) : results;
|
519
|
+
}
|
520
|
+
});
|
521
|
+
|