/* --- 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 = ''; 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:"") for getElementsByTagName('*') for some documents // Should never inject incorrect markup on XML documents if (!features.isXMLDocument){ try { testNode.innerHTML = '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 = ''; features.idGetsName = document.getElementById(id) === testNode.firstChild; } catch(e){} if (testNode.getElementsByClassName){ // Safari 3.2 getElementsByClassName caches results try { testNode.innerHTML = ''; 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 = ''; brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2); } catch(e){} features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN; } if (testNode.querySelectorAll){ // IE 8 returns closed nodes (EG:"") for querySelectorAll('*') for some documents // Should never inject incorrect markup on XML documents if (!features.isXMLDocument){ try { testNode.innerHTML = '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 = ''; features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length; } catch(e){} // Webkit and Opera dont return selected options on querySelectorAll try { testNode.innerHTML = ''; features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0); } catch(e){} // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll try { testNode.innerHTML = ''; 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 = '_'; } 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 = '
'; 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 /**/ 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; } /**/ /**/ 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; } /**/ 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; } /**//**/ // cache elements for the nth selectors this.posNTH = {}; this.posNTHLast = {}; this.posNTHType = {}; this.posNTHTypeLast = {}; /**//**/ // 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; }; /**//**/ 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; }; }; /**//**/ 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 = { /**/ '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-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'); }, /**/ /**/ '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; }, /**/ // 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; } /**/ }; 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(/**/(typeof exports != 'undefined') ? exports : /**/this);