ayril 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|