This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1010 lines
30 KiB
1010 lines
30 KiB
/* |
|
--- |
|
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);
|
|
|