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.
1011 lines
30 KiB
1011 lines
30 KiB
6 years ago
|
/*
|
||
|
---
|
||
|
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);
|