📄 query.js
字号:
// 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!!!
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -