Standalone CSS Selector Parser and Engine. An official MooTools project.
Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta
// @require https://update.greasyfork.org/scripts/371600/623833/Slick.js
/*
---
name: Slick.Parser
description: Standalone CSS3 Selector parser
provides: Slick.Parser
...
*/
;(function(){
var parsed,
separatorIndex,
combinatorIndex,
reversed,
cache = {},
reverseCache = {},
reUnescape = /\\/g;
var parse = function(expression, isReversed){
if (expression == null) return null;
if (expression.Slick === true) return expression;
expression = ('' + expression).replace(/^\s+|\s+$/g, '');
reversed = !!isReversed;
var currentCache = (reversed) ? reverseCache : cache;
if (currentCache[expression]) return currentCache[expression];
parsed = {
Slick: true,
expressions: [],
raw: expression,
reverse: function(){
return parse(this.raw, true);
}
};
separatorIndex = -1;
while (expression != (expression = expression.replace(regexp, parser)));
parsed.length = parsed.expressions.length;
return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
};
var reverseCombinator = function(combinator){
if (combinator === '!') return ' ';
else if (combinator === ' ') return '!';
else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
else return '!' + combinator;
};
var reverse = function(expression){
var expressions = expression.expressions;
for (var i = 0; i < expressions.length; i++){
var exp = expressions[i];
var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
for (var j = 0; j < exp.length; j++){
var cexp = exp[j];
if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
cexp.combinator = cexp.reverseCombinator;
delete cexp.reverseCombinator;
}
exp.reverse().push(last);
}
return expression;
};
var escapeRegExp = (function(){
// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
var from = /(?=[\-\[\]{}()*+?.\\\^$|,#\s])/g, to = '\\';
return function(string){ return string.replace(from, to) }
}())
var regexp = new RegExp(
/*
#!/usr/bin/env ruby
puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
__END__
"(?x)^(?:\
\\s* ( , ) \\s* # Separator \n\
| \\s* ( <combinator>+ ) \\s* # Combinator \n\
| ( \\s+ ) # CombinatorChildren \n\
| ( <unicode>+ | \\* ) # Tag \n\
| \\# ( <unicode>+ ) # ID \n\
| \\. ( <unicode>+ ) # ClassName \n\
| # Attribute \n\
\\[ \
\\s* (<unicode1>+) (?: \
\\s* ([*^$!~|]?=) (?: \
\\s* (?:\
([\"']?)(.*?)\\9 \
)\
) \
)? \\s* \
\\](?!\\]) \n\
| :+ ( <unicode>+ )(?:\
\\( (?:\
(?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
) \\)\
)?\
)"
*/
"^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
.replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
.replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
.replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
);
function parser(
rawMatch,
separator,
combinator,
combinatorChildren,
tagName,
id,
className,
attributeKey,
attributeOperator,
attributeQuote,
attributeValue,
pseudoMarker,
pseudoClass,
pseudoQuote,
pseudoClassQuotedValue,
pseudoClassValue
){
if (separator || separatorIndex === -1){
parsed.expressions[++separatorIndex] = [];
combinatorIndex = -1;
if (separator) return '';
}
if (combinator || combinatorChildren || combinatorIndex === -1){
combinator = combinator || ' ';
var currentSeparator = parsed.expressions[separatorIndex];
if (reversed && currentSeparator[combinatorIndex])
currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
}
var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
if (tagName){
currentParsed.tag = tagName.replace(reUnescape, '');
} else if (id){
currentParsed.id = id.replace(reUnescape, '');
} else if (className){
className = className.replace(reUnescape, '');
if (!currentParsed.classList) currentParsed.classList = [];
if (!currentParsed.classes) currentParsed.classes = [];
currentParsed.classList.push(className);
currentParsed.classes.push({
value: className,
regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
});
} else if (pseudoClass){
pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
if (!currentParsed.pseudos) currentParsed.pseudos = [];
currentParsed.pseudos.push({
key: pseudoClass.replace(reUnescape, ''),
value: pseudoClassValue,
type: pseudoMarker.length == 1 ? 'class' : 'element'
});
} else if (attributeKey){
attributeKey = attributeKey.replace(reUnescape, '');
attributeValue = (attributeValue || '').replace(reUnescape, '');
var test, regexp;
switch (attributeOperator){
case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
case '=' : test = function(value){
return attributeValue == value;
}; break;
case '*=' : test = function(value){
return value && value.indexOf(attributeValue) > -1;
}; break;
case '!=' : test = function(value){
return attributeValue != value;
}; break;
default : test = function(value){
return !!value;
};
}
if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
return false;
};
if (!test) test = function(value){
return value && regexp.test(value);
};
if (!currentParsed.attributes) currentParsed.attributes = [];
currentParsed.attributes.push({
key: attributeKey,
operator: attributeOperator,
value: attributeValue,
test: test
});
}
return '';
};
// Slick NS
var Slick = (this.Slick || {});
Slick.parse = function(expression){
return parse(expression);
};
Slick.escapeRegExp = escapeRegExp;
if (!this.Slick) this.Slick = Slick;
}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
/*
---
name: Slick.Finder
description: The new, superfast css selector engine.
provides: Slick.Finder
requires: Slick.Parser
...
*/
;(function(){
var local = {},
featuresCache = {},
toString = Object.prototype.toString;
// Feature / Bug detection
local.isNativeCode = function(fn){
return (/\{\s*\[native code\]\s*\}/).test('' + fn);
};
local.isXML = function(document){
return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
(document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
};
local.setDocument = function(document){
// convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
var nodeType = document.nodeType;
if (nodeType == 9); // document
else if (nodeType) document = document.ownerDocument; // node
else if (document.navigator) document = document.document; // window
else return;
// check if it's the old document
if (this.document === document) return;
this.document = document;
// check if we have done feature detection on this document before
var root = document.documentElement,
rootUid = this.getUIDXML(root),
features = featuresCache[rootUid],
feature;
if (features){
for (feature in features){
this[feature] = features[feature];
}
return;
}
features = featuresCache[rootUid] = {};
features.root = root;
features.isXMLDocument = this.isXML(document);
features.brokenStarGEBTN
= features.starSelectsClosedQSA
= features.idGetsName
= features.brokenMixedCaseQSA
= features.brokenGEBCN
= features.brokenCheckedQSA
= features.brokenEmptyAttributeQSA
= features.isHTMLDocument
= features.nativeMatchesSelector
= false;
var starSelectsClosed, starSelectsComments,
brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
brokenFormAttributeGetter;
var selected, id = 'slick_uniqueid';
var testNode = document.createElement('div');
var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
testRoot.appendChild(testNode);
// on non-HTML documents innerHTML and getElementsById doesnt work properly
try {
testNode.innerHTML = '<a id="'+id+'"></a>';
features.isHTMLDocument = !!document.getElementById(id);
} catch(e){}
if (features.isHTMLDocument){
testNode.style.display = 'none';
// IE returns comment nodes for getElementsByTagName('*') for some documents
testNode.appendChild(document.createComment(''));
starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
// IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
// Should never inject incorrect markup on XML documents
if (!features.isXMLDocument){
try {
testNode.innerHTML = 'foo</foo>';
selected = testNode.getElementsByTagName('*');
starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
} catch(e){}
}
features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
// native matchesSelector function
features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
if (features.nativeMatchesSelector) try {
// if matchesSelector trows errors on incorrect sintaxes we can use it
features.nativeMatchesSelector.call(root, ':slick');
features.nativeMatchesSelector = null;
} catch(e){}
// IE returns elements with the name instead of just id for getElementsById for some documents
try {
testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
features.idGetsName = document.getElementById(id) === testNode.firstChild;
} catch(e){}
if (testNode.getElementsByClassName){
// Safari 3.2 getElementsByClassName caches results
try {
testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
testNode.getElementsByClassName('b').length;
testNode.firstChild.className = 'b';
cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
} catch(e){}
// Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
try {
testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
} catch(e){}
features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
}
if (testNode.querySelectorAll){
// IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
// Should never inject incorrect markup on XML documents
if (!features.isXMLDocument){
try {
testNode.innerHTML = 'foo</foo>';
selected = testNode.querySelectorAll('*');
features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
} catch(e){}
}
// Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
try {
testNode.innerHTML = '<a class="MiX"></a>';
features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
} catch(e){}
// Webkit and Opera dont return selected options on querySelectorAll
try {
testNode.innerHTML = '<select><option selected="selected">a</option></select>';
features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
} catch(e){}
// IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
try {
testNode.innerHTML = '<a class=""></a>';
features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
} catch(e){}
}
if (features.nativeMatchesSelector) {
// Webkit does not match correctly with the :nth-child pseudo on elements inside the body
// but if a style is applyed using this selector, the match starts working
try {
testNode.innerHTML = '_<style>:nth-child(2){}</style>';
} catch(e){}
}
// IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
try {
testNode.innerHTML = '<form action="s"><input id="action"/></form>';
brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
} catch(e){}
}
try {
root.slick_expando = 1;
delete root.slick_expando;
features.getUID = this.getUIDHTML;
} catch(e) {
features.getUID = this.getUIDXML;
}
testRoot.removeChild(testNode);
testNode = selected = testRoot = null;
// getAttribute
features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
var method = this.attributeGetters[name];
if (method) return method.call(node);
var attributeNode = node.getAttributeNode(name);
return (attributeNode) ? attributeNode.nodeValue : null;
} : function(node, name){
var method = this.attributeGetters[name];
return (method) ? method.call(node) : node.getAttribute(name);
};
// hasAttribute
features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
return node.hasAttribute(attribute);
} : function(node, attribute) {
node = node.getAttributeNode(attribute);
return !!(node && (node.specified || node.nodeValue));
};
// contains
// FIXME: Add specs: local.contains should be different for xml and html documents?
var nativeRootContains = root && this.isNativeCode(root.contains),
nativeDocumentContains = document && this.isNativeCode(document.contains);
features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
return context.contains(node);
} : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
// IE8 does not have .contains on document.
return context === node || ((context === document) ? document.documentElement : context).contains(node);
} : (root && root.compareDocumentPosition) ? function(context, node){
return context === node || !!(context.compareDocumentPosition(node) & 16);
} : function(context, node){
if (node) do {
if (node === context) return true;
} while ((node = node.parentNode));
return false;
};
// document order sorting
// credits to Sizzle (http://sizzlejs.com/)
features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
} : ('sourceIndex' in root) ? function(a, b){
if (!a.sourceIndex || !b.sourceIndex) return 0;
return a.sourceIndex - b.sourceIndex;
} : (document.createRange) ? function(a, b){
if (!a.ownerDocument || !b.ownerDocument) return 0;
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
aRange.setStart(a, 0);
aRange.setEnd(a, 0);
bRange.setStart(b, 0);
bRange.setEnd(b, 0);
return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
} : null ;
root = null;
for (feature in features){
this[feature] = features[feature];
}
};
// Main Method
var reSiblingContextSelector = /^\s*[+~]/,
reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
qsaFailExpCache = {};
local.search = function(context, expression, append, first){
var found = this.found = (first) ? null : (append || []);
if (!context) return found;
else if (context.navigator) context = context.document; // Convert the node from a window to a document
else if (!context.nodeType) return found;
// setup
var parsed, i,
uniques = this.uniques = {},
hasOthers = !!(append && append.length),
contextIsDocument = (context.nodeType == 9);
if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
// avoid duplicating items already in the append array
if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
// expression checks
if (typeof expression == 'string'){ // expression is a string
/*<simple-selectors-override>*/
var simpleSelector = expression.match(reSimpleSelector);
simpleSelectors: if (simpleSelector) {
var symbol = simpleSelector[1],
name = simpleSelector[2],
node, nodes;
if (!symbol){
if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
nodes = context.getElementsByTagName(name);
if (first) return nodes[0] || null;
for (i = 0; node = nodes[i++];){
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
}
} else if (symbol == '#'){
if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
node = context.getElementById(name);
if (!node) return found;
if (this.idGetsName && this.getAttribute(node, 'id') != name) break simpleSelectors;
if (first) return node || null;
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
} else if (symbol == '.'){
if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
if (context.getElementsByClassName && !this.brokenGEBCN){
nodes = context.getElementsByClassName(name);
if (first) return nodes[0] || null;
for (i = 0; node = nodes[i++];){
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
}
} else {
var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
nodes = context.getElementsByTagName('*');
for (i = 0; node = nodes[i++];){
className = node.className;
if (!(className && matchClass.test(className))) continue;
if (first) return node;
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
}
}
}
if (hasOthers) this.sort(found);
return (first) ? null : found;
}
/*</simple-selectors-override>*/
/*<query-selector-override>*/
querySelector: if (context.querySelectorAll) {
if (!this.isHTMLDocument
|| qsaFailExpCache[expression]
//TODO: only skip when expression is actually mixed case
|| this.brokenMixedCaseQSA
|| (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
|| (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
|| (!contextIsDocument //Abort when !contextIsDocument and...
// there are multiple expressions in the selector
// since we currently only fix non-document rooted QSA for single expression selectors
&& expression.indexOf(',') > -1
)
|| Slick.disableQSA
) break querySelector;
var _expression = expression,
_context = context;
if (!contextIsDocument){
if (reSiblingContextSelector.test(_expression)) {
context = _context.parentNode;
}
// non-document rooted QSA
// credits to Andrew Dupont
var currentId = _context.getAttribute('id'),
slickid = 'slickid__';
_context.setAttribute('id', slickid);
_expression = '#' + slickid + ' ' + _expression;
}
try {
if (first) return context.querySelector(_expression) || null;
else nodes = context.querySelectorAll(_expression);
} catch(e) {
qsaFailExpCache[expression] = 1;
break querySelector;
} finally {
if (!contextIsDocument){
if (currentId) _context.setAttribute('id', currentId);
else _context.removeAttribute('id');
context = _context;
}
}
if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
} else for (i = 0; node = nodes[i++];){
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
}
if (hasOthers) this.sort(found);
return found;
}
/*</query-selector-override>*/
parsed = this.Slick.parse(expression);
if (!parsed.length) return found;
} else if (expression == null){ // there is no expression
return found;
} else if (expression.Slick){ // expression is a parsed Slick object
parsed = expression;
} else if (this.contains(context.documentElement || context, expression)){ // expression is a node
(found) ? found.push(expression) : found = expression;
return found;
} else { // other junk
return found;
}
/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
// cache elements for the nth selectors
this.posNTH = {};
this.posNTHLast = {};
this.posNTHType = {};
this.posNTHTypeLast = {};
/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
// if append is null and there is only a single selector with one expression use pushArray, else use pushUID
this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
if (found == null) found = [];
// default engine
var j, m, n,
combinator, tag, id, classList, classes, attributes, pseudos,
currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
combinator = 'combinator:' + currentBit.combinator;
if (!this[combinator]) continue search;
tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
id = currentBit.id;
classList = currentBit.classList;
classes = currentBit.classes;
attributes = currentBit.attributes;
pseudos = currentBit.pseudos;
lastBit = (j === (currentExpression.length - 1));
this.bitUniques = {};
if (lastBit){
this.uniques = uniques;
this.found = found;
} else {
this.uniques = {};
this.found = [];
}
if (j === 0){
this[combinator](context, tag, id, classes, attributes, pseudos, classList);
if (first && lastBit && found.length) break search;
} else {
if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
if (found.length) break search;
} else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
}
currentItems = this.found;
}
// should sort if there are nodes in append and if you pass multiple expressions.
if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
return (first) ? (found[0] || null) : found;
};
// Utils
local.uidx = 1;
local.uidk = 'slick-uniqueid';
local.getUIDXML = function(node){
var uid = node.getAttribute(this.uidk);
if (!uid){
uid = this.uidx++;
node.setAttribute(this.uidk, uid);
}
return uid;
};
local.getUIDHTML = function(node){
return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
};
// sort based on the setDocument documentSorter method.
local.sort = function(results){
if (!this.documentSorter) return results;
results.sort(this.documentSorter);
return results;
};
/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
local.cacheNTH = {};
local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
local.parseNTHArgument = function(argument){
var parsed = argument.match(this.matchNTH);
if (!parsed) return false;
var special = parsed[2] || false;
var a = parsed[1] || 1;
if (a == '-') a = -1;
var b = +parsed[3] || 0;
parsed =
(special == 'n') ? {a: a, b: b} :
(special == 'odd') ? {a: 2, b: 1} :
(special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
return (this.cacheNTH[argument] = parsed);
};
local.createNTHPseudo = function(child, sibling, positions, ofType){
return function(node, argument){
var uid = this.getUID(node);
if (!this[positions][uid]){
var parent = node.parentNode;
if (!parent) return false;
var el = parent[child], count = 1;
if (ofType){
var nodeName = node.nodeName;
do {
if (el.nodeName != nodeName) continue;
this[positions][this.getUID(el)] = count++;
} while ((el = el[sibling]));
} else {
do {
if (el.nodeType != 1) continue;
this[positions][this.getUID(el)] = count++;
} while ((el = el[sibling]));
}
}
argument = argument || 'n';
var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
if (!parsed) return false;
var a = parsed.a, b = parsed.b, pos = this[positions][uid];
if (a == 0) return b == pos;
if (a > 0){
if (pos < b) return false;
} else {
if (b < pos) return false;
}
return ((pos - b) % a) == 0;
};
};
/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
local.pushArray = function(node, tag, id, classes, attributes, pseudos){
if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
};
local.pushUID = function(node, tag, id, classes, attributes, pseudos){
var uid = this.getUID(node);
if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
this.uniques[uid] = true;
this.found.push(node);
}
};
local.matchNode = function(node, selector){
if (this.isHTMLDocument && this.nativeMatchesSelector){
try {
return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
} catch(matchError) {}
}
var parsed = this.Slick.parse(selector);
if (!parsed) return true;
// simple (single) selectors
var expressions = parsed.expressions, simpleExpCounter = 0, i;
for (i = 0; (currentExpression = expressions[i]); i++){
if (currentExpression.length == 1){
var exp = currentExpression[0];
if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
simpleExpCounter++;
}
}
if (simpleExpCounter == parsed.length) return false;
var nodes = this.search(this.document, parsed), item;
for (i = 0; item = nodes[i++];){
if (item === node) return true;
}
return false;
};
local.matchPseudo = function(node, name, argument){
var pseudoName = 'pseudo:' + name;
if (this[pseudoName]) {
// saves a this.found reference so if the pseudo-selector
// uses Slick it wont mess with the current reference
var found = this.found,
result = this[pseudoName](node, argument);
this.found = found;
return result;
}
var attribute = this.getAttribute(node, name);
return (argument) ? argument == attribute : !!attribute;
};
local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
if (tag){
var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
if (tag == '*'){
if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
} else {
if (nodeName != tag) return false;
}
}
if (id && node.getAttribute('id') != id) return false;
var i, part, cls;
if (classes) for (i = classes.length; i--;){
cls = this.getAttribute(node, 'class');
if (!(cls && classes[i].regexp.test(cls))) return false;
}
if (attributes) for (i = attributes.length; i--;){
part = attributes[i];
if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
}
if (pseudos) for (i = pseudos.length; i--;){
part = pseudos[i];
if (!this.matchPseudo(node, part.key, part.value)) return false;
}
return true;
};
var combinators = {
' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
var i, item, children;
if (this.isHTMLDocument){
getById: if (id){
item = this.document.getElementById(id);
if ((!item && node.all) || (this.idGetsName && item && this.getAttribute(item, 'id') != id)){
// all[id] returns all the elements with that name or id inside node
// if theres just one it will return the element, else it will be a collection
children = node.all[id];
if (!children) return;
if (!children[0]) children = [children];
for (i = 0; item = children[i++];){
if (this.getAttribute(item, 'id') == id){
this.push(item, tag, null, classes, attributes, pseudos);
break;
}
}
return;
}
if (!item){
// if the context is in the dom we return, else we will try GEBTN, breaking the getById label
if (this.contains(this.root, node)) return;
else break getById;
} else if (this.document !== node && !this.contains(node, item)) return;
this.push(item, tag, null, classes, attributes, pseudos);
return;
}
getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
children = node.getElementsByClassName(classList.join(' '));
if (!(children && children.length)) break getByClass;
for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
return;
}
}
getByTag: {
children = node.getElementsByTagName(tag);
if (!(children && children.length)) break getByTag;
if (!this.brokenStarGEBTN) tag = null;
for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
}
},
'>': function(node, tag, id, classes, attributes, pseudos){ // direct children
if ((node = node.firstChild)) do {
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
} while ((node = node.nextSibling));
},
'+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
while ((node = node.nextSibling)) if (node.nodeType == 1){
this.push(node, tag, id, classes, attributes, pseudos);
break;
}
},
'^': function(node, tag, id, classes, attributes, pseudos){ // first child
node = node.firstChild;
if (node){
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
}
},
'~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
while ((node = node.nextSibling)){
if (node.nodeType != 1) continue;
var uid = this.getUID(node);
if (this.bitUniques[uid]) break;
this.bitUniques[uid] = true;
this.push(node, tag, id, classes, attributes, pseudos);
}
},
'++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
this['combinator:+'](node, tag, id, classes, attributes, pseudos);
this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
},
'~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
this['combinator:~'](node, tag, id, classes, attributes, pseudos);
this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
},
'!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
},
'!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
node = node.parentNode;
if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
},
'!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
while ((node = node.previousSibling)) if (node.nodeType == 1){
this.push(node, tag, id, classes, attributes, pseudos);
break;
}
},
'!^': function(node, tag, id, classes, attributes, pseudos){ // last child
node = node.lastChild;
if (node){
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
}
},
'!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
while ((node = node.previousSibling)){
if (node.nodeType != 1) continue;
var uid = this.getUID(node);
if (this.bitUniques[uid]) break;
this.bitUniques[uid] = true;
this.push(node, tag, id, classes, attributes, pseudos);
}
}
};
for (var c in combinators) local['combinator:' + c] = combinators[c];
var pseudos = {
/*<pseudo-selectors>*/
'empty': function(node){
var child = node.firstChild;
return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
},
'not': function(node, expression){
return !this.matchNode(node, expression);
},
'contains': function(node, text){
return (node.innerText || node.textContent || '').indexOf(text) > -1;
},
'first-child': function(node){
while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
return true;
},
'last-child': function(node){
while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
return true;
},
'only-child': function(node){
var prev = node;
while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
var next = node;
while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
return true;
},
/*<nth-pseudo-selectors>*/
'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
'index': function(node, index){
return this['pseudo:nth-child'](node, '' + ((+index) + 1));
},
'even': function(node){
return this['pseudo:nth-child'](node, '2n');
},
'odd': function(node){
return this['pseudo:nth-child'](node, '2n+1');
},
/*</nth-pseudo-selectors>*/
/*<of-type-pseudo-selectors>*/
'first-of-type': function(node){
var nodeName = node.nodeName;
while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
return true;
},
'last-of-type': function(node){
var nodeName = node.nodeName;
while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
return true;
},
'only-of-type': function(node){
var prev = node, nodeName = node.nodeName;
while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
var next = node;
while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
return true;
},
/*</of-type-pseudo-selectors>*/
// custom pseudos
'enabled': function(node){
return !node.disabled;
},
'disabled': function(node){
return node.disabled;
},
'checked': function(node){
return node.checked || node.selected;
},
'focus': function(node){
return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
},
'root': function(node){
return (node === this.root);
},
'selected': function(node){
return node.selected;
}
/*</pseudo-selectors>*/
};
for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
// attributes methods
var attributeGetters = local.attributeGetters = {
'for': function(){
return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
},
'href': function(){
return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
},
'style': function(){
return (this.style) ? this.style.cssText : this.getAttribute('style');
},
'type': function(){
return this.getAttribute('type');
},
'tabindex': function(){
var attributeNode = this.getAttributeNode('tabindex');
return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
},
'maxlength': function(){
var attributeNode = this.getAttributeNode('maxLength');
return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
}
};
attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
// Slick
var Slick = local.Slick = (this.Slick || {});
Slick.version = '1.1.7';
// Slick finder
Slick.search = function(context, expression, append){
return local.search(context, expression, append);
};
Slick.find = function(context, expression){
return local.search(context, expression, null, true);
};
// Slick containment checker
Slick.contains = function(container, node){
local.setDocument(container);
return local.contains(container, node);
};
// Slick attribute getter
Slick.getAttribute = function(node, name){
local.setDocument(node);
return local.getAttribute(node, name);
};
Slick.hasAttribute = function(node, name){
local.setDocument(node);
return local.hasAttribute(node, name);
};
// Slick matcher
Slick.match = function(node, selector){
if (!(node && selector)) return false;
if (!selector || selector === node) return true;
local.setDocument(node);
return local.matchNode(node, selector);
};
// Slick attribute accessor
Slick.defineAttributeGetter = function(name, fn){
local.attributeGetters[name] = fn;
return this;
};
Slick.lookupAttributeGetter = function(name){
return local.attributeGetters[name];
};
// Slick pseudo accessor
Slick.definePseudo = function(name, fn){
local['pseudo:' + name] = function(node, argument){
return fn.call(node, argument);
};
return this;
};
Slick.lookupPseudo = function(name){
var pseudo = local['pseudo:' + name];
if (pseudo) return function(argument){
return pseudo.call(this, argument);
};
return null;
};
// Slick overrides accessor
Slick.override = function(regexp, fn){
local.override(regexp, fn);
return this;
};
Slick.isXML = local.isXML;
Slick.uidOf = function(node){
return local.getUIDHTML(node);
};
if (!this.Slick) this.Slick = Slick;
}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);