summaryrefslogtreecommitdiff
path: root/includes/js/dojo/_base/query.js
diff options
context:
space:
mode:
Diffstat (limited to 'includes/js/dojo/_base/query.js')
-rw-r--r--includes/js/dojo/_base/query.js1191
1 files changed, 0 insertions, 1191 deletions
diff --git a/includes/js/dojo/_base/query.js b/includes/js/dojo/_base/query.js
deleted file mode 100644
index 8743df8..0000000
--- a/includes/js/dojo/_base/query.js
+++ /dev/null
@@ -1,1191 +0,0 @@
-if(!dojo._hasResource["dojo._base.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.query"] = true;
-dojo.provide("dojo._base.query");
-dojo.require("dojo._base.NodeList");
-
-/*
- dojo.query() architectural overview:
-
- dojo.query is a relatively full-featured CSS3 query library. It is
- designed to take any valid CSS3 selector and return the nodes matching
- the selector. To do this quickly, it processes queries in several
- steps, applying caching where profitable.
-
- The steps (roughly in reverse order of the way they appear in the code):
- 1.) check to see if we already have a "query dispatcher"
- - if so, use that with the given parameterization. Skip to step 4.
- 2.) attempt to determine which branch to dispatch the query to:
- - JS (optimized DOM iteration)
- - xpath (for browsers that support it and where it's fast)
- - native (not available in any browser yet)
- 3.) tokenize and convert to executable "query dispatcher"
- - this is where the lion's share of the complexity in the
- system lies. In the DOM version, the query dispatcher is
- assembled as a chain of "yes/no" test functions pertaining to
- a section of a simple query statement (".blah:nth-child(odd)"
- but not "div div", which is 2 simple statements). Individual
- statement dispatchers are cached (to prevent re-definition)
- as are entire dispatch chains (to make re-execution of the
- same query fast)
- - in the xpath path, tokenization yeilds a concatenation of
- parameterized xpath selectors. As with the DOM version, both
- simple selector blocks and overall evaluators are cached to
- prevent re-defintion
- 4.) the resulting query dispatcher is called in the passed scope (by default the top-level document)
- - for DOM queries, this results in a recursive, top-down
- evaluation of nodes based on each simple query section
- - xpath queries can, thankfully, be executed in one shot
- 5.) matched nodes are pruned to ensure they are unique
-*/
-
-;(function(){
- // define everything in a closure for compressability reasons. "d" is an
- // alias to "dojo" since it's so frequently used. This seems a
- // transformation that the build system could perform on a per-file basis.
-
- ////////////////////////////////////////////////////////////////////////
- // Utility code
- ////////////////////////////////////////////////////////////////////////
-
- var d = dojo;
- var childNodesName = dojo.isIE ? "children" : "childNodes";
- var caseSensitive = false;
-
- var getQueryParts = function(query){
- // summary: state machine for query tokenization
- if(">~+".indexOf(query.charAt(query.length-1)) >= 0){
- query += " *"
- }
- query += " "; // ensure that we terminate the state machine
-
- var ts = function(s, e){
- return d.trim(query.slice(s, e));
- }
-
- // the overall data graph of the full query, as represented by queryPart objects
- var qparts = [];
- // state keeping vars
- var inBrackets = -1;
- var inParens = -1;
- var inMatchFor = -1;
- var inPseudo = -1;
- var inClass = -1;
- var inId = -1;
- var inTag = -1;
- var lc = ""; // the last character
- var cc = ""; // the current character
- var pStart;
- // iteration vars
- var x = 0; // index in the query
- var ql = query.length;
- var currentPart = null; // data structure representing the entire clause
- var _cp = null; // the current pseudo or attr matcher
-
- var endTag = function(){
- if(inTag >= 0){
- var tv = (inTag == x) ? null : ts(inTag, x).toLowerCase();
- currentPart[ (">~+".indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
- inTag = -1;
- }
- }
-
- var endId = function(){
- if(inId >= 0){
- currentPart.id = ts(inId, x).replace(/\\/g, "");
- inId = -1;
- }
- }
-
- var endClass = function(){
- if(inClass >= 0){
- currentPart.classes.push(ts(inClass+1, x).replace(/\\/g, ""));
- inClass = -1;
- }
- }
-
- var endAll = function(){
- endId(); endTag(); endClass();
- }
-
- for(; lc=cc, cc=query.charAt(x),x<ql; x++){
- if(lc == "\\"){ continue; }
- if(!currentPart){
- // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
- pStart = x;
- currentPart = {
- query: null,
- pseudos: [],
- attrs: [],
- classes: [],
- tag: null,
- oper: null,
- id: null
- };
- inTag = x;
- }
-
- if(inBrackets >= 0){
- // look for a the close first
- if(cc == "]"){
- if(!_cp.attr){
- _cp.attr = ts(inBrackets+1, x);
- }else{
- _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
- }
- var cmf = _cp.matchFor;
- if(cmf){
- if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
- _cp.matchFor = cmf.substring(1, cmf.length-1);
- }
- }
- currentPart.attrs.push(_cp);
- _cp = null; // necessaray?
- inBrackets = inMatchFor = -1;
- }else if(cc == "="){
- var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
- _cp.type = addToCc+cc;
- _cp.attr = ts(inBrackets+1, x-addToCc.length);
- inMatchFor = x+1;
- }
- // now look for other clause parts
- }else if(inParens >= 0){
- if(cc == ")"){
- if(inPseudo >= 0){
- _cp.value = ts(inParens+1, x);
- }
- inPseudo = inParens = -1;
- }
- }else if(cc == "#"){
- endAll();
- inId = x+1;
- }else if(cc == "."){
- endAll();
- inClass = x;
- }else if(cc == ":"){
- endAll();
- inPseudo = x;
- }else if(cc == "["){
- endAll();
- inBrackets = x;
- _cp = {
- /*=====
- attr: null, type: null, matchFor: null
- =====*/
- };
- }else if(cc == "("){
- if(inPseudo >= 0){
- _cp = {
- name: ts(inPseudo+1, x),
- value: null
- }
- currentPart.pseudos.push(_cp);
- }
- inParens = x;
- }else if(cc == " " && lc != cc){
- // note that we expect the string to be " " terminated
- endAll();
- if(inPseudo >= 0){
- currentPart.pseudos.push({ name: ts(inPseudo+1, x) });
- }
- currentPart.hasLoops = (
- currentPart.pseudos.length ||
- currentPart.attrs.length ||
- currentPart.classes.length );
- currentPart.query = ts(pStart, x);
- currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
- qparts.push(currentPart);
- currentPart = null;
- }
- }
- return qparts;
- };
-
-
- ////////////////////////////////////////////////////////////////////////
- // XPath query code
- ////////////////////////////////////////////////////////////////////////
-
- // this array is a lookup used to generate an attribute matching function.
- // There is a similar lookup/generator list for the DOM branch with similar
- // calling semantics.
- var xPathAttrs = {
- "*=": function(attr, value){
- return "[contains(@"+attr+", '"+ value +"')]";
- },
- "^=": function(attr, value){
- return "[starts-with(@"+attr+", '"+ value +"')]";
- },
- "$=": function(attr, value){
- return "[substring(@"+attr+", string-length(@"+attr+")-"+(value.length-1)+")='"+value+"']";
- },
- "~=": function(attr, value){
- return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
- },
- "|=": function(attr, value){
- return "[contains(concat(' ',@"+attr+",' '), ' "+ value +"-')]";
- },
- "=": function(attr, value){
- return "[@"+attr+"='"+ value +"']";
- }
- };
-
- // takes a list of attribute searches, the overall query, a function to
- // generate a default matcher, and a closure-bound method for providing a
- // matching function that generates whatever type of yes/no distinguisher
- // the query method needs. The method is a bit tortured and hard to read
- // because it needs to be used in both the XPath and DOM branches.
- var handleAttrs = function( attrList,
- query,
- getDefault,
- handleMatch){
- d.forEach(query.attrs, function(attr){
- var matcher;
- // type, attr, matchFor
- if(attr.type && attrList[attr.type]){
- matcher = attrList[attr.type](attr.attr, attr.matchFor);
- }else if(attr.attr.length){
- matcher = getDefault(attr.attr);
- }
- if(matcher){ handleMatch(matcher); }
- });
- }
-
- var buildPath = function(query){
- var xpath = ".";
- var qparts = getQueryParts(d.trim(query));
- while(qparts.length){
- var tqp = qparts.shift();
- var prefix;
- var postfix = "";
- if(tqp.oper == ">"){
- prefix = "/";
- // prefix = "/child::*";
- tqp = qparts.shift();
- }else if(tqp.oper == "~"){
- prefix = "/following-sibling::"; // get element following siblings
- tqp = qparts.shift();
- }else if(tqp.oper == "+"){
- // FIXME:
- // fails when selecting subsequent siblings by node type
- // because the position() checks the position in the list
- // of matching elements and not the localized siblings
- prefix = "/following-sibling::";
- postfix = "[position()=1]";
- tqp = qparts.shift();
- }else{
- prefix = "//";
- // prefix = "/descendant::*"
- }
-
- // get the tag name (if any)
-
- xpath += prefix + tqp.tag + postfix;
-
- // check to see if it's got an id. Needs to come first in xpath.
- if(tqp.id){
- xpath += "[@id='"+tqp.id+"'][1]";
- }
-
- d.forEach(tqp.classes, function(cn){
- var cnl = cn.length;
- var padding = " ";
- if(cn.charAt(cnl-1) == "*"){
- padding = ""; cn = cn.substr(0, cnl-1);
- }
- xpath +=
- "[contains(concat(' ',@class,' '), ' "+
- cn + padding + "')]";
- });
-
- handleAttrs(xPathAttrs, tqp,
- function(condition){
- return "[@"+condition+"]";
- },
- function(matcher){
- xpath += matcher;
- }
- );
-
- // FIXME: need to implement pseudo-class checks!!
- };
- return xpath;
- };
-
- var _xpathFuncCache = {};
- var getXPathFunc = function(path){
- if(_xpathFuncCache[path]){
- return _xpathFuncCache[path];
- }
-
- var doc = d.doc;
- // don't need to memoize. The closure scope handles it for us.
- var xpath = buildPath(path);
-
- var tf = function(parent){
- // XPath query strings are memoized.
- var ret = [];
- var xpathResult;
- try{
- xpathResult = doc.evaluate(xpath, parent, null,
- // XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
- XPathResult.ANY_TYPE, null);
- }catch(e){
- console.debug("failure in exprssion:", xpath, "under:", parent);
- console.debug(e);
- }
- var result = xpathResult.iterateNext();
- while(result){
- ret.push(result);
- result = xpathResult.iterateNext();
- }
- return ret;
- }
- return _xpathFuncCache[path] = tf;
- };
-
- /*
- d.xPathMatch = function(query){
- // XPath based DOM query system. Handles a small subset of CSS
- // selectors, subset is identical to the non-XPath version of this
- // function.
-
- return getXPathFunc(query)();
- }
- */
-
- ////////////////////////////////////////////////////////////////////////
- // DOM query code
- ////////////////////////////////////////////////////////////////////////
-
- var _filtersCache = {};
- var _simpleFiltersCache = {};
-
- // the basic building block of the yes/no chaining system. agree(f1, f2)
- // generates a new function which returns the boolean results of both of
- // the passed functions to a single logical-anded result.
- var agree = function(first, second){
- if(!first){ return second; }
- if(!second){ return first; }
-
- return function(){
- return first.apply(window, arguments) && second.apply(window, arguments);
- }
- }
-
- var _childElements = function(root){
- var ret = [];
- var te, x=0, tret = root[childNodesName];
- while(te=tret[x++]){
- if(te.nodeType == 1){ ret.push(te); }
- }
- return ret;
- }
-
- var _nextSiblings = function(root, single){
- var ret = [];
- var te = root;
- while(te = te.nextSibling){
- if(te.nodeType == 1){
- ret.push(te);
- if(single){ break; }
- }
- }
- return ret;
- }
-
- var _filterDown = function(element, queryParts, matchArr, idx){
- // NOTE:
- // in the fast path! this function is called recursively and for
- // every run of a query.
- var nidx = idx+1;
- var isFinal = (queryParts.length == nidx);
- var tqp = queryParts[idx];
-
- // see if we can constrain our next level to direct children
- if(tqp.oper){
- var ecn = (tqp.oper == ">") ?
- _childElements(element) :
- _nextSiblings(element, (tqp.oper == "+"));
-
- if(!ecn || !ecn.length){
- return;
- }
- nidx++;
- isFinal = (queryParts.length == nidx);
- // kinda janky, too much array alloc
- var tf = getFilterFunc(queryParts[idx+1]);
- // for(var x=ecn.length-1, te; x>=0, te=ecn[x]; x--){
- for(var x=0, ecnl=ecn.length, te; x<ecnl, te=ecn[x]; x++){
- if(tf(te)){
- if(isFinal){
- matchArr.push(te);
- }else{
- _filterDown(te, queryParts, matchArr, nidx);
- }
- }
- /*
- if(x==0){
- break;
- }
- */
- }
- }
-
- // otherwise, keep going down, unless we'er at the end
- var candidates = getElementsFunc(tqp)(element);
- if(isFinal){
- while(candidates.length){
- matchArr.push(candidates.shift());
- }
- /*
- candidates.unshift(0, matchArr.length-1);
- matchArr.splice.apply(matchArr, candidates);
- */
- }else{
- // if we're not yet at the bottom, keep going!
- while(candidates.length){
- _filterDown(candidates.shift(), queryParts, matchArr, nidx);
- }
- }
- }
-
- var filterDown = function(elements, queryParts){
- var ret = [];
-
- // for every root, get the elements that match the descendant selector
- // for(var x=elements.length-1, te; x>=0, te=elements[x]; x--){
- var x = elements.length - 1, te;
- while(te = elements[x--]){
- _filterDown(te, queryParts, ret, 0);
- }
- return ret;
- }
-
- var getFilterFunc = function(q){
- // note: query can't have spaces!
- if(_filtersCache[q.query]){
- return _filtersCache[q.query];
- }
- var ff = null;
-
- // does it have a tagName component?
- if(q.tag){
- if(q.tag == "*"){
- ff = agree(ff,
- function(elem){
- return (elem.nodeType == 1);
- }
- );
- }else{
- // tag name match
- ff = agree(ff,
- function(elem){
- return (
- (elem.nodeType == 1) &&
- (q.tag == elem.tagName.toLowerCase())
- );
- // return isTn;
- }
- );
- }
- }
-
- // does the node have an ID?
- if(q.id){
- ff = agree(ff,
- function(elem){
- return (
- (elem.nodeType == 1) &&
- (elem.id == q.id)
- );
- }
- );
- }
-
- if(q.hasLoops){
- // if we have other query param parts, make sure we add them to the
- // filter chain
- ff = agree(ff, getSimpleFilterFunc(q));
- }
-
- return _filtersCache[q.query] = ff;
- }
-
- var getNodeIndex = function(node){
- // NOTE:
- // we could have a more accurate caching mechanism by invalidating
- // caches after the query has finished, but I think that'd lead to
- // significantly more cache churn than the cache would provide
- // value for in the common case. Generally, we're more
- // conservative (and therefore, more accurate) than jQuery and
- // DomQuery WRT node node indexes, but there may be corner cases
- // in which we fall down. How much we care about them is TBD.
-
- var pn = node.parentNode;
- var pnc = pn.childNodes;
-
- // check to see if we can trust the cache. If not, re-key the whole
- // thing and return our node match from that.
-
- var nidx = -1;
- var child = pn.firstChild;
- if(!child){
- return nidx;
- }
-
- var ci = node["__cachedIndex"];
- var cl = pn["__cachedLength"];
-
- // only handle cache building if we've gone out of sync
- if(((typeof cl == "number")&&(cl != pnc.length))||(typeof ci != "number")){
- // rip though the whole set, building cache indexes as we go
- pn["__cachedLength"] = pnc.length;
- var idx = 1;
- do{
- // we only assign indexes for nodes with nodeType == 1, as per:
- // http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
- // only elements are counted in the search order, and they
- // begin at 1 for the first child's index
-
- if(child === node){
- nidx = idx;
- }
- if(child.nodeType == 1){
- child["__cachedIndex"] = idx;
- idx++;
- }
- child = child.nextSibling;
- }while(child);
- }else{
- // NOTE:
- // could be incorrect in some cases (node swaps involving the
- // passed node, etc.), but we ignore those due to the relative
- // unlikelihood of that occuring
- nidx = ci;
- }
- return nidx;
- }
-
- var firedCount = 0;
-
- var blank = "";
- var _getAttr = function(elem, attr){
- if(attr == "class"){
- return elem.className || blank;
- }
- if(attr == "for"){
- return elem.htmlFor || blank;
- }
- return elem.getAttribute(attr, 2) || blank;
- }
-
- var attrs = {
- "*=": function(attr, value){
- return function(elem){
- // E[foo*="bar"]
- // an E element whose "foo" attribute value contains
- // the substring "bar"
- return (_getAttr(elem, attr).indexOf(value)>=0);
- }
- },
- "^=": function(attr, value){
- // E[foo^="bar"]
- // an E element whose "foo" attribute value begins exactly
- // with the string "bar"
- return function(elem){
- return (_getAttr(elem, attr).indexOf(value)==0);
- }
- },
- "$=": function(attr, value){
- // E[foo$="bar"]
- // an E element whose "foo" attribute value ends exactly
- // with the string "bar"
- var tval = " "+value;
- return function(elem){
- var ea = " "+_getAttr(elem, attr);
- return (ea.lastIndexOf(value)==(ea.length-value.length));
- }
- },
- "~=": function(attr, value){
- // E[foo~="bar"]
- // an E element whose "foo" attribute value is a list of
- // space-separated values, one of which is exactly equal
- // to "bar"
-
- // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
- var tval = " "+value+" ";
- return function(elem){
- var ea = " "+_getAttr(elem, attr)+" ";
- return (ea.indexOf(tval)>=0);
- }
- },
- "|=": function(attr, value){
- // E[hreflang|="en"]
- // an E element whose "hreflang" attribute has a
- // hyphen-separated list of values beginning (from the
- // left) with "en"
- var valueDash = " "+value+"-";
- return function(elem){
- var ea = " "+(elem.getAttribute(attr, 2) || "");
- return (
- (ea == value) ||
- (ea.indexOf(valueDash)==0)
- );
- }
- },
- "=": function(attr, value){
- return function(elem){
- return (_getAttr(elem, attr) == value);
- }
- }
- };
-
- var pseudos = {
- "first-child": function(name, condition){
- return function(elem){
- if(elem.nodeType != 1){ return false; }
- // check to see if any of the previous siblings are elements
- var fc = elem.previousSibling;
- while(fc && (fc.nodeType != 1)){
- fc = fc.previousSibling;
- }
- return (!fc);
- }
- },
- "last-child": function(name, condition){
- return function(elem){
- if(elem.nodeType != 1){ return false; }
- // check to see if any of the next siblings are elements
- var nc = elem.nextSibling;
- while(nc && (nc.nodeType != 1)){
- nc = nc.nextSibling;
- }
- return (!nc);
- }
- },
- "empty": function(name, condition){
- return function(elem){
- // DomQuery and jQuery get this wrong, oddly enough.
- // The CSS 3 selectors spec is pretty explicit about
- // it, too.
- var cn = elem.childNodes;
- var cnl = elem.childNodes.length;
- // if(!cnl){ return true; }
- for(var x=cnl-1; x >= 0; x--){
- var nt = cn[x].nodeType;
- if((nt == 1)||(nt == 3)){ return false; }
- }
- return true;
- }
- },
- "contains": function(name, condition){
- return function(elem){
- // FIXME: I dislike this version of "contains", as
- // whimsical attribute could set it off. An inner-text
- // based version might be more accurate, but since
- // jQuery and DomQuery also potentially get this wrong,
- // I'm leaving it for now.
- return (elem.innerHTML.indexOf(condition) >= 0);
- }
- },
- "not": function(name, condition){
- var ntf = getFilterFunc(getQueryParts(condition)[0]);
- return function(elem){
- return (!ntf(elem));
- }
- },
- "nth-child": function(name, condition){
- var pi = parseInt;
- if(condition == "odd"){
- return function(elem){
- return (
- ((getNodeIndex(elem)) % 2) == 1
- );
- }
- }else if((condition == "2n")||
- (condition == "even")){
- return function(elem){
- return ((getNodeIndex(elem) % 2) == 0);
- }
- }else if(condition.indexOf("0n+") == 0){
- var ncount = pi(condition.substr(3));
- return function(elem){
- return (elem.parentNode[childNodesName][ncount-1] === elem);
- }
- }else if( (condition.indexOf("n+") > 0) &&
- (condition.length > 3) ){
- var tparts = condition.split("n+", 2);
- var pred = pi(tparts[0]);
- var idx = pi(tparts[1]);
- return function(elem){
- return ((getNodeIndex(elem) % pred) == idx);
- }
- }else if(condition.indexOf("n") == -1){
- var ncount = pi(condition);
- return function(elem){
- return (getNodeIndex(elem) == ncount);
- }
- }
- }
- };
-
- var defaultGetter = (d.isIE) ? function(cond){
- var clc = cond.toLowerCase();
- return function(elem){
- return elem[cond]||elem[clc];
- }
- } : function(cond){
- return function(elem){
- return (elem && elem.getAttribute && elem.hasAttribute(cond));
- }
- };
-
- var getSimpleFilterFunc = function(query){
-
- var fcHit = (_simpleFiltersCache[query.query]||_filtersCache[query.query]);
- if(fcHit){ return fcHit; }
-
- var ff = null;
-
- // the only case where we'll need the tag name is if we came from an ID query
- if(query.id){ // do we have an ID component?
- if(query.tag != "*"){
- ff = agree(ff, function(elem){
- return (elem.tagName.toLowerCase() == query.tag);
- });
- }
- }
-
- // if there's a class in our query, generate a match function for it
- d.forEach(query.classes, function(cname, idx, arr){
- // get the class name
- var isWildcard = cname.charAt(cname.length-1) == "*";
- if(isWildcard){
- cname = cname.substr(0, cname.length-1);
- }
- // I dislike the regex thing, even if memozied in a cache, but it's VERY short
- var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
- ff = agree(ff, function(elem){
- return re.test(elem.className);
- });
- ff.count = idx;
- });
-
- d.forEach(query.pseudos, function(pseudo){
- if(pseudos[pseudo.name]){
- ff = agree(ff, pseudos[pseudo.name](pseudo.name, pseudo.value));
- }
- });
-
- handleAttrs(attrs, query, defaultGetter,
- function(tmatcher){ ff = agree(ff, tmatcher); }
- );
- if(!ff){
- ff = function(){ return true; };
- }
- return _simpleFiltersCache[query.query] = ff;
- }
-
- var _getElementsFuncCache = { };
-
- var getElementsFunc = function(query, root){
- var fHit = _getElementsFuncCache[query.query];
- if(fHit){ return fHit; }
-
- // NOTE: this function is in the fast path! not memoized!!!
-
- // the query doesn't contain any spaces, so there's only so many
- // things it could be
-
- if(query.id && !query.hasLoops && !query.tag){
- // ID-only query. Easy.
- return _getElementsFuncCache[query.query] = function(root){
- // FIXME: if root != document, check for parenting!
- return [ d.byId(query.id) ];
- }
- }
-
- var filterFunc = getSimpleFilterFunc(query);
-
- var retFunc;
- if(query.tag && query.id && !query.hasLoops){
- // we got a filtered ID search (e.g., "h4#thinger")
- retFunc = function(root){
- var te = d.byId(query.id);
- if(filterFunc(te)){
- return [ te ];
- }
- }
- }else{
- var tret;
-
- if(!query.hasLoops){
- // it's just a plain-ol elements-by-tag-name query from the root
- retFunc = function(root){
- var ret = [];
- var te, x=0, tret = root.getElementsByTagName(query.tag);
- while(te=tret[x++]){
- ret.push(te);
- }
- return ret;
- }
- }else{
- retFunc = function(root){
- var ret = [];
- var te, x=0, tret = root.getElementsByTagName(query.tag);
- while(te=tret[x++]){
- if(filterFunc(te)){
- ret.push(te);
- }
- }
- return ret;
- }
- }
- }
- return _getElementsFuncCache[query.query] = retFunc;
- }
-
- var _partsCache = {};
-
- ////////////////////////////////////////////////////////////////////////
- // the query runner
- ////////////////////////////////////////////////////////////////////////
-
- // this is the second level of spliting, from full-length queries (e.g.,
- // "div.foo .bar") into simple query expressions (e.g., ["div.foo",
- // ".bar"])
- var _queryFuncCache = {
- "*": d.isIE ?
- function(root){
- return root.all;
- } :
- function(root){
- return root.getElementsByTagName("*");
- },
- "~": _nextSiblings,
- "+": function(root){ return _nextSiblings(root, true); },
- ">": _childElements
- };
-
- var getStepQueryFunc = function(query){
- // if it's trivial, get a fast-path dispatcher
- var qparts = getQueryParts(d.trim(query));
- // if(query[query.length-1] == ">"){ query += " *"; }
- if(qparts.length == 1){
- var tt = getElementsFunc(qparts[0]);
- tt.nozip = true;
- return tt;
- }
-
- // otherwise, break it up and return a runner that iterates over the parts recursively
- var sqf = function(root){
- var localQueryParts = qparts.slice(0); // clone the src arr
- var candidates;
- if(localQueryParts[0].oper == ">"){ // FIXME: what if it's + or ~?
- candidates = [ root ];
- // root = document;
- }else{
- candidates = getElementsFunc(localQueryParts.shift())(root);
- }
- return filterDown(candidates, localQueryParts);
- }
- return sqf;
- }
-
- // a specialized method that implements our primoridal "query optimizer".
- // This allows us to dispatch queries to the fastest subsystem we can get.
- var _getQueryFunc = (
- // NOTE:
- // XPath on the Webkit nighlies is slower than it's DOM iteration
- // for most test cases
- // FIXME:
- // we should try to capture some runtime speed data for each query
- // function to determine on the fly if we should stick w/ the
- // potentially optimized variant or if we should try something
- // new.
- (document["evaluate"] && !d.isSafari) ?
- function(query){
- // has xpath support that's faster than DOM
- var qparts = query.split(" ");
- // can we handle it?
- if( (document["evaluate"])&&
- (query.indexOf(":") == -1)&&
- (query.indexOf("+") == -1) // skip direct sibling matches. See line ~344
- ){
- // dojo.debug(query);
- // should we handle it?
-
- // kind of a lame heuristic, but it works
- if(
- // a "div div div" style query
- ((qparts.length > 2)&&(query.indexOf(">") == -1))||
- // or something else with moderate complexity. kinda janky
- (qparts.length > 3)||
- (query.indexOf("[")>=0)||
- // or if it's a ".thinger" query
- ((1 == qparts.length)&&(0 <= query.indexOf(".")))
-
- ){
- // use get and cache a xpath runner for this selector
- return getXPathFunc(query);
- }
- }
-
- // fallthrough
- return getStepQueryFunc(query);
- } : getStepQueryFunc
- );
- // uncomment to disable XPath for testing and tuning the DOM path
- // _getQueryFunc = getStepQueryFunc;
-
- // FIXME: we've got problems w/ the NodeList query()/filter() functions if we go XPath for everything
-
- // uncomment to disable DOM queries for testing and tuning XPath
- // _getQueryFunc = getXPathFunc;
-
- // this is the primary caching for full-query results. The query dispatcher
- // functions are generated here and then pickled for hash lookup in the
- // future
- var getQueryFunc = function(query){
- // return a cached version if one is available
- var qcz = query.charAt(0);
- if(d.doc["querySelectorAll"] &&
- ( (!d.isSafari) || (d.isSafari > 3.1) ) && // see #5832
- // as per CSS 3, we can't currently start w/ combinator:
- // http://www.w3.org/TR/css3-selectors/#w3cselgrammar
- (">+~".indexOf(qcz) == -1)
- ){
- return function(root){
- var r = root.querySelectorAll(query);
- r.nozip = true; // skip expensive duplication checks and just wrap in a NodeList
- return r;
- };
- }
- if(_queryFuncCache[query]){ return _queryFuncCache[query]; }
- if(0 > query.indexOf(",")){
- // if it's not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
- return _queryFuncCache[query] = _getQueryFunc(query);
- }else{
- // if it's a complex query, break it up into it's constituent parts
- // and return a dispatcher that will merge the parts when run
-
- // var parts = query.split(", ");
- var parts = query.split(/\s*,\s*/);
- var tf = function(root){
- var pindex = 0; // avoid array alloc for every invocation
- var ret = [];
- var tp;
- while(tp = parts[pindex++]){
- ret = ret.concat(_getQueryFunc(tp, tp.indexOf(" "))(root));
- }
- return ret;
- }
- // ...cache and return
- return _queryFuncCache[query] = tf;
- }
- }
-
- // FIXME:
- // Dean's Base2 uses a system whereby queries themselves note if
- // they'll need duplicate filtering. We need to get on that plan!!
-
- // attempt to efficiently determine if an item in a list is a dupe,
- // returning a list of "uniques", hopefully in doucment order
- var _zipIdx = 0;
- var _zip = function(arr){
- if(arr && arr.nozip){ return d.NodeList._wrap(arr); }
- var ret = new d.NodeList();
- if(!arr){ return ret; }
- if(arr[0]){
- ret.push(arr[0]);
- }
- if(arr.length < 2){ return ret; }
- _zipIdx++;
- arr[0]["_zipIdx"] = _zipIdx;
- for(var x=1, te; te = arr[x]; x++){
- if(arr[x]["_zipIdx"] != _zipIdx){
- ret.push(te);
- }
- te["_zipIdx"] = _zipIdx;
- }
- // FIXME: should we consider stripping these properties?
- return ret;
- }
-
- // the main executor
- d.query = function(/*String*/ query, /*String|DOMNode?*/ root){
- // summary:
- // Returns nodes which match the given CSS3 selector, searching the
- // entire document by default but optionally taking a node to scope
- // the search by. Returns an instance of dojo.NodeList.
- // description:
- // dojo.query() is the swiss army knife of DOM node manipulation in
- // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
- // "$" function, dojo.query provides robust, high-performance
- // CSS-based node selector support with the option of scoping searches
- // to a particular sub-tree of a document.
- //
- // Supported Selectors:
- // --------------------
- //
- // dojo.query() supports a rich set of CSS3 selectors, including:
- //
- // * class selectors (e.g., `.foo`)
- // * node type selectors like `span`
- // * ` ` descendant selectors
- // * `>` child element selectors
- // * `#foo` style ID selectors
- // * `*` universal selector
- // * `~`, the immediately preceeded-by sibling selector
- // * `+`, the preceeded-by sibling selector
- // * attribute queries:
- // | * `[foo]` attribute presence selector
- // | * `[foo='bar']` attribute value exact match
- // | * `[foo~='bar']` attribute value list item match
- // | * `[foo^='bar']` attribute start match
- // | * `[foo$='bar']` attribute end match
- // | * `[foo*='bar']` attribute substring match
- // * `:first-child`, `:last-child` positional selectors
- // * `:empty` content emtpy selector
- // * `:empty` content emtpy selector
- // * `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
- // * `:nth-child(even)`, `:nth-child(odd)` positional selectors
- // * `:not(...)` negation pseudo selectors
- //
- // Any legal combination of these selectors will work with
- // `dojo.query()`, including compound selectors ("," delimited).
- // Very complex and useful searches can be constructed with this
- // palette of selectors and when combined with functions for
- // maniplation presented by dojo.NodeList, many types of DOM
- // manipulation operations become very straightforward.
- //
- // Unsupported Selectors:
- // ----------------------
- //
- // While dojo.query handles many CSS3 selectors, some fall outside of
- // what's resaonable for a programmatic node querying engine to
- // handle. Currently unsupported selectors include:
- //
- // * namespace-differentiated selectors of any form
- // * all `::` pseduo-element selectors
- // * certain pseduo-selectors which don't get a lot of day-to-day use:
- // | * `:root`, `:lang()`, `:target`, `:focus`
- // * all visual and state selectors:
- // | * `:root`, `:active`, `:hover`, `:visisted`, `:link`,
- // `:enabled`, `:disabled`, `:checked`
- // * `:*-of-type` pseudo selectors
- //
- // dojo.query and XML Documents:
- // -----------------------------
- //
- // `dojo.query` currently only supports searching XML documents
- // whose tags and attributes are 100% lower-case. This is a known
- // limitation and will [be addressed soon](http://trac.dojotoolkit.org/ticket/3866)
- // Non-selector Queries:
- // ---------------------
- //
- // If something other than a String is passed for the query,
- // `dojo.query` will return a new `dojo.NodeList` constructed from
- // that parameter alone and all further processing will stop. This
- // means that if you have a reference to a node or NodeList, you
- // can quickly construct a new NodeList from the original by
- // calling `dojo.query(node)` or `dojo.query(list)`.
- //
- // query:
- // The CSS3 expression to match against. For details on the syntax of
- // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
- // root:
- // A DOMNode (or node id) to scope the search from. Optional.
- // returns: dojo.NodeList
- // An instance of `dojo.NodeList`. Many methods are available on
- // NodeLists for searching, iterating, manipulating, and handling
- // events on the matched nodes in the returned list.
- // example:
- // search the entire document for elements with the class "foo":
- // | dojo.query(".foo");
- // these elements will match:
- // | <span class="foo"></span>
- // | <span class="foo bar"></span>
- // | <p class="thud foo"></p>
- // example:
- // search the entire document for elements with the classes "foo" *and* "bar":
- // | dojo.query(".foo.bar");
- // these elements will match:
- // | <span class="foo bar"></span>
- // while these will not:
- // | <span class="foo"></span>
- // | <p class="thud foo"></p>
- // example:
- // find `<span>` elements which are descendants of paragraphs and
- // which have a "highlighted" class:
- // | dojo.query("p span.highlighted");
- // the innermost span in this fragment matches:
- // | <p class="foo">
- // | <span>...
- // | <span class="highlighted foo bar">...</span>
- // | </span>
- // | </p>
- // example:
- // set an "odd" class on all odd table rows inside of the table
- // `#tabular_data`, using the `>` (direct child) selector to avoid
- // affecting any nested tables:
- // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
- // example:
- // remove all elements with the class "error" from the document
- // and store them in a list:
- // | var errors = dojo.query(".error").orphan();
- // example:
- // add an onclick handler to every submit button in the document
- // which causes the form to be sent via Ajax instead:
- // | dojo.query("input[type='submit']").onclick(function(e){
- // | dojo.stopEvent(e); // prevent sending the form
- // | var btn = e.target;
- // | dojo.xhrPost({
- // | form: btn.form,
- // | load: function(data){
- // | // replace the form with the response
- // | var div = dojo.doc.createElement("div");
- // | dojo.place(div, btn.form, "after");
- // | div.innerHTML = data;
- // | dojo.style(btn.form, "display", "none");
- // | }
- // | });
- // | });
-
-
- // NOTE: elementsById is not currently supported
- // NOTE: ignores xpath-ish queries for now
-
- if(query.constructor == d.NodeList){
- return query;
- }
- if(!d.isString(query)){
- return new d.NodeList(query); // dojo.NodeList
- }
- if(d.isString(root)){
- root = d.byId(root);
- }
-
- return _zip(getQueryFunc(query)(root||d.doc)); // dojo.NodeList
- }
-
- /*
- // exposing this was a mistake
- d.query.attrs = attrs;
- */
- // exposing this because new pseudo matches are only executed through the
- // DOM query path (never through the xpath optimizing branch)
- d.query.pseudos = pseudos;
-
- // one-off function for filtering a NodeList based on a simple selector
- d._filterQueryResult = function(nodeList, simpleFilter){
- var tnl = new d.NodeList();
- var ff = (simpleFilter) ? getFilterFunc(getQueryParts(simpleFilter)[0]) : function(){ return true; };
- for(var x=0, te; te = nodeList[x]; x++){
- if(ff(te)){ tnl.push(te); }
- }
- return tnl;
- }
-})();
-
-}