tt-rss/lib/dojo/selector/lite.js.uncompressed.js
2013-03-18 10:26:26 +04:00

283 lines
9.2 KiB
JavaScript

define("dojo/selector/lite", ["../has", "../_base/kernel"], function(has, dojo){
"use strict";
var testDiv = document.createElement("div");
var matchesSelector = testDiv.matchesSelector || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector; // IE9, WebKit, Firefox have this, but not Opera yet
var querySelectorAll = testDiv.querySelectorAll;
var unionSplit = /([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g;
has.add("dom-matches-selector", !!matchesSelector);
has.add("dom-qsa", !!querySelectorAll);
// this is a simple query engine. It has handles basic selectors, and for simple
// common selectors is extremely fast
var liteEngine = function(selector, root){
// summary:
// A small lightweight query selector engine that implements CSS2.1 selectors
// minus pseudo-classes and the sibling combinator, plus CSS3 attribute selectors
if(combine && selector.indexOf(',') > -1){
return combine(selector, root);
}
// use the root's ownerDocument if provided, otherwise try to use dojo.doc. Note
// that we don't use dojo/_base/window's doc to reduce dependencies, and
// fallback to plain document if dojo.doc hasn't been defined (by dojo/_base/window).
// presumably we will have a better way to do this in 2.0
var doc = root ? root.ownerDocument || root : dojo.doc || document,
match = (querySelectorAll ?
/^([\w]*)#([\w\-]+$)|^(\.)([\w\-\*]+$)|^(\w+$)/ : // this one only matches on simple queries where we can beat qSA with specific methods
/^([\w]*)#([\w\-]+)(?:\s+(.*))?$|(?:^|(>|.+\s+))([\w\-\*]+)(\S*$)/) // this one matches parts of the query that we can use to speed up manual filtering
.exec(selector);
root = root || doc;
if(match){
// fast path regardless of whether or not querySelectorAll exists
if(match[2]){
// an #id
// use dojo.byId if available as it fixes the id retrieval in IE, note that we can't use the dojo namespace in 2.0, but if there is a conditional module use, we will use that
var found = dojo.byId ? dojo.byId(match[2]) : doc.getElementById(match[2]);
if(!found || (match[1] && match[1] != found.tagName.toLowerCase())){
// if there is a tag qualifer and it doesn't match, no matches
return [];
}
if(root != doc){
// there is a root element, make sure we are a child of it
var parent = found;
while(parent != root){
parent = parent.parentNode;
if(!parent){
return [];
}
}
}
return match[3] ?
liteEngine(match[3], found)
: [found];
}
if(match[3] && root.getElementsByClassName){
// a .class
return root.getElementsByClassName(match[4]);
}
var found;
if(match[5]){
// a tag
found = root.getElementsByTagName(match[5]);
if(match[4] || match[6]){
selector = (match[4] || "") + match[6];
}else{
// that was the entirety of the query, return results
return found;
}
}
}
if(querySelectorAll){
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
if (root.nodeType === 1 && root.nodeName.toLowerCase() !== "object"){
return useRoot(root, selector, root.querySelectorAll);
}else{
// we can use the native qSA
return root.querySelectorAll(selector);
}
}else if(!found){
// search all children and then filter
found = root.getElementsByTagName("*");
}
// now we filter the nodes that were found using the matchesSelector
var results = [];
for(var i = 0, l = found.length; i < l; i++){
var node = found[i];
if(node.nodeType == 1 && jsMatchesSelector(node, selector, root)){
// keep the nodes that match the selector
results.push(node);
}
}
return results;
};
var useRoot = function(context, query, method){
// this function creates a temporary id so we can do rooted qSA queries, this is taken from sizzle
var oldContext = context,
old = context.getAttribute("id"),
nid = old || "__dojo__",
hasParent = context.parentNode,
relativeHierarchySelector = /^\s*[+~]/.test(query);
if(relativeHierarchySelector && !hasParent){
return [];
}
if(!old){
context.setAttribute("id", nid);
}else{
nid = nid.replace(/'/g, "\\$&");
}
if(relativeHierarchySelector && hasParent){
context = context.parentNode;
}
var selectors = query.match(unionSplit);
for(var i = 0; i < selectors.length; i++){
selectors[i] = "[id='" + nid + "'] " + selectors[i];
}
query = selectors.join(",");
try{
return method.call(context, query);
}finally{
if(!old){
oldContext.removeAttribute("id");
}
}
};
if(!has("dom-matches-selector")){
var jsMatchesSelector = (function(){
// a JS implementation of CSS selector matching, first we start with the various handlers
var caseFix = testDiv.tagName == "div" ? "toLowerCase" : "toUpperCase";
var selectorTypes = {
"": function(tagName){
tagName = tagName[caseFix]();
return function(node){
return node.tagName == tagName;
};
},
".": function(className){
var classNameSpaced = ' ' + className + ' ';
return function(node){
return node.className.indexOf(className) > -1 && (' ' + node.className + ' ').indexOf(classNameSpaced) > -1;
};
},
"#": function(id){
return function(node){
return node.id == id;
};
}
};
var attrComparators = {
"^=": function(attrValue, value){
return attrValue.indexOf(value) == 0;
},
"*=": function(attrValue, value){
return attrValue.indexOf(value) > -1;
},
"$=": function(attrValue, value){
return attrValue.substring(attrValue.length - value.length, attrValue.length) == value;
},
"~=": function(attrValue, value){
return (' ' + attrValue + ' ').indexOf(' ' + value + ' ') > -1;
},
"|=": function(attrValue, value){
return (attrValue + '-').indexOf(value + '-') == 0;
},
"=": function(attrValue, value){
return attrValue == value;
},
"": function(attrValue, value){
return true;
}
};
function attr(name, value, type){
var firstChar = value.charAt(0);
if(firstChar == '"' || firstChar == "'"){
// it is quoted, remove the quotes
value = value.slice(1, -1);
}
value = value.replace(/\\/g,'');
var comparator = attrComparators[type || ""];
return function(node){
var attrValue = node.getAttribute(name);
return attrValue && comparator(attrValue, value);
};
}
function ancestor(matcher){
return function(node, root){
while((node = node.parentNode) != root){
if(matcher(node, root)){
return true;
}
}
};
}
function parent(matcher){
return function(node, root){
node = node.parentNode;
return matcher ?
node != root && matcher(node, root)
: node == root;
};
}
var cache = {};
function and(matcher, next){
return matcher ?
function(node, root){
return next(node) && matcher(node, root);
}
: next;
}
return function(node, selector, root){
// this returns true or false based on if the node matches the selector (optionally within the given root)
var matcher = cache[selector]; // check to see if we have created a matcher function for the given selector
if(!matcher){
// create a matcher function for the given selector
// parse the selectors
if(selector.replace(/(?:\s*([> ])\s*)|(#|\.)?((?:\\.|[\w-])+)|\[\s*([\w-]+)\s*(.?=)?\s*("(?:\\.|[^"])+"|'(?:\\.|[^'])+'|(?:\\.|[^\]])*)\s*\]/g, function(t, combinator, type, value, attrName, attrType, attrValue){
if(value){
matcher = and(matcher, selectorTypes[type || ""](value.replace(/\\/g, '')));
}
else if(combinator){
matcher = (combinator == " " ? ancestor : parent)(matcher);
}
else if(attrName){
matcher = and(matcher, attr(attrName, attrValue, attrType));
}
return "";
})){
throw new Error("Syntax error in query");
}
if(!matcher){
return true;
}
cache[selector] = matcher;
}
// now run the matcher function on the node
return matcher(node, root);
};
})();
}
if(!has("dom-qsa")){
var combine = function(selector, root){
// combined queries
var selectors = selector.match(unionSplit);
var indexed = [];
// add all results and keep unique ones, this only runs in IE, so we take advantage
// of known IE features, particularly sourceIndex which is unique and allows us to
// order the results
for(var i = 0; i < selectors.length; i++){
selector = new String(selectors[i].replace(/\s*$/,''));
selector.indexOf = escape; // keep it from recursively entering combine
var results = liteEngine(selector, root);
for(var j = 0, l = results.length; j < l; j++){
var node = results[j];
indexed[node.sourceIndex] = node;
}
}
// now convert from a sparse array to a dense array
var totalResults = [];
for(i in indexed){
totalResults.push(indexed[i]);
}
return totalResults;
};
}
liteEngine.match = matchesSelector ? function(node, selector, root){
if(root && root.nodeType != 9){
// doesn't support three args, use rooted id trick
return useRoot(root, selector, function(query){
return matchesSelector.call(node, query);
});
}
// we have a native matchesSelector, use that
return matchesSelector.call(node, selector);
} : jsMatchesSelector; // otherwise use the JS matches impl
return liteEngine;
});