/* * Copyright (C) 2007-2015 Diego Perini * All rights reserved. * * nwmatcher.js - A fast CSS selector engine and matcher * * Author: Diego Perini * Version: 1.3.6 * Created: 20070722 * Release: 20150710 * * License: * http://javascript.nwbox.com/NWMatcher/MIT-LICENSE * Download: * http://javascript.nwbox.com/NWMatcher/nwmatcher.js */ (function(global, factory) { if (typeof module == 'object' && typeof exports == 'object') { // in a Node.js environment, the nwmatcher functions will operate on // the passed "browserGlobal" and will be returned in an object module.exports = function (browserGlobal) { // passed global does not contain // references to native objects browserGlobal.console = console; browserGlobal.parseInt = parseInt; browserGlobal.Function = Function; browserGlobal.Boolean = Boolean; browserGlobal.Number = Number; browserGlobal.RegExp = RegExp; browserGlobal.String = String; browserGlobal.Object = Object; browserGlobal.Array = Array; browserGlobal.Error = Error; browserGlobal.Date = Date; browserGlobal.Math = Math; var exports = browserGlobal.Object(); factory(browserGlobal, exports); return exports; }; module.factory = factory; } else { // in a browser environment, the nwmatcher functions will operate on // the "global" loading them and be attached to "global.NW.Dom" factory(global, (global.NW || (global.NW = global.Object())) && (global.NW.Dom || (global.NW.Dom = global.Object()))); global.NW.Dom.factory = factory; } })(this, function(global, exports) { var version = 'nwmatcher-1.3.6', Dom = exports, // processing context & root element doc = global.document, root = doc.documentElement, // save utility methods references slice = global.Array.prototype.slice, string = global.Object.prototype.toString, // persist previous parsed data isSingleMatch, isSingleSelect, lastSlice, lastContext, lastPosition, lastMatcher, lastSelector, lastPartsMatch, lastPartsSelect, // accepted prefix identifiers // (id, class & pseudo-class) prefixes = '[#.:]?', // accepted attribute operators operators = '([~*^$|!]?={1})', // accepted whitespace characters whitespace = '[\\x20\\t\\n\\r\\f]*', // 4 combinators F E, F>E, F+E, F~E combinators = '[\\x20]|[>+~][^>+~]', // an+b format params for pseudo-classes pseudoparms = '(?:[-+]?\\d*n)?[-+]?\\d*', // CSS quoted string values quotedvalue = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"' + "|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'", // skip round brackets groups skipround = '\\([^()]+\\)|\\(.*\\)', // skip curly brackets groups skipcurly = '\\{[^{}]+\\}|\\{.*\\}', // skip square brackets groups skipsquare = '\\[[^[\\]]*\\]|\\[.*\\]', // skip [ ], ( ), { } brackets groups skipgroup = '\\[.*\\]|\\(.*\\)|\\{.*\\}', // http://www.w3.org/TR/css3-syntax/#characters // unicode/ISO 10646 characters 161 and higher // NOTE: Safari 2.0.x crashes with escaped (\\) // Unicode ranges in regular expressions so we // use a negated character range class instead encoding = '(?:[-\\w]|[^\\x00-\\xa0]|\\\\.)', // CSS identifier syntax identifier = '(?:-?[_a-zA-Z]{1}[-\\w]*|[^\\x00-\\xa0]+|\\\\.+)+', // build attribute string attrcheck = '(' + quotedvalue + '|' + identifier + ')', attributes = whitespace + '(' + encoding + '*:?' + encoding + '+)' + whitespace + '(?:' + operators + whitespace + attrcheck + ')?' + whitespace, attrmatcher = attributes.replace(attrcheck, '([\\x22\\x27]*)((?:\\\\?.)*?)\\3'), // build pseudoclass string pseudoclass = '((?:' + // an+b parameters or quoted string pseudoparms + '|' + quotedvalue + '|' + // id, class, pseudo-class selector prefixes + '|' + encoding + '+|' + // nested HTML attribute selector '\\[' + attributes + '\\]|' + // nested pseudo-class selector '\\(.+\\)|' + whitespace + '|' + // nested pseudos/separators ',)+)', // placeholder for extensions extensions = '.+', // CSS3: syntax scanner and // one pass validation only // using regular expression standardValidator = // discard start '(?=[\\x20\\t\\n\\r\\f]*[^>+~(){}<>])' + // open match group '(' + //universal selector '\\*' + // id/class/tag/pseudo-class identifier '|(?:' + prefixes + identifier + ')' + // combinator selector '|' + combinators + // HTML attribute selector '|\\[' + attributes + '\\]' + // pseudo-classes parameters '|\\(' + pseudoclass + '\\)' + // dom properties selector (extension) '|\\{' + extensions + '\\}' + // selector group separator (comma) '|(?:,|' + whitespace + ')' + // close match group ')+', // validator for complex selectors in ':not()' pseudo-classes extendedValidator = standardValidator.replace(pseudoclass, '.*'), // validator for standard selectors as default reValidator = new global.RegExp(standardValidator, 'g'), // whitespace is any combination of these 5 character [\x20\t\n\r\f] // http://www.w3.org/TR/css3-selectors/#selector-syntax reTrimSpaces = new global.RegExp('^' + whitespace + '|' + whitespace + '$', 'g'), // only allow simple selectors nested in ':not()' pseudo-classes reSimpleNot = new global.RegExp('^(' + '(?!:not)' + '(' + prefixes + '|' + identifier + '|\\([^()]*\\))+' + '|\\[' + attributes + '\\]' + ')$'), // split comma groups, exclude commas from // quotes '' "" and from brackets () [] {} reSplitGroup = new global.RegExp('(' + '[^,\\\\()[\\]]+' + '|' + skipsquare + '|' + skipround + '|' + skipcurly + '|\\\\.' + ')+', 'g'), // split last, right most, selector group token reSplitToken = new global.RegExp('(' + '\\[' + attributes + '\\]|' + '\\(' + pseudoclass + '\\)|' + '\\\\.|[^\\x20\\t\\r\\n\\f>+~])+', 'g'), // for in excess whitespace removal reWhiteSpace = /[\x20\t\n\r\f]+/g, reOptimizeSelector = new global.RegExp(identifier + '|^$'), /*----------------------------- FEATURE TESTING ----------------------------*/ // detect native methods isNative = (function() { var re = / \w+\(/, isnative = String(Object.prototype.toString).replace(re, ' ('); return function(method) { return method && typeof method != 'string' && isnative == String(method).replace(re, ' ('); }; })(), // NATIVE_XXXXX true if method exist and is callable // detect if DOM methods are native in browsers NATIVE_FOCUS = isNative(doc.hasFocus), NATIVE_QSAPI = isNative(doc.querySelector), NATIVE_GEBID = isNative(doc.getElementById), NATIVE_GEBTN = isNative(root.getElementsByTagName), NATIVE_GEBCN = isNative(root.getElementsByClassName), // detect native getAttribute/hasAttribute methods, // frameworks extend these to elements, but it seems // this does not work for XML namespaced attributes, // used to check both getAttribute/hasAttribute in IE NATIVE_GET_ATTRIBUTE = isNative(root.getAttribute), NATIVE_HAS_ATTRIBUTE = isNative(root.hasAttribute), // check if slice() can convert nodelist to array // see http://yura.thinkweb2.com/cft/ NATIVE_SLICE_PROTO = (function() { var isBuggy = false; try { isBuggy = !!slice.call(doc.childNodes, 0)[0]; } catch(e) { } return isBuggy; })(), // supports the new traversal API NATIVE_TRAVERSAL_API = 'nextElementSibling' in root && 'previousElementSibling' in root, // BUGGY_XXXXX true if method is feature tested and has known bugs // detect buggy gEBID BUGGY_GEBID = NATIVE_GEBID ? (function() { var isBuggy = true, x = 'x' + global.String(+new global.Date), a = doc.createElementNS ? 'a' : ''; (a = doc.createElement(a)).name = x; root.insertBefore(a, root.firstChild); isBuggy = !!doc.getElementById(x); root.removeChild(a); return isBuggy; })() : true, // detect IE gEBTN comment nodes bug BUGGY_GEBTN = NATIVE_GEBTN ? (function() { var div = doc.createElement('div'); div.appendChild(doc.createComment('')); return !!div.getElementsByTagName('*')[0]; })() : true, // detect Opera gEBCN second class and/or UTF8 bugs as well as Safari 3.2 // caching class name results and not detecting when changed, // tests are based on the jQuery selector test suite BUGGY_GEBCN = NATIVE_GEBCN ? (function() { var isBuggy, div = doc.createElement('div'), test = '\u53f0\u5317'; // Opera tests div.appendChild(doc.createElement('span')). setAttribute('class', test + 'abc ' + test); div.appendChild(doc.createElement('span')). setAttribute('class', 'x'); isBuggy = !div.getElementsByClassName(test)[0]; // Safari test div.lastChild.className = test; return isBuggy || div.getElementsByClassName(test).length != 2; })() : true, // detect IE bug with dynamic attributes BUGGY_GET_ATTRIBUTE = NATIVE_GET_ATTRIBUTE ? (function() { var input = doc.createElement('input'); input.setAttribute('value', 5); return input.defaultValue != 5; })() : true, // detect IE bug with non-standard boolean attributes BUGGY_HAS_ATTRIBUTE = NATIVE_HAS_ATTRIBUTE ? (function() { var option = doc.createElement('option'); option.setAttribute('selected', 'selected'); return !option.hasAttribute('selected'); })() : true, // detect Safari bug with selected option elements BUGGY_SELECTED = (function() { var select = doc.createElement('select'); select.appendChild(doc.createElement('option')); return !select.firstChild.selected; })(), // initialized with the loading context // and reset for each different context BUGGY_QUIRKS_GEBCN, BUGGY_QUIRKS_QSAPI, QUIRKS_MODE, XML_DOCUMENT, // detect Opera browser OPERA = /opera/i.test(string.call(global.opera)), // skip simple selector optimizations for Opera >= 11 OPERA_QSAPI = OPERA && global.parseFloat(global.opera.version()) >= 11, // check Selector API implementations RE_BUGGY_QSAPI = NATIVE_QSAPI ? (function() { var pattern = new global.Array(), context, element, expect = function(selector, element, n) { var result = false; context.appendChild(element); try { result = context.querySelectorAll(selector).length == n; } catch(e) { } while (context.firstChild) { context.removeChild(context.firstChild); } return result; }; // certain bugs can only be detected in standard documents // to avoid writing a live loading document create a fake one if (doc.implementation && doc.implementation.createDocument) { // use a shadow document body as context context = doc.implementation.createDocument('', '', null). appendChild(doc.createElement('html')). appendChild(doc.createElement('head')).parentNode. appendChild(doc.createElement('body')); } else { // use an unattached div node as context context = doc.createElement('div'); } // fix for Safari 8.x and other engines that // fail querying filtered sibling combinators element = doc.createElement('div'); element.innerHTML = '


'; expect('p#a+*', element, 0) && pattern.push('\\w+#\\w+.*[+~]'); // ^= $= *= operators bugs with empty values (Opera 10 / IE8) element = doc.createElement('p'); element.setAttribute('class', ''); expect('[class^=""]', element, 1) && pattern.push('[*^$]=[\\x20\\t\\n\\r\\f]*(?:""|' + "'')"); // :checked bug with option elements (Firefox 3.6.x) // it wrongly includes 'selected' options elements // HTML5 rules says selected options also match element = doc.createElement('option'); element.setAttribute('selected', 'selected'); expect(':checked', element, 0) && pattern.push(':checked'); // :enabled :disabled bugs with hidden fields (Firefox 3.5) // http://www.w3.org/TR/html5/links.html#selector-enabled // http://www.w3.org/TR/css3-selectors/#enableddisabled // not supported by IE8 Query Selector element = doc.createElement('input'); element.setAttribute('type', 'hidden'); expect(':enabled', element, 0) && pattern.push(':enabled', ':disabled'); // :link bugs with hyperlinks matching (Firefox/Safari) element = doc.createElement('link'); element.setAttribute('href', 'x'); expect(':link', element, 1) || pattern.push(':link'); // avoid attribute selectors for IE QSA if (BUGGY_HAS_ATTRIBUTE) { // IE fails in reading: // - original values for input/textarea // - original boolean values for controls pattern.push('\\[[\\x20\\t\\n\\r\\f]*(?:checked|disabled|ismap|multiple|readonly|selected|value)'); } return pattern.length ? new global.RegExp(pattern.join('|')) : { 'test': function() { return false; } }; })() : true, // matches class selectors RE_CLASS = new global.RegExp('(?:\\[[\\x20\\t\\n\\r\\f]*class\\b|\\.' + identifier + ')'), // matches simple id, tag & class selectors RE_SIMPLE_SELECTOR = new global.RegExp( BUGGY_GEBTN && BUGGY_GEBCN || OPERA ? '^#?-?[_a-zA-Z]{1}' + encoding + '*$' : BUGGY_GEBTN ? '^[.#]?-?[_a-zA-Z]{1}' + encoding + '*$' : BUGGY_GEBCN ? '^(?:\\*|#-?[_a-zA-Z]{1}' + encoding + '*)$' : '^(?:\\*|[.#]?-?[_a-zA-Z]{1}' + encoding + '*)$'), /*----------------------------- LOOKUP OBJECTS -----------------------------*/ LINK_NODES = new global.Object({ 'a': 1, 'A': 1, 'area': 1, 'AREA': 1, 'link': 1, 'LINK': 1 }), // boolean attributes should return attribute name instead of true/false ATTR_BOOLEAN = new global.Object({ 'checked': 1, 'disabled': 1, 'ismap': 1, 'multiple': 1, 'readonly': 1, 'selected': 1 }), // dynamic attributes that needs to be checked against original HTML value ATTR_DEFAULT = new global.Object({ 'value': 'defaultValue', 'checked': 'defaultChecked', 'selected': 'defaultSelected' }), // attributes referencing URI data values need special treatment in IE ATTR_URIDATA = new global.Object({ 'action': 2, 'cite': 2, 'codebase': 2, 'data': 2, 'href': 2, 'longdesc': 2, 'lowsrc': 2, 'src': 2, 'usemap': 2 }), // HTML 5 draft specifications // http://www.whatwg.org/specs/web-apps/current-work/#selectors HTML_TABLE = new global.Object({ // class attribute must be treated case-insensitive in HTML quirks mode // initialized by default to Standard Mode (case-sensitive), // set dynamically by the attribute resolver 'class': 0, 'accept': 1, 'accept-charset': 1, 'align': 1, 'alink': 1, 'axis': 1, 'bgcolor': 1, 'charset': 1, 'checked': 1, 'clear': 1, 'codetype': 1, 'color': 1, 'compact': 1, 'declare': 1, 'defer': 1, 'dir': 1, 'direction': 1, 'disabled': 1, 'enctype': 1, 'face': 1, 'frame': 1, 'hreflang': 1, 'http-equiv': 1, 'lang': 1, 'language': 1, 'link': 1, 'media': 1, 'method': 1, 'multiple': 1, 'nohref': 1, 'noresize': 1, 'noshade': 1, 'nowrap': 1, 'readonly': 1, 'rel': 1, 'rev': 1, 'rules': 1, 'scope': 1, 'scrolling': 1, 'selected': 1, 'shape': 1, 'target': 1, 'text': 1, 'type': 1, 'valign': 1, 'valuetype': 1, 'vlink': 1 }), // the following attributes must be treated case-insensitive in XHTML mode // Niels Leenheer http://rakaz.nl/item/css_selector_bugs_case_sensitivity XHTML_TABLE = new global.Object({ 'accept': 1, 'accept-charset': 1, 'alink': 1, 'axis': 1, 'bgcolor': 1, 'charset': 1, 'codetype': 1, 'color': 1, 'enctype': 1, 'face': 1, 'hreflang': 1, 'http-equiv': 1, 'lang': 1, 'language': 1, 'link': 1, 'media': 1, 'rel': 1, 'rev': 1, 'target': 1, 'text': 1, 'type': 1, 'vlink': 1 }), /*-------------------------- REGULAR EXPRESSIONS ---------------------------*/ // placeholder to add functionalities Selectors = new global.Object({ // as a simple example this will check // for chars not in standard ascii table // // 'mySpecialSelector': { // 'Expression': /\u0080-\uffff/, // 'Callback': mySelectorCallback // } // // 'mySelectorCallback' will be invoked // only after passing all other standard // checks and only if none of them worked }), // attribute operators Operators = new global.Object({ '=': "n=='%m'", '^=': "n.indexOf('%m')==0", '*=': "n.indexOf('%m')>-1", '|=': "(n+'-').indexOf('%m-')==0", '~=': "(' '+n+' ').indexOf(' %m ')>-1", '$=': "n.substr(n.length-'%m'.length)=='%m'" }), // optimization expressions Optimize = new global.Object({ ID: new global.RegExp('^\\*?#(' + encoding + '+)|' + skipgroup), TAG: new global.RegExp('^(' + encoding + '+)|' + skipgroup), CLASS: new global.RegExp('^\\*?\\.(' + encoding + '+$)|' + skipgroup) }), // precompiled Regular Expressions Patterns = new global.Object({ // structural pseudo-classes and child selectors spseudos: /^\:(root|empty|(?:first|last|only)(?:-child|-of-type)|nth(?:-last)?(?:-child|-of-type)\(\s*(even|odd|(?:[-+]{0,1}\d*n\s*)?[-+]{0,1}\s*\d*)\s*\))?(.*)/i, // uistates + dynamic + negation pseudo-classes dpseudos: /^\:(link|visited|target|active|focus|hover|checked|disabled|enabled|selected|lang\(([-\w]{2,})\)|not\(([^()]*|.*)\))?(.*)/i, // element attribute matcher attribute: new global.RegExp('^\\[' + attrmatcher + '\\](.*)'), // E > F children: /^[\x20\t\n\r\f]*\>[\x20\t\n\r\f]*(.*)/, // E + F adjacent: /^[\x20\t\n\r\f]*\+[\x20\t\n\r\f]*(.*)/, // E ~ F relative: /^[\x20\t\n\r\f]*\~[\x20\t\n\r\f]*(.*)/, // E F ancestor: /^[\x20\t\n\r\f]+(.*)/, // all universal: /^\*(.*)/, // id id: new global.RegExp('^#(' + encoding + '+)(.*)'), // tag tagName: new global.RegExp('^(' + encoding + '+)(.*)'), // class className: new global.RegExp('^\\.(' + encoding + '+)(.*)') }), /*------------------------------ UTIL METHODS ------------------------------*/ // concat elements to data concatList = function(data, elements) { var i = -1, element; if (!data.length && global.Array.slice) return global.Array.slice(elements); while ((element = elements[++i])) data[data.length] = element; return data; }, // concat elements to data and callback concatCall = function(data, elements, callback) { var i = -1, element; while ((element = elements[++i])) { if (false === callback(data[data.length] = element)) { break; } } return data; }, // change context specific variables switchContext = function(from, force) { var div, oldDoc = doc; // save passed context lastContext = from; // set new context document doc = from.ownerDocument || from; if (force || oldDoc !== doc) { // set document root root = doc.documentElement; // set host environment flags XML_DOCUMENT = doc.createElement('DiV').nodeName == 'DiV'; // In quirks mode css class names are case insensitive. // In standards mode they are case sensitive. See docs: // https://developer.mozilla.org/en/Mozilla_Quirks_Mode_Behavior // http://www.whatwg.org/specs/web-apps/current-work/#selectors QUIRKS_MODE = !XML_DOCUMENT && typeof doc.compatMode == 'string' ? doc.compatMode.indexOf('CSS') < 0 : (function() { var style = doc.createElement('div').style; return style && (style.width = 1) && style.width == '1px'; })(); div = doc.createElement('div'); div.appendChild(doc.createElement('p')).setAttribute('class', 'xXx'); div.appendChild(doc.createElement('p')).setAttribute('class', 'xxx'); // GEBCN buggy in quirks mode, match count is: // Firefox 3.0+ [xxx = 1, xXx = 1] // Opera 10.63+ [xxx = 0, xXx = 2] BUGGY_QUIRKS_GEBCN = !XML_DOCUMENT && NATIVE_GEBCN && QUIRKS_MODE && (div.getElementsByClassName('xxx').length != 2 || div.getElementsByClassName('xXx').length != 2); // QSAPI buggy in quirks mode, match count is: // At least Chrome 4+, Firefox 3.5+, Opera 10.x+, Safari 4+ [xxx = 1, xXx = 2] // Safari 3.2 QSA doesn't work with mixedcase in quirksmode [xxx = 1, xXx = 0] // https://bugs.webkit.org/show_bug.cgi?id=19047 // must test the attribute selector '[class~=xxx]' // before '.xXx' or the bug may not present itself BUGGY_QUIRKS_QSAPI = !XML_DOCUMENT && NATIVE_QSAPI && QUIRKS_MODE && (div.querySelectorAll('[class~=xxx]').length != 2 || div.querySelectorAll('.xXx').length != 2); Config.CACHING && Dom.setCache(true, doc); } }, // convert a CSS string or identifier containing escape sequence to a // javascript string with javascript escape sequences convertEscapes = function(str) { return str.replace(/\\([0-9a-fA-F]{1,6}\x20?|.)|([\x22\x27])/g, function(substring, p1, p2) { var codePoint, highHex, highSurrogate, lowHex, lowSurrogate; if (p2) { // unescaped " or ' return '\\' + p2; } if (/^[0-9a-fA-F]/.test(p1)) { // \1f23 codePoint = parseInt(p1, 16); if (codePoint < 0 || codePoint > 0x10ffff) { // the replacement character return '\\ufffd'; } // javascript strings are in UTF-16 if (codePoint <= 0xffff) { // Basic lowHex = '000' + codePoint.toString(16); return '\\u' + lowHex.substr(lowHex.length - 4); } // Supplementary codePoint -= 0x10000; highSurrogate = (codePoint >> 10) + 0xd800; lowSurrogate = (codePoint % 0x400) + 0xdc00; highHex = '000' + highSurrogate.toString(16); lowHex = '000' + lowSurrogate.toString(16); return '\\u' + highHex.substr(highHex.length - 4) + '\\u' + lowHex.substr(lowHex.length - 4); } if (/^[\\\x22\x27]/.test(p1)) { // \' \" return substring; } // \g \h \. \# etc return p1; }); }, /*------------------------------ DOM METHODS -------------------------------*/ // element by id (raw) // @return reference or null byIdRaw = function(id, elements) { var i = -1, element = null; while ((element = elements[++i])) { if (element.getAttribute('id') == id) { break; } } return element; }, // element by id // @return reference or null _byId = !BUGGY_GEBID ? function(id, from) { id = id.replace(/\\([^\\]{1})/g, '$1'); return from.getElementById && from.getElementById(id) || byIdRaw(id, from.getElementsByTagName('*')); } : function(id, from) { var element = null; id = id.replace(/\\([^\\]{1})/g, '$1'); if (XML_DOCUMENT || from.nodeType != 9) { return byIdRaw(id, from.getElementsByTagName('*')); } if ((element = from.getElementById(id)) && element.name == id && from.getElementsByName) { return byIdRaw(id, from.getElementsByName(id)); } return element; }, // publicly exposed byId // @return reference or null byId = function(id, from) { from || (from = doc); if (lastContext !== from) { switchContext(from); } return _byId(id, from); }, // elements by tag (raw) // @return array byTagRaw = function(tag, from) { var any = tag == '*', element = from, elements = new global.Array(), next = element.firstChild; any || (tag = tag.toUpperCase()); while ((element = next)) { if (element.tagName > '@' && (any || element.tagName.toUpperCase() == tag)) { elements[elements.length] = element; } if ((next = element.firstChild || element.nextSibling)) continue; while (!next && (element = element.parentNode) && element !== from) { next = element.nextSibling; } } return elements; }, // elements by tag // @return array _byTag = !BUGGY_GEBTN && NATIVE_SLICE_PROTO ? function(tag, from) { return XML_DOCUMENT || from.nodeType == 11 ? byTagRaw(tag, from) : slice.call(from.getElementsByTagName(tag), 0); } : function(tag, from) { var i = -1, j = i, data = new global.Array(), element, elements = from.getElementsByTagName(tag); if (tag == '*') { while ((element = elements[++i])) { if (element.nodeName > '@') data[++j] = element; } } else { while ((element = elements[++i])) { data[i] = element; } } return data; }, // publicly exposed byTag // @return array byTag = function(tag, from) { from || (from = doc); if (lastContext !== from) { switchContext(from); } return _byTag(tag, from); }, // publicly exposed byName // @return array byName = function(name, from) { return select('[name="' + name.replace(/\\([^\\]{1})/g, '$1') + '"]', from); }, // elements by class (raw) // @return array byClassRaw = function(name, from) { var i = -1, j = i, data = new global.Array(), element, elements = _byTag('*', from), n; name = ' ' + (QUIRKS_MODE ? name.toLowerCase() : name).replace(/\\([^\\]{1})/g, '$1') + ' '; while ((element = elements[++i])) { n = XML_DOCUMENT ? element.getAttribute('class') : element.className; if (n && n.length && (' ' + (QUIRKS_MODE ? n.toLowerCase() : n). replace(reWhiteSpace, ' ') + ' ').indexOf(name) > -1) { data[++j] = element; } } return data; }, // elements by class // @return array _byClass = function(name, from) { return (BUGGY_GEBCN || BUGGY_QUIRKS_GEBCN || XML_DOCUMENT || !from.getElementsByClassName) ? byClassRaw(name, from) : slice.call(from.getElementsByClassName(name.replace(/\\([^\\]{1})/g, '$1')), 0); }, // publicly exposed byClass // @return array byClass = function(name, from) { from || (from = doc); if (lastContext !== from) { switchContext(from); } return _byClass(name, from); }, // check element is descendant of container // @return boolean contains = 'compareDocumentPosition' in root ? function(container, element) { return (container.compareDocumentPosition(element) & 16) == 16; } : 'contains' in root ? function(container, element) { return container !== element && container.contains(element); } : function(container, element) { while ((element = element.parentNode)) { if (element === container) return true; } return false; }, // attribute value // @return string getAttribute = !BUGGY_GET_ATTRIBUTE ? function(node, attribute) { return node.getAttribute(attribute); } : function(node, attribute) { attribute = attribute.toLowerCase(); if (typeof node[attribute] == 'object') { return node.attributes[attribute] && node.attributes[attribute].value; } return ( // 'type' can only be read by using native getAttribute attribute == 'type' ? node.getAttribute(attribute) : // specific URI data attributes (parameter 2 to fix IE bug) ATTR_URIDATA[attribute] ? node.getAttribute(attribute, 2) : // boolean attributes should return name instead of true/false ATTR_BOOLEAN[attribute] ? node.getAttribute(attribute) ? attribute : 'false' : (node = node.getAttributeNode(attribute)) && node.value); }, // attribute presence // @return boolean hasAttribute = !BUGGY_HAS_ATTRIBUTE ? function(node, attribute) { return XML_DOCUMENT ? !!node.getAttribute(attribute) : node.hasAttribute(attribute); } : function(node, attribute) { // read the node attribute object var obj = node.getAttributeNode(attribute = attribute.toLowerCase()); return ATTR_DEFAULT[attribute] && attribute != 'value' ? node[ATTR_DEFAULT[attribute]] : obj && obj.specified; }, // check node emptyness // @return boolean isEmpty = function(node) { node = node.firstChild; while (node) { if (node.nodeType == 3 || node.nodeName > '@') return false; node = node.nextSibling; } return true; }, // check if element matches the :link pseudo // @return boolean isLink = function(element) { return hasAttribute(element,'href') && LINK_NODES[element.nodeName]; }, // child position by nodeType // @return number nthElement = function(element, last) { var count = 1, succ = last ? 'nextSibling' : 'previousSibling'; while ((element = element[succ])) { if (element.nodeName > '@') ++count; } return count; }, // child position by nodeName // @return number nthOfType = function(element, last) { var count = 1, succ = last ? 'nextSibling' : 'previousSibling', type = element.nodeName; while ((element = element[succ])) { if (element.nodeName == type) ++count; } return count; }, /*------------------------------- DEBUGGING --------------------------------*/ // get/set (string/object) working modes configure = function(option) { if (typeof option == 'string') { return Config[option] || Config; } if (typeof option != 'object') { return false; } for (var i in option) { Config[i] = !!option[i]; if (i == 'SIMPLENOT') { matchContexts = new global.Object(); matchResolvers = new global.Object(); selectContexts = new global.Object(); selectResolvers = new global.Object(); if (!Config[i]) { Config['USE_QSAPI'] = false; } } else if (i == 'USE_QSAPI') { Config[i] = !!option[i] && NATIVE_QSAPI; } } reValidator = new global.RegExp(Config.SIMPLENOT ? standardValidator : extendedValidator, 'g'); return true; }, // control user notifications emit = function(message) { if (Config.VERBOSITY) { throw new global.Error(message); } if (global.console && global.console.log) { global.console.log(message); } }, Config = new global.Object({ // used to enable/disable caching of result sets CACHING: false, // by default do not add missing left/right context // to selector string shortcuts like "+div" or "ul>" // callable Dom.shortcuts method has to be available SHORTCUTS: false, // by default disable complex selectors nested in // ':not()' pseudo-classes, as for specifications SIMPLENOT: true, // strict QSA match all non-unique IDs (false) // speed & libs compat match unique ID (true) UNIQUE_ID: true, // HTML5 handling for the ":checked" pseudo-class USE_HTML5: true, // controls enabling the Query Selector API branch USE_QSAPI: NATIVE_QSAPI, // controls the engine error/warning notifications VERBOSITY: true }), /*---------------------------- COMPILER METHODS ----------------------------*/ // code string reused to build compiled functions ACCEPT_NODE = 'r[r.length]=c[k];if(f&&false===f(c[k]))break main;else continue main;', // compile a comma separated group of selector // @mode boolean true for select, false for match // return a compiled function compile = function(selector, source, mode) { var parts = typeof selector == 'string' ? selector.match(reSplitGroup) : selector; // ensures that source is a string typeof source == 'string' || (source = ''); if (parts.length == 1) { source += compileSelector(parts[0], mode ? ACCEPT_NODE : 'f&&f(k);return true;', mode); } else { // for each selector in the group var i = -1, seen = new global.Object(), token; while ((token = parts[++i])) { token = token.replace(reTrimSpaces, ''); // avoid repeating the same token // in comma separated group (p, p) if (!seen[token] && (seen[token] = true)) { source += compileSelector(token, mode ? ACCEPT_NODE : 'f&&f(k);return true;', mode); } } } if (mode) { // for select method return new global.Function('c,s,r,d,h,g,f,v', 'var N,n,x=0,k=-1,e;main:while((e=c[++k])){' + source + '}return r;'); } else { // for match method return new global.Function('e,s,r,d,h,g,f,v', 'var N,n,x=0,k=e;' + source + 'return false;'); } }, // allows to cache already visited nodes FILTER = 'var z=v[@]||(v[@]=[]),l=z.length-1;' + 'while(l>=0&&z[l]!==e)--l;' + 'if(l!==-1){break;}' + 'z[z.length]=e;', // compile a CSS3 string selector into ad-hoc javascript matching function // @return string (to be compiled) compileSelector = function(selector, source, mode) { var a, b, n, k = 0, expr, match, result, status, test, type; while (selector) { k++; // *** Universal selector // * match all (empty block, do not remove) if ((match = selector.match(Patterns.universal))) { // do nothing, handled in the compiler where // BUGGY_GEBTN return comment nodes (ex: IE) expr = ''; } // *** ID selector // #Foo Id case sensitive else if ((match = selector.match(Patterns.id))) { // document can contain conflicting elements (id/name) // prototype selector unit need this method to recover bad HTML forms source = 'if(' + (XML_DOCUMENT ? 's.getAttribute(e,"id")' : '(e.submit?s.getAttribute(e,"id"):e.id)') + '=="' + match[1] + '"' + '){' + source + '}'; } // *** Type selector // Foo Tag (case insensitive) else if ((match = selector.match(Patterns.tagName))) { // both tagName and nodeName properties may be upper/lower case // depending on their creation NAMESPACE in createElementNS() source = 'if(e.nodeName' + (XML_DOCUMENT ? '=="' + match[1] + '"' : '.toUpperCase()' + '=="' + match[1].toUpperCase() + '"') + '){' + source + '}'; } // *** Class selector // .Foo Class (case sensitive) else if ((match = selector.match(Patterns.className))) { // W3C CSS3 specs: element whose "class" attribute has been assigned a // list of whitespace-separated values, see section 6.4 Class selectors // and notes at the bottom; explicitly non-normative in this specification. source = 'if((n=' + (XML_DOCUMENT ? 's.getAttribute(e,"class")' : 'e.className') + ')&&n.length&&(" "+' + (QUIRKS_MODE ? 'n.toLowerCase()' : 'n') + '.replace(' + reWhiteSpace + '," ")+" ").indexOf(" ' + (QUIRKS_MODE ? match[1].toLowerCase() : match[1]) + ' ")>-1' + '){' + source + '}'; } // *** Attribute selector // [attr] [attr=value] [attr="value"] [attr='value'] and !=, *=, ~=, |=, ^=, $= // case sensitivity is treated differently depending on the document type (see map) else if ((match = selector.match(Patterns.attribute))) { // xml namespaced attribute ? expr = match[1].split(':'); expr = expr.length == 2 ? expr[1] : expr[0] + ''; if (match[2] && !Operators[match[2]]) { emit('Unsupported operator in attribute selectors "' + selector + '"'); return ''; } test = 'false'; // replace Operators parameter if needed if (match[2] && match[4] && (test = Operators[match[2]])) { match[4] = convertEscapes(match[4]); // case treatment depends on document HTML_TABLE['class'] = QUIRKS_MODE ? 1 : 0; type = (XML_DOCUMENT ? XHTML_TABLE : HTML_TABLE)[expr.toLowerCase()]; test = test.replace(/\%m/g, type ? match[4].toLowerCase() : match[4]); } else if (match[2] == '!=' || match[2] == '=') { test = 'n' + match[2] + '=""'; } source = 'if(n=s.hasAttribute(e,"' + match[1] + '")){' + (match[2] ? 'n=s.getAttribute(e,"' + match[1] + '")' : '') + (type && match[2] ? '.toLowerCase();' : ';') + 'if(' + (match[2] ? test : 'n') + '){' + source + '}}'; } // *** Adjacent sibling combinator // E + F (F adiacent sibling of E) else if ((match = selector.match(Patterns.adjacent))) { source = (mode ? '' : FILTER.replace(/@/g, k)) + source; source = NATIVE_TRAVERSAL_API ? 'var N' + k + '=e;while(e&&(e=e.previousElementSibling)){' + source + 'break;}e=N' + k + ';' : 'var N' + k + '=e;while(e&&(e=e.previousSibling)){if(e.nodeName>"@"){' + source + 'break;}}e=N' + k + ';'; } // *** General sibling combinator // E ~ F (F relative sibling of E) else if ((match = selector.match(Patterns.relative))) { source = (mode ? '' : FILTER.replace(/@/g, k)) + source; source = NATIVE_TRAVERSAL_API ? ('var N' + k + '=e;e=e.parentNode.firstElementChild;' + 'while(e&&e!==N' + k + '){' + source + 'e=e.nextElementSibling;}e=N' + k + ';') : ('var N' + k + '=e;e=e.parentNode.firstChild;' + 'while(e&&e!==N' + k + '){if(e.nodeName>"@"){' + source + '}e=e.nextSibling;}e=N' + k + ';'); } // *** Child combinator // E > F (F children of E) else if ((match = selector.match(Patterns.children))) { source = (mode ? '' : FILTER.replace(/@/g, k)) + source; source = 'var N' + k + '=e;while(e&&e!==h&&e!==g&&(e=e.parentNode)){' + source + 'break;}e=N' + k + ';'; } // *** Descendant combinator // E F (E ancestor of F) else if ((match = selector.match(Patterns.ancestor))) { source = (mode ? '' : FILTER.replace(/@/g, k)) + source; source = 'var N' + k + '=e;while(e&&e!==h&&e!==g&&(e=e.parentNode)){' + source + '}e=N' + k + ';'; } // *** Structural pseudo-classes // :root, :empty, // :first-child, :last-child, :only-child, // :first-of-type, :last-of-type, :only-of-type, // :nth-child(), :nth-last-child(), :nth-of-type(), :nth-last-of-type() else if ((match = selector.match(Patterns.spseudos)) && match[1]) { switch (match[1]) { case 'root': // element root of the document if (match[3]) { source = 'if(e===h||s.contains(h,e)){' + source + '}'; } else { source = 'if(e===h){' + source + '}'; } break; case 'empty': // element that has no children source = 'if(s.isEmpty(e)){' + source + '}'; break; default: if (match[1] && match[2]) { if (match[2] == 'n') { source = 'if(e!==h){' + source + '}'; break; } else if (match[2] == 'even') { a = 2; b = 0; } else if (match[2] == 'odd') { a = 2; b = 1; } else { // assumes correct "an+b" format, "b" before "a" to keep "n" values b = ((n = match[2].match(/(-?\d+)$/)) ? global.parseInt(n[1], 10) : 0); a = ((n = match[2].match(/(-?\d*)n/i)) ? global.parseInt(n[1], 10) : 0); if (n && n[1] == '-') a = -1; } // build test expression out of structural pseudo (an+b) parameters // see here: http://www.w3.org/TR/css3-selectors/#nth-child-pseudo test = a > 1 ? (/last/i.test(match[1])) ? '(n-(' + b + '))%' + a + '==0' : 'n>=' + b + '&&(n-(' + b + '))%' + a + '==0' : a < -1 ? (/last/i.test(match[1])) ? '(n-(' + b + '))%' + a + '==0' : 'n<=' + b + '&&(n-(' + b + '))%' + a + '==0' : a === 0 ? 'n==' + b : a == -1 ? 'n<=' + b : 'n>=' + b; // 4 cases: 1 (nth) x 4 (child, of-type, last-child, last-of-type) source = 'if(e!==h){' + 'n=s[' + (/-of-type/i.test(match[1]) ? '"nthOfType"' : '"nthElement"') + ']' + '(e,' + (/last/i.test(match[1]) ? 'true' : 'false') + ');' + 'if(' + test + '){' + source + '}' + '}'; } else { // 6 cases: 3 (first, last, only) x 1 (child) x 2 (-of-type) a = /first/i.test(match[1]) ? 'previous' : 'next'; n = /only/i.test(match[1]) ? 'previous' : 'next'; b = /first|last/i.test(match[1]); type = /-of-type/i.test(match[1]) ? '&&n.nodeName!=e.nodeName' : '&&n.nodeName<"@"'; source = 'if(e!==h){' + ( 'n=e;while((n=n.' + a + 'Sibling)' + type + ');if(!n){' + (b ? source : 'n=e;while((n=n.' + n + 'Sibling)' + type + ');if(!n){' + source + '}') + '}' ) + '}'; } break; } } // *** negation, user action and target pseudo-classes // *** UI element states and dynamic pseudo-classes // CSS3 :not, :checked, :enabled, :disabled, :target // CSS3 :active, :hover, :focus // CSS3 :link, :visited else if ((match = selector.match(Patterns.dpseudos)) && match[1]) { switch (match[1].match(/^\w+/)[0]) { // CSS3 negation pseudo-class case 'not': // compile nested selectors, DO NOT pass the callback parameter // SIMPLENOT allow disabling complex selectors nested // in ':not()' pseudo-classes, breaks some test units expr = match[3].replace(reTrimSpaces, ''); if (Config.SIMPLENOT && !reSimpleNot.test(expr)) { // see above, log error but continue execution emit('Negation pseudo-class only accepts simple selectors "' + selector + '"'); return ''; } else { if ('compatMode' in doc) { source = 'if(!' + compile(expr, '', false) + '(e,s,r,d,h,g)){' + source + '}'; } else { source = 'if(!s.match(e, "' + expr.replace(/\x22/g, '\\"') + '",g)){' + source +'}'; } } break; // CSS3 UI element states case 'checked': // for radio buttons checkboxes (HTML4) and options (HTML5) source = 'if((typeof e.form!=="undefined"&&(/^(?:radio|checkbox)$/i).test(e.type)&&e.checked)' + (Config.USE_HTML5 ? '||(/^option$/i.test(e.nodeName)&&(e.selected||e.checked))' : '') + '){' + source + '}'; break; case 'disabled': // does not consider hidden input fields source = 'if(((typeof e.form!=="undefined"' + (Config.USE_HTML5 ? '' : '&&!(/^hidden$/i).test(e.type)') + ')||s.isLink(e))&&e.disabled===true){' + source + '}'; break; case 'enabled': // does not consider hidden input fields source = 'if(((typeof e.form!=="undefined"' + (Config.USE_HTML5 ? '' : '&&!(/^hidden$/i).test(e.type)') + ')||s.isLink(e))&&e.disabled===false){' + source + '}'; break; // CSS3 lang pseudo-class case 'lang': test = ''; if (match[2]) test = match[2].substr(0, 2) + '-'; source = 'do{(n=e.lang||"").toLowerCase();' + 'if((n==""&&h.lang=="' + match[2].toLowerCase() + '")||' + '(n&&(n=="' + match[2].toLowerCase() + '"||n.substr(0,3)=="' + test.toLowerCase() + '")))' + '{' + source + 'break;}}while((e=e.parentNode)&&e!==g);'; break; // CSS3 target pseudo-class case 'target': source = 'if(e.id==d.location.hash.slice(1)){' + source + '}'; break; // CSS3 dynamic pseudo-classes case 'link': source = 'if(s.isLink(e)&&!e.visited){' + source + '}'; break; case 'visited': source = 'if(s.isLink(e)&&e.visited){' + source + '}'; break; // CSS3 user action pseudo-classes IE & FF3 have native support // these capabilities may be emulated by some event managers case 'active': if (XML_DOCUMENT) break; source = 'if(e===d.activeElement){' + source + '}'; break; case 'hover': if (XML_DOCUMENT) break; source = 'if(e===d.hoverElement){' + source + '}'; break; case 'focus': if (XML_DOCUMENT) break; source = NATIVE_FOCUS ? 'if(e===d.activeElement&&d.hasFocus()&&(e.type||e.href||typeof e.tabIndex=="number")){' + source + '}' : 'if(e===d.activeElement&&(e.type||e.href)){' + source + '}'; break; // CSS2 selected pseudo-classes, not part of current CSS3 drafts // the 'selected' property is only available for option elements case 'selected': // fix Safari selectedIndex property bug expr = BUGGY_SELECTED ? '||(n=e.parentNode)&&n.options[n.selectedIndex]===e' : ''; source = 'if(/^option$/i.test(e.nodeName)&&(e.selected||e.checked' + expr + ')){' + source + '}'; break; default: break; } } else { // this is where external extensions are // invoked if expressions match selectors expr = false; status = false; for (expr in Selectors) { if ((match = selector.match(Selectors[expr].Expression)) && match[1]) { result = Selectors[expr].Callback(match, source); source = result.source; status = result.status; if (status) { break; } } } // if an extension fails to parse the selector // it must return a false boolean in "status" if (!status) { // log error but continue execution, don't throw real exceptions // because blocking following processes maybe is not a good idea emit('Unknown pseudo-class selector "' + selector + '"'); return ''; } if (!expr) { // see above, log error but continue execution emit('Unknown token in selector "' + selector + '"'); return ''; } } // error if no matches found by the pattern scan if (!match) { emit('Invalid syntax in selector "' + selector + '"'); return ''; } // ensure "match" is not null or empty since // we do not throw real DOMExceptions above selector = match && match[match.length - 1]; } return source; }, /*----------------------------- QUERY METHODS ------------------------------*/ // match element with selector // @return boolean match = function(element, selector, from, callback) { var parts; if (!(element && element.nodeType == 1)) { emit('Invalid element argument'); return false; } else if (typeof selector != 'string') { emit('Invalid selector argument'); return false; } else if (from && from.nodeType == 1 && !contains(from, element)) { return false; } else if (lastContext !== from) { // reset context data when it changes // and ensure context is set to a default switchContext(from || (from = element.ownerDocument)); } selector = selector.replace(reTrimSpaces, ''); Config.SHORTCUTS && (selector = Dom.shortcuts(selector, element, from)); if (lastMatcher != selector) { // process valid selector strings if ((parts = selector.match(reValidator)) && parts[0] == selector) { isSingleMatch = (parts = selector.match(reSplitGroup)).length < 2; // save passed selector lastMatcher = selector; lastPartsMatch = parts; } else { emit('The string "' + selector + '", is not a valid CSS selector'); return false; } } else parts = lastPartsMatch; // compile matcher resolvers if necessary if (!matchResolvers[selector] || matchContexts[selector] !== from) { matchResolvers[selector] = compile(isSingleMatch ? [selector] : parts, '', false); matchContexts[selector] = from; } return matchResolvers[selector](element, Snapshot, [ ], doc, root, from, callback, new global.Object()); }, // select only the first element // matching selector (document ordered) first = function(selector, from) { return select(selector, from, function() { return false; })[0] || null; }, // select elements matching selector // using new Query Selector API // or cross-browser client API // @return array select = function(selector, from, callback) { var i, changed, element, elements, parts, token, original = selector; if (arguments.length === 0) { emit('Not enough arguments'); return [ ]; } else if (typeof selector != 'string') { return [ ]; } else if (from && !(/1|9|11/).test(from.nodeType)) { emit('Invalid or illegal context element'); return [ ]; } else if (lastContext !== from) { // reset context data when it changes // and ensure context is set to a default switchContext(from || (from = doc)); } if (Config.CACHING && (elements = Dom.loadResults(original, from, doc, root))) { return callback ? concatCall([ ], elements, callback) : elements; } if (!OPERA_QSAPI && RE_SIMPLE_SELECTOR.test(selector)) { switch (selector.charAt(0)) { case '#': if (Config.UNIQUE_ID) { elements = (element = _byId(selector.slice(1), from)) ? [ element ] : [ ]; } break; case '.': elements = _byClass(selector.slice(1), from); break; default: elements = _byTag(selector, from); break; } } else if (!XML_DOCUMENT && Config.USE_QSAPI && !(BUGGY_QUIRKS_QSAPI && RE_CLASS.test(selector)) && !RE_BUGGY_QSAPI.test(selector)) { try { elements = from.querySelectorAll(selector); } catch(e) { } } if (elements) { elements = callback ? concatCall([ ], elements, callback) : NATIVE_SLICE_PROTO ? slice.call(elements) : concatList([ ], elements); Config.CACHING && Dom.saveResults(original, from, doc, elements); return elements; } selector = selector.replace(reTrimSpaces, ''); Config.SHORTCUTS && (selector = Dom.shortcuts(selector, from)); if ((changed = lastSelector != selector)) { // process valid selector strings if ((parts = selector.match(reValidator)) && parts[0] == selector) { isSingleSelect = (parts = selector.match(reSplitGroup)).length < 2; // save passed selector lastSelector = selector; lastPartsSelect = parts; } else { emit('The string "' + selector + '", is not a valid CSS selector'); return [ ]; } } else parts = lastPartsSelect; // commas separators are treated sequentially to maintain order if (from.nodeType == 11) { elements = byTagRaw('*', from); } else if (!XML_DOCUMENT && isSingleSelect) { if (changed) { // get right most selector token parts = selector.match(reSplitToken); token = parts[parts.length - 1]; // only last slice before :not rules lastSlice = token.split(':not')[0]; // position where token was found lastPosition = selector.length - token.length; } // ID optimization RTL, to reduce number of elements to visit if (Config.UNIQUE_ID && (parts = lastSlice.match(Optimize.ID)) && (token = parts[1])) { if ((element = _byId(token, from))) { if (match(element, selector)) { callback && callback(element); elements = new global.Array(element); } else elements = new global.Array(); } } // ID optimization LTR, to reduce selection context searches else if (Config.UNIQUE_ID && (parts = selector.match(Optimize.ID)) && (token = parts[1])) { if ((element = _byId(token, doc))) { if ('#' + token == selector) { callback && callback(element); elements = new global.Array(element); } else if (/[>+~]/.test(selector)) { from = element.parentNode; } else { from = element; } } else elements = new global.Array(); } if (elements) { Config.CACHING && Dom.saveResults(original, from, doc, elements); return elements; } if (!NATIVE_GEBCN && (parts = lastSlice.match(Optimize.TAG)) && (token = parts[1])) { if ((elements = _byTag(token, from)).length === 0) { return [ ]; } selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace(token, '*'); } else if ((parts = lastSlice.match(Optimize.CLASS)) && (token = parts[1])) { if ((elements = _byClass(token, from)).length === 0) { return [ ]; } if (reOptimizeSelector.test(selector.charAt(selector.indexOf(token) - 1))) { selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace('.' + token, ''); } else { selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace('.' + token, '*'); } } else if ((parts = selector.match(Optimize.CLASS)) && (token = parts[1])) { if ((elements = _byClass(token, from)).length === 0) { return [ ]; } for (i = 0, els = new global.Array(); elements.length > i; ++i) { els = concatList(els, elements[i].getElementsByTagName('*')); } elements = els; if (reOptimizeSelector.test(selector.charAt(selector.indexOf(token) - 1))) { selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace('.' + token, ''); } else { selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace('.' + token, '*'); } } else if (NATIVE_GEBCN && (parts = lastSlice.match(Optimize.TAG)) && (token = parts[1])) { if ((elements = _byTag(token, from)).length === 0) { return [ ]; } selector = selector.slice(0, lastPosition) + selector.slice(lastPosition).replace(token, '*'); } } if (!elements) { elements = /^(?:applet|object)$/i.test(from.nodeName) ? from.childNodes : _byTag('*', from); } // end of prefiltering pass // compile selector resolver if necessary if (!selectResolvers[selector] || selectContexts[selector] !== from) { selectResolvers[selector] = compile(isSingleSelect ? [selector] : parts, '', true); selectContexts[selector] = from; } elements = selectResolvers[selector](elements, Snapshot, [ ], doc, root, from, callback, new global.Object()); Config.CACHING && Dom.saveResults(original, from, doc, elements); return elements; }, /*-------------------------------- STORAGE ---------------------------------*/ // empty function handler FN = function(x) { return x; }, // compiled match functions returning booleans matchContexts = new global.Object(), matchResolvers = new global.Object(), // compiled select functions returning collections selectContexts = new global.Object(), selectResolvers = new global.Object(), // used to pass methods to compiled functions Snapshot = new global.Object({ // element indexing methods nthElement: nthElement, nthOfType: nthOfType, // element inspection methods getAttribute: getAttribute, hasAttribute: hasAttribute, // element selection methods byClass: _byClass, byName: byName, byTag: _byTag, byId: _byId, // helper/check methods contains: contains, isEmpty: isEmpty, isLink: isLink, // selection/matching select: select, match: match }), Tokens = new global.Object({ prefixes: prefixes, encoding: encoding, operators: operators, whitespace: whitespace, identifier: identifier, attributes: attributes, combinators: combinators, pseudoclass: pseudoclass, pseudoparms: pseudoparms, quotedvalue: quotedvalue }); /*------------------------------- PUBLIC API -------------------------------*/ // code referenced by extensions Dom.ACCEPT_NODE = ACCEPT_NODE; // retrieve element by id attr Dom.byId = byId; // retrieve elements by tag name Dom.byTag = byTag; // retrieve elements by name attr Dom.byName = byName; // retrieve elements by class name Dom.byClass = byClass; // read the value of the attribute // as was in the original HTML code Dom.getAttribute = getAttribute; // check for the attribute presence // as was in the original HTML code Dom.hasAttribute = hasAttribute; // element match selector, return boolean true/false Dom.match = match; // first element match only, return element or null Dom.first = first; // elements matching selector, starting from element Dom.select = select; // compile selector into ad-hoc javascript resolver Dom.compile = compile; // check that two elements are ancestor/descendant Dom.contains = contains; // handle selector engine configuration settings Dom.configure = configure; // initialize caching for each document Dom.setCache = FN; // load previously collected result set Dom.loadResults = FN; // save previously collected result set Dom.saveResults = FN; // handle missing context in selector strings Dom.shortcuts = FN; // log resolvers errors/warnings Dom.emit = emit; // options enabing specific engine functionality Dom.Config = Config; // pass methods references to compiled resolvers Dom.Snapshot = Snapshot; // operators descriptor // for attribute operators extensions Dom.Operators = Operators; // selectors descriptor // for pseudo-class selectors extensions Dom.Selectors = Selectors; // export string patterns Dom.Tokens = Tokens; // export version string Dom.Version = version; // add or overwrite user defined operators Dom.registerOperator = function(symbol, resolver) { Operators[symbol] || (Operators[symbol] = resolver); }; // add selector patterns for user defined callbacks Dom.registerSelector = function(name, rexp, func) { Selectors[name] || (Selectors[name] = new global.Object({ Expression: rexp, Callback: func })); }; /*---------------------------------- INIT ----------------------------------*/ // init context specific variables switchContext(doc, true); }); /* selectivizr.com Notes about this source ----------------------- * The #DEBUG_START and #DEBUG_END comments are used to mark blocks of code that will be removed prior to building a final release version (using a pre-compression script) References: ----------- * CSS Syntax : https://www.w3.org/TR/2003/WD-css3-syntax-20030813/#style * Selectors : https://www.w3.org/TR/css3-selectors/#selectors * IE Compatability : https://msdn.microsoft.com/en-us/library/cc351024%28VS.85%29.aspx * W3C Selector Tests : https://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/html/tests/ */ (function(win) { // Determine IE version and stop execution if browser isn't IE. This // handles the script being loaded by non IE browsers because the // developer didn't use conditional comments. var ieUserAgent = navigator.userAgent.match(/MSIE (\d+)/); if (!ieUserAgent) { return false; } // =========================== Init Objects ============================ var doc = document; var root = doc.documentElement; var xhr = getXHRObject(); var ieVersion = ieUserAgent[1]; // If were not in standards mode, IE is too old / new or we can't create // an XMLHttpRequest object then we should get out now. if (doc.compatMode != 'CSS1Compat' || ieVersion<6 || ieVersion>8 || !xhr) { return; } // ========================= Common Objects ============================ // Compatible selector engines in order of CSS3 support. Note: '*' is // a placeholder for the object key name. (basically, crude compression) var selectorEngines = { "NW" : "*.Dom.select", "MooTools" : "$$", "DOMAssistant" : "*.$", "Prototype" : "$$", "YAHOO" : "*.util.Selector.query", "Sizzle" : "*", "jQuery" : "*", "dojo" : "*.query" }; var selectorMethod; var enabledWatchers = []; // array of :enabled/:disabled elements to poll var domPatches = []; var ie6PatchID = 0; // used to solve ie6's multiple class bug var patchIE6MultipleClasses = true; // if true adds class bloat to ie6 var namespace = "slvzr"; // Stylesheet parsing regexp's var RE_COMMENT = /(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)\s*?/g; var RE_IMPORT = /@import\s*(?:(?:(?:url\(\s*(['"]?)([^;]*)\1)\s*\))|(?:(['"])([^;]*)\3))\s*([^;]*);/g; var RE_ASSET_URL = /(behavior\s*?:\s*)?\burl\(\s*(["']?)(?!data:)([^"')]+)\2\s*\)/g; var RE_PSEUDO_STRUCTURAL = /^:(empty|(first|last|only|nth(-last)?)-(child|of-type))$/; var RE_PSEUDO_ELEMENTS = /:(:first-(?:line|letter))/g; var RE_SELECTOR_GROUP = /((?:^|(?:\s*})+)(?:\s*@media[^{]+{)?)\s*([^\{]*?[\[:][^{]+)/g; var RE_SELECTOR_PARSE = /([ +~>])|(:[a-z-]+(?:\(.*?\)+)?)|(\[.*?\])/g; var RE_LIBRARY_INCOMPATIBLE_PSEUDOS = /(:not\()?:(hover|enabled|disabled|focus|checked|target|active|visited|first-line|first-letter)\)?/g; var RE_PATCH_CLASS_NAME_REPLACE = /[^\w-]/g; // HTML UI element regexp's var RE_INPUT_ELEMENTS = /^(INPUT|SELECT|TEXTAREA|BUTTON)$/; var RE_INPUT_CHECKABLE_TYPES = /^(checkbox|radio)$/; // Broken attribute selector implementations (IE7/8 native [^=""], [$=""] and [*=""]) var BROKEN_ATTR_IMPLEMENTATIONS = ieVersion>6 ? /[\$\^*]=(['"])\1/ : null; // Whitespace normalization regexp's var RE_TIDY_TRAILING_WHITESPACE = /([(\[+~])\s+/g; var RE_TIDY_LEADING_WHITESPACE = /\s+([)\]+~])/g; var RE_TIDY_CONSECUTIVE_WHITESPACE = /\s+/g; var RE_TIDY_TRIM_WHITESPACE = /^\s*((?:[\S\s]*\S)?)\s*$/; // String constants var EMPTY_STRING = ""; var SPACE_STRING = " "; var PLACEHOLDER_STRING = "$1"; // =========================== Patching ================================ // --[ patchStyleSheet() ]---------------------------------------------- // Scans the passed cssText for selectors that require emulation and // creates one or more patches for each matched selector. function patchStyleSheet( cssText ) { return cssText.replace(RE_PSEUDO_ELEMENTS, PLACEHOLDER_STRING). replace(RE_SELECTOR_GROUP, function(m, prefix, selectorText) { var selectorGroups = selectorText.split(","); function comboBreaker (match, combinator, pseudo, attribute, index) { if (combinator) { if (patches.length>0) { domPatches.push( { selector: selector.substring(0, index), patches: patches } ); patches = []; } return combinator; } else { var patch = (pseudo) ? patchPseudoClass( pseudo ) : patchAttribute( attribute ); if (patch) { patches.push(patch); return "." + patch.className; } return match; } } for (var c = 0, cs = selectorGroups.length; c < cs; c++) { var selector = normalizeSelectorWhitespace(selectorGroups[c]) + SPACE_STRING; var patches = []; selectorGroups[c] = selector.replace(RE_SELECTOR_PARSE, comboBreaker); } return prefix + selectorGroups.join(","); }); } // --[ patchAttribute() ]----------------------------------------------- // returns a patch for an attribute selector. function patchAttribute( attr ) { return (!BROKEN_ATTR_IMPLEMENTATIONS || BROKEN_ATTR_IMPLEMENTATIONS.test(attr)) ? { className: createClassName(attr), applyClass: true } : null; } // --[ patchPseudoClass() ]--------------------------------------------- // returns a patch for a pseudo-class function patchPseudoClass( pseudo ) { var applyClass = true; var className = createClassName(pseudo.slice(1)); var isNegated = pseudo.substring(0, 5) == ":not("; var activateEventName; var deactivateEventName; // if negated, remove :not() if (isNegated) { pseudo = pseudo.slice(5, -1); } // bracket contents are irrelevant - remove them var bracketIndex = pseudo.indexOf("("); if (bracketIndex > -1) { pseudo = pseudo.substring(0, bracketIndex); } // check we're still dealing with a pseudo-class if (pseudo.charAt(0) == ":") { switch (pseudo.slice(1)) { case "root": applyClass = function(e) { return isNegated ? e != root : e == root; }; break; case "target": // :target is only supported in IE8 if (ieVersion == 8) { applyClass = function(e) { var handler = function() { var hash = location.hash; var hashID = hash.slice(1); return isNegated ? (hash == EMPTY_STRING || e.id != hashID) : (hash != EMPTY_STRING && e.id == hashID); }; addEvent( win, "hashchange", function() { toggleElementClass(e, className, handler()); }); return handler(); }; break; } return false; case "checked": applyClass = function(e) { if (RE_INPUT_CHECKABLE_TYPES.test(e.type)) { addEvent( e, "propertychange", function() { if (event.propertyName == "checked") { toggleElementClass( e, className, e.checked !== isNegated ); } }); } return e.checked !== isNegated; }; break; case "disabled": isNegated = !isNegated; break; case "enabled": applyClass = function(e) { if (RE_INPUT_ELEMENTS.test(e.tagName)) { addEvent( e, "propertychange", function() { if (event.propertyName == "$disabled") { toggleElementClass( e, className, e.$disabled === isNegated ); } }); enabledWatchers.push(e); e.$disabled = e.disabled; return e.disabled === isNegated; } return pseudo == ":enabled" ? isNegated : !isNegated; }; break; case "focus": activateEventName = "focus"; deactivateEventName = "blur"; break; case "hover": if (!activateEventName) { activateEventName = "mouseenter"; deactivateEventName = "mouseleave"; } applyClass = function(e) { addEvent( e, isNegated ? deactivateEventName : activateEventName, function() { toggleElementClass( e, className, true ); }); addEvent( e, isNegated ? activateEventName : deactivateEventName, function() { toggleElementClass( e, className, false ); }); return isNegated; }; break; // everything else default: // If we don't support this pseudo-class don't create // a patch for it if (!RE_PSEUDO_STRUCTURAL.test(pseudo)) { return false; } break; } } return { className: className, applyClass: applyClass }; } // --[ applyPatches() ]------------------------------------------------- function applyPatches() { var elms, selectorText, patches, domSelectorText; for (var c=0; c 0) { setInterval( function() { for (var c = 0, cl = enabledWatchers.length; c < cl; c++) { var e = enabledWatchers[c]; if (e.disabled !== e.$disabled) { if (e.disabled) { e.disabled = false; e.$disabled = true; e.disabled = true; } else { e.$disabled = e.disabled; } } } }, 250); } } // Determine the baseUrl and download the stylesheets var baseTags = doc.getElementsByTagName("BASE"); var baseUrl = (baseTags.length > 0) ? baseTags[0].href : doc.location.href; getStyleSheets(); // Bind selectivizr to the ContentLoaded event. ContentLoaded(win, function() { // Determine the "best fit" selector engine for (var engine in selectorEngines) { var members, member, context = win; if (win[engine]) { members = selectorEngines[engine].replace("*", engine).split("."); while ((member = members.shift()) && (context = context[member])) {} if (typeof context == "function") { selectorMethod = context; init(); return; } } } }); /*! * ContentLoaded.js by Diego Perini, modified for IE<9 only (to save space) * * Author: Diego Perini (diego.perini at gmail.com) * Summary: cross-browser wrapper for DOMContentLoaded * Updated: 20101020 * License: MIT * Version: 1.2 * * URL: * http://javascript.nwbox.com/ContentLoaded/ * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE * */ // @w window reference // @f function reference function ContentLoaded(win, fn) { var done = false, top = true, init = function(e) { if (e.type == "readystatechange" && doc.readyState != "complete") return; (e.type == "load" ? win : doc).detachEvent("on" + e.type, init, false); if (!done && (done = true)) fn.call(win, e.type || e); }, poll = function() { try { root.doScroll("left"); } catch(e) { setTimeout(poll, 50); return; } init('poll'); }; if (doc.readyState == "complete") fn.call(win, EMPTY_STRING); else { if (doc.createEventObject && root.doScroll) { try { top = !win.frameElement; } catch(e) { } if (top) poll(); } addEvent(doc,"readystatechange", init); addEvent(win,"load", init); } } })(this); /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ (function(w) { "use strict"; w.matchMedia = w.matchMedia || function(doc, undefined) { var bool, docElem = doc.documentElement, refNode = docElem.firstElementChild || docElem.firstChild, fakeBody = doc.createElement("body"), div = doc.createElement("div"); div.id = "mq-test-1"; div.style.cssText = "position:absolute;top:-100em"; fakeBody.style.background = "none"; fakeBody.appendChild(div); return function(q) { div.innerHTML = '­'; docElem.insertBefore(fakeBody, refNode); bool = div.offsetWidth === 42; docElem.removeChild(fakeBody); return { matches: bool, media: q }; }; }(w.document); })(this); /*! Respond.js v1.4.0: min/max-width media query polyfill. (c) Scott Jehl. MIT Lic. j.mp/respondjs */ (function(w) { "use strict"; var respond = {}; w.respond = respond; respond.update = function() {}; var requestQueue = [], xmlHttp = function() { var xmlhttpmethod = false; try { xmlhttpmethod = new w.XMLHttpRequest(); } catch (e) { xmlhttpmethod = new w.ActiveXObject("Microsoft.XMLHTTP"); } return function() { return xmlhttpmethod; }; }(), ajax = function(url, callback) { var req = xmlHttp(); if (!req) { return; } req.open("GET", url, true); req.onreadystatechange = function() { if (req.readyState !== 4 || req.status !== 200 && req.status !== 304) { return; } callback(req.responseText); }; if (req.readyState === 4) { return; } req.send(null); }; respond.ajax = ajax; respond.queue = requestQueue; respond.regex = { media: /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi, keyframes: /@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi, urls: /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, findStyles: /@media *([^\{]+)\{([\S\s]+?)$/, only: /(only\s+)?([a-zA-Z]+)\s?/, minw: /\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/, maxw: /\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ }; respond.mediaQueriesSupported = w.matchMedia && w.matchMedia("only all") !== null && w.matchMedia("only all").matches; if (respond.mediaQueriesSupported) { return; } var doc = w.document, docElem = doc.documentElement, mediastyles = [], rules = [], appendedEls = [], parsedSheets = {}, resizeThrottle = 30, head = doc.getElementsByTagName("head")[0] || docElem, base = doc.getElementsByTagName("base")[0], links = head.getElementsByTagName("link"), lastCall, resizeDefer, eminpx, getEmValue = function() { var ret, div = doc.createElement("div"), body = doc.body, originalHTMLFontSize = docElem.style.fontSize, originalBodyFontSize = body && body.style.fontSize, fakeUsed = false; div.style.cssText = "position:absolute;font-size:1em;width:1em"; if (!body) { body = fakeUsed = doc.createElement("body"); body.style.background = "none"; } docElem.style.fontSize = "100%"; body.style.fontSize = "100%"; body.appendChild(div); if (fakeUsed) { docElem.insertBefore(body, docElem.firstChild); } ret = div.offsetWidth; if (fakeUsed) { docElem.removeChild(body); } else { body.removeChild(div); } docElem.style.fontSize = originalHTMLFontSize; if (originalBodyFontSize) { body.style.fontSize = originalBodyFontSize; } ret = eminpx = parseFloat(ret); return ret; }, applyMedia = function(fromResize) { var name = "clientWidth", docElemProp = docElem[name], currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[name] || docElemProp, styleBlocks = {}, lastLink = links[links.length - 1], now = new Date().getTime(); if (fromResize && lastCall && now - lastCall < resizeThrottle) { w.clearTimeout(resizeDefer); resizeDefer = w.setTimeout(applyMedia, resizeThrottle); return; } else { lastCall = now; } for (var i in mediastyles) { if (mediastyles.hasOwnProperty(i)) { var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw, minnull = min === null, maxnull = max === null, em = "em"; if (!!min) { min = parseFloat(min) * (min.indexOf(em) > -1 ? eminpx || getEmValue() : 1); } if (!!max) { max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1); } if (!thisstyle.hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max)) { if (!styleBlocks[thisstyle.media]) { styleBlocks[thisstyle.media] = []; } styleBlocks[thisstyle.media].push(rules[thisstyle.rules]); } } } for (var j in appendedEls) { if (appendedEls.hasOwnProperty(j)) { if (appendedEls[j] && appendedEls[j].parentNode === head) { head.removeChild(appendedEls[j]); } } } appendedEls.length = 0; for (var k in styleBlocks) { if (styleBlocks.hasOwnProperty(k)) { var ss = doc.createElement("style"), css = styleBlocks[k].join("\n"); ss.type = "text/css"; ss.media = k; head.insertBefore(ss, lastLink.nextSibling); if (ss.styleSheet) { ss.styleSheet.cssText = css; } else { ss.appendChild(doc.createTextNode(css)); } appendedEls.push(ss); } } }, translate = function(styles, href, media) { var qs = styles.replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0; href = href.substring(0, href.lastIndexOf("/")); var repUrls = function(css) { return css.replace(respond.regex.urls, "$1" + href + "$2$3"); }, useMedia = !ql && media; if (href.length) { href += "/"; } if (useMedia) { ql = 1; } for (var i = 0; i < ql; i++) { var fullq, thisq, eachq, eql; if (useMedia) { fullq = media; rules.push(repUrls(styles)); } else { fullq = qs[i].match(respond.regex.findStyles) && RegExp.$1; rules.push(RegExp.$2 && repUrls(RegExp.$2)); } eachq = fullq.split(","); eql = eachq.length; for (var j = 0; j < eql; j++) { thisq = eachq[j]; mediastyles.push({ media: thisq.split("(")[0].match(respond.regex.only) && RegExp.$2 || "all", rules: rules.length - 1, hasquery: thisq.indexOf("(") > -1, minw: thisq.match(respond.regex.minw) && parseFloat(RegExp.$1) + (RegExp.$2 || ""), maxw: thisq.match(respond.regex.maxw) && parseFloat(RegExp.$1) + (RegExp.$2 || "") }); } } applyMedia(); }, makeRequests = function() { if (requestQueue.length) { var thisRequest = requestQueue.shift(); ajax(thisRequest.href, function(styles) { translate(styles, thisRequest.href, thisRequest.media); parsedSheets[thisRequest.href] = true; w.setTimeout(function() { makeRequests(); }, 0); }); } }, ripCSS = function() { for (var i = 0; i < links.length; i++) { var sheet = links[i], href = sheet.href, media = sheet.media, isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; if (!!href && isCSS && !parsedSheets[href]) { if (sheet.styleSheet && sheet.styleSheet.rawCssText) { translate(sheet.styleSheet.rawCssText, href, media); parsedSheets[href] = true; } else { if (!/^([a-zA-Z:]*\/\/)/.test(href) && !base || href.replace(RegExp.$1, "").split("/")[0] === w.location.host) { if (href.substring(0, 2) === "//") { href = w.location.protocol + href; } requestQueue.push({ href: href, media: media }); } } } } makeRequests(); }; ripCSS(); respond.update = ripCSS; respond.getEmValue = getEmValue; function callMedia() { applyMedia(true); } if (w.addEventListener) { w.addEventListener("resize", callMedia, false); } else if (w.attachEvent) { w.attachEvent("onresize", callMedia); } })(this); // Every if (!Array.prototype.every) { Array.prototype.every = function (block /*, thisp */) { var thisp = arguments[1]; for (var i = 0; i < this.length; i++) if (!block.call(thisp, this[i])) return false; return true; }; } // Filter if (!Array.prototype.filter) { Array.prototype.filter = function (block /*, thisp */) { var values = []; var thisp = arguments[1]; for (var i = 0; i < this.length; i++) if (block.call(thisp, this[i])) values.push(this[i]); return values; }; } // forEach if (!Array.prototype.forEach) { Array.prototype.forEach = function(block, thisObject) { var len = this.length >>> 0; for (var i = 0; i < len; i++) { if (i in this) { block.call(thisObject, this[i], i, this); } } }; } // indexOf if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (value /*, fromIndex */ ) { var length = this.length; if (!length) return -1; var i = arguments[1] || 0; if (i >= length) return -1; if (i < 0) i += length; for (; i < length; i++) { if (!Object.prototype.hasOwnProperty.call(this, i)) continue; if (value === this[i]) return i; } return -1; }; } // Array.isArray if (!Array.isArray) { Array.isArray = function(obj) { return Object.prototype.toString.call(obj) == "[object Array]"; }; } // lastIndexOf if (!Array.prototype.lastIndexOf) { Array.prototype.lastIndexOf = function (value /*, fromIndex */) { var length = this.length; if (!length) return -1; var i = arguments[1] || length; if (i < 0) i += length; i = Math.min(i, length - 1); for (; i >= 0; i--) { if (!Object.prototype.hasOwnProperty.call(this, i)) continue; if (value === this[i]) return i; } return -1; }; } // map // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map if (!Array.prototype.map) { Array.prototype.map = function(fun /*, thisp*/) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); var res = new Array(len); var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in this) res[i] = fun.call(thisp, this[i], i, this); } return res; }; } // reduce // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce if (!Array.prototype.reduce) { Array.prototype.reduce = function(fun /*, initial*/) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); // no value to return if no initial value and an empty array if (len === 0 && arguments.length === 1) throw new TypeError(); var i = 0, rv; if (arguments.length >= 2) { rv = arguments[1]; } else { do { if (i in this) { rv = this[i++]; break; } // if array contains no values, no initial value to return if (++i >= len) throw new TypeError(); } while (true); } for (; i < len; i++) { if (i in this) rv = fun.call(null, rv, this[i], i, this); } return rv; }; } // reduceRight // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight if (!Array.prototype.reduceRight) { Array.prototype.reduceRight = function(fun /*, initial*/) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); // no value to return if no initial value, empty array if (len === 0 && arguments.length === 1) throw new TypeError(); var i = len - 1, rv; if (arguments.length >= 2) { rv = arguments[1]; } else { do { if (i in this) { rv = this[i--]; break; } // if array contains no values, no initial value to return if (--i < 0) throw new TypeError(); } while (true); } for (; i >= 0; i--) { if (i in this) rv = fun.call(null, rv, this[i], i, this); } return rv; }; } // Every if (!Array.prototype.some) { Array.prototype.some = function (block /*, thisp */) { var thisp = arguments[1]; for (var i = 0; i < this.length; i++) if (block.call(thisp, this[i])) return true; return false; }; } // now if (!Date.now) { Date.now = function () { return new Date().getTime(); }; } // parse // based on work shared by Daniel Friesen (dantman) // http://gist.github.com/303249 if (isNaN(Date.parse("T00:00"))) { // XXX global assignment won't work in embeddings that use // an alternate object for the context. Date = (function(NativeDate) { // Date.length === 7 var Date = function(Y, M, D, h, m, s, ms) { var length = arguments.length; if (this instanceof NativeDate) { var date = length === 1 && String(Y) === Y ? // isString(Y) // We explicitly pass it through parse: new NativeDate(Date.parse(Y)) : // We have to manually make calls depending on argument // length here length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) : length >= 6 ? new NativeDate(Y, M, D, h, m, s) : length >= 5 ? new NativeDate(Y, M, D, h, m) : length >= 4 ? new NativeDate(Y, M, D, h) : length >= 3 ? new NativeDate(Y, M, D) : length >= 2 ? new NativeDate(Y, M) : length >= 1 ? new NativeDate(Y) : new NativeDate(); // Prevent mixups with unfixed Date object date.constructor = Date; return date; } return NativeDate.apply(this, arguments); }; // 15.9.1.15 Date Time String Format var isoDateExpression = new RegExp("^" + "(?:" + // optional year-month-day "(" + // year capture "(?:[+-]\\d\\d)?" + // 15.9.1.15.1 Extended years "\\d\\d\\d\\d" + // four-digit year ")" + "(?:-" + // optional month-day "(\\d\\d)" + // month capture "(?:-" + // optional day "(\\d\\d)" + // day capture ")?" + ")?" + ")?" + "(?:T" + // hour:minute:second.subsecond "(\\d\\d)" + // hour capture ":(\\d\\d)" + // minute capture "(?::" + // optional :second.subsecond "(\\d\\d)" + // second capture "(?:\\.(\\d\\d\\d))?" + // milisecond capture ")?" + ")?" + "(?:" + // time zone "Z|" + // UTC capture "([+-])(\\d\\d):(\\d\\d)" + // timezone offset // capture sign, hour, minute ")?" + "$"); // Copy any custom methods a 3rd party library may have added for (var key in NativeDate) Date[key] = NativeDate[key]; // Copy "native" methods explicitly; they may be non-enumerable Date.now = NativeDate.now; Date.UTC = NativeDate.UTC; Date.prototype = NativeDate.prototype; Date.prototype.constructor = Date; // Upgrade Date.parse to handle the ISO dates we use // TODO review specification to ascertain whether it is // necessary to implement partial ISO date strings. Date.parse = function(string) { var match = isoDateExpression.exec(string); if (match) { match.shift(); // kill match[0], the full match // recognize times without dates before normalizing the // numeric values, for later use var timeOnly = match[0] === undefined; // parse numerics for (var i = 0; i < 10; i++) { // skip + or - for the timezone offset if (i === 7) continue; // Note: parseInt would read 0-prefix numbers as // octal. Number constructor or unary + work better // here: match[i] = +(match[i] || (i < 3 ? 1 : 0)); // match[1] is the month. Months are 0-11 in JavaScript // Date objects, but 1-12 in ISO notation, so we // decrement. if (i === 1) match[i]--; } // if no year-month-date is provided, return a milisecond // quantity instead of a UTC date number value. if (timeOnly) return ((match[3] * 60 + match[4]) * 60 + match[5]) * 1000 + match[6]; // account for an explicit time zone offset if provided var offset = (match[8] * 60 + match[9]) * 60 * 1000; if (match[6] === "-") offset = -offset; return NativeDate.UTC.apply(this, match.slice(0, 7)) + offset; } return NativeDate.parse.apply(this, arguments); }; return Date; })(Date); } // toISOString // Format a Date object as a string according to a subset of the ISO-8601 standard. // Useful in Atom, among other things. if (!Date.prototype.toISOString) { Date.prototype.toISOString = function() { return ( this.getFullYear() + "-" + (this.getMonth() + 1) + "-" + this.getDate() + "T" + this.getHours() + ":" + this.getMinutes() + ":" + this.getSeconds() + "Z" ); }; } // toJSON if (!Date.prototype.toJSON) { Date.prototype.toJSON = function (key) { // This function provides a String representation of a Date object for // use by JSON.stringify (15.12.3). When the toJSON method is called // with argument key, the following steps are taken: // 1. Let O be the result of calling ToObject, giving it the this // value as its argument. // 2. Let tv be ToPrimitive(O, hint Number). // 3. If tv is a Number and is not finite, return null. // XXX // 4. Let toISO be the result of calling the [[Get]] internal method of // O with argument "toISOString". // 5. If IsCallable(toISO) is false, throw a TypeError exception. if (typeof this.toISOString != "function") throw new TypeError(); // 6. Return the result of calling the [[Call]] internal method of // toISO with O as the this value and an empty argument list. return this.toISOString(); // NOTE 1 The argument is ignored. // NOTE 2 The toJSON function is intentionally generic; it does not // require that its this value be a Date object. Therefore, it can be // transferred to other kinds of objects for use as a method. However, // it does require that any such object have a toISOString method. An // object is free to use the argument key to filter its // stringification. }; } // bind // http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf var slice = Array.prototype.slice; if (!Function.prototype.bind) { Function.prototype.bind = function (that) { // .length is 1 // 1. Let Target be the this value. var target = this; // 2. If IsCallable(Target) is false, throw a TypeError exception. // XXX this gets pretty close, for all intents and purposes, letting // some duck-types slide if (typeof target.apply != "function" || typeof target.call != "function") return new TypeError(); // 3. Let A be a new (possibly empty) internal list of all of the // argument values provided after thisArg (arg1, arg2 etc), in order. var args = slice.call(arguments); // 4. Let F be a new native ECMAScript object. // 9. Set the [[Prototype]] internal property of F to the standard // built-in Function prototype object as specified in 15.3.3.1. // 10. Set the [[Call]] internal property of F as described in // 15.3.4.5.1. // 11. Set the [[Construct]] internal property of F as described in // 15.3.4.5.2. // 12. Set the [[HasInstance]] internal property of F as described in // 15.3.4.5.3. // 13. The [[Scope]] internal property of F is unused and need not // exist. var bound = function () { if (this instanceof bound) { // 15.3.4.5.2 [[Construct]] // When the [[Construct]] internal method of a function object, // F that was created using the bind function is called with a // list of arguments ExtraArgs the following steps are taken: // 1. Let target be the value of F's [[TargetFunction]] // internal property. // 2. If target has no [[Construct]] internal method, a // TypeError exception is thrown. // 3. Let boundArgs be the value of F's [[BoundArgs]] internal // property. // 4. Let args be a new list containing the same values as the // list boundArgs in the same order followed by the same // values as the list ExtraArgs in the same order. var self = Object.create(target.prototype); target.apply(self, args.concat(slice.call(arguments))); return self; } else { // 15.3.4.5.1 [[Call]] // When the [[Call]] internal method of a function object, F, // which was created using the bind function is called with a // this value and a list of arguments ExtraArgs the following // steps are taken: // 1. Let boundArgs be the value of F's [[BoundArgs]] internal // property. // 2. Let boundThis be the value of F's [[BoundThis]] internal // property. // 3. Let target be the value of F's [[TargetFunction]] internal // property. // 4. Let args be a new list containing the same values as the list // boundArgs in the same order followed by the same values as // the list ExtraArgs in the same order. 5. Return the // result of calling the [[Call]] internal method of target // providing boundThis as the this value and providing args // as the arguments. // equiv: target.call(this, ...boundArgs, ...args) return target.call.apply( target, args.concat(slice.call(arguments)) ); } }; // 5. Set the [[TargetFunction]] internal property of F to Target. // extra: bound.bound = target; // 6. Set the [[BoundThis]] internal property of F to the value of // thisArg. // extra: bound.boundTo = that; // 7. Set the [[BoundArgs]] internal property of F to A. // extra: bound.boundArgs = args; bound.length = ( // 14. If the [[Class]] internal property of Target is "Function", then typeof target == "function" ? // a. Let L be the length property of Target minus the length of A. // b. Set the length own property of F to either 0 or L, whichever is larger. Math.max(target.length - args.length, 0) : // 15. Else set the length own property of F to 0. 0 ); // 16. The length own property of F is given attributes as specified in // 15.3.5.1. // TODO // 17. Set the [[Extensible]] internal property of F to true. // TODO // 18. Call the [[DefineOwnProperty]] internal method of F with // arguments "caller", PropertyDescriptor {[[Value]]: null, // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: // false}, and false. // TODO // 19. Call the [[DefineOwnProperty]] internal method of F with // arguments "arguments", PropertyDescriptor {[[Value]]: null, // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: // false}, and false. // TODO // NOTE Function objects created using Function.prototype.bind do not // have a prototype property. // XXX can't delete it in pure-js. return bound; }; } // create if (!Object.create) { Object.create = function(prototype, properties) { if (typeof prototype != "object" || prototype === null) throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'"); function Type() {} Type.prototype = prototype; var object = new Type(); if (typeof properties !== "undefined") Object.defineProperties(object, properties); return object; }; } // defineProperties if (!Object.defineProperties) { Object.defineProperties = function(object, properties) { for (var property in properties) { if (Object.prototype.hasOwnProperty.call(properties, property)) Object.defineProperty(object, property, properties[property]); } return object; }; } // defineProperty if (!Object.defineProperty) { Object.defineProperty = function(object, property, descriptor) { var has = Object.prototype.hasOwnProperty; if (typeof descriptor == "object" && object.__defineGetter__) { if (has.call(descriptor, "value")) { if (!object.__lookupGetter__(property) && !object.__lookupSetter__(property)) // data property defined and no pre-existing accessors object[property] = descriptor.value; if (has.call(descriptor, "get") || has.call(descriptor, "set")) // descriptor has a value property but accessor already exists throw new TypeError("Object doesn't support this action"); } // fail silently if "writable", "enumerable", or "configurable" // are requested but not supported /* // alternate approach: if ( // can't implement these features; allow false but not true !(has.call(descriptor, "writable") ? descriptor.writable : true) || !(has.call(descriptor, "enumerable") ? descriptor.enumerable : true) || !(has.call(descriptor, "configurable") ? descriptor.configurable : true) ) throw new RangeError( "This implementation of Object.defineProperty does not " + "support configurable, enumerable, or writable." ); */ else if (typeof descriptor.get == "function") object.__defineGetter__(property, descriptor.get); if (typeof descriptor.set == "function") object.__defineSetter__(property, descriptor.set); } return object; }; } if (!Object.freeze) { Object.freeze = function freeze(object) { if (Object(object) !== object) { throw new TypeError('Object.freeze can only be called on Objects.'); } // this is misleading and breaks feature-detection, but // allows "securable" code to "gracefully" degrade to working // but insecure code. return object; }; } // detect a Rhino bug and patch it try { Object.freeze(function () {}); } catch (exception) { Object.freeze = (function (freezeObject) { return function freeze(object) { if (typeof object === 'function') { return object; } else { return freezeObject(object); } }; }(Object.freeze)); } // getOwnPropertyDescriptor if (!Object.getOwnPropertyDescriptor) { Object.getOwnPropertyDescriptor = function (object) { return {}; // XXX }; } // getOwnPropertyNames if (!Object.getOwnPropertyNames) { Object.getOwnPropertyNames = function (object) { return Object.keys(object); }; } // getPrototypeOf if (!Object.getPrototypeOf) { Object.getPrototypeOf = function (object) { return object.__proto__; // or undefined if not available in this engine }; } // isExtensible if (!Object.isExtensible) { Object.isExtensible = function (object) { return true; }; } // isFrozen if (!Object.isFrozen) { Object.isFrozen = function (object) { return false; }; } // isSealed if (!Object.isSealed) { Object.isSealed = function (object) { return false; }; } // keys if (!Object.keys) { Object.keys = function (object) { var keys = []; for (var name in object) { if (Object.prototype.hasOwnProperty.call(object, name)) { keys.push(name); } } return keys; }; } // preventExtensions if (!Object.preventExtensions) { Object.preventExtensions = function (object) { return object; }; } // seal if (!Object.seal) { Object.seal = function (object) { return object; }; } // trim if (!String.prototype.trim) { // http://blog.stevenlevithan.com/archives/faster-trim-javascript var trimBeginRegexp = /^\s\s*/; var trimEndRegexp = /\s\s*$/; String.prototype.trim = function () { return String(this).replace(trimBeginRegexp, '').replace(trimEndRegexp, ''); }; } // addEventListener // removeEventListener // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener?redirectlocale=en-US&redirectslug=DOM%2FEventTarget.addEventListener#Compatibility (function() { if (!Element.prototype.addEventListener) { var eventListeners=[]; var addEventListener=function(type,listener /*, useCapture (will be ignored) */) { var self=this; var wrapper=function(e) { e.target=e.srcElement; e.currentTarget=self; if (typeof listener.handleEvent != "undefined") { listener.handleEvent(e); } else { listener.call(self,e); } }; if (type=="DOMContentLoaded") { var wrapper2=function(e) { if (document.readyState=="complete") { wrapper(e); } }; document.attachEvent("onreadystatechange",wrapper2); eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper2}); if (document.readyState=="complete") { var e=new Event(); e.srcElement=window; wrapper2(e); } } else { this.attachEvent("on"+type,wrapper); eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper}); } }; var removeEventListener=function(type,listener /*, useCapture (will be ignored) */) { var counter=0; while (counter= 0; --i) { if(n = nodes[i], 1 === n.nodeType) { return n; } } return null; } }); } })(); // Element.nextElementSibling (function () { "use strict"; if (!("nextElementSibling" in document.documentElement)) { Object.defineProperty(Element.prototype, "nextElementSibling", { get: function(){ var e = this.nextSibling; while(e && 1 !== e.nodeType) { e = e.nextSibling; } return e; } }); } })(); // event.pageX / event.pageY (function () { "use strict"; if(!window.MouseEvent && !("pageX" in Event.prototype)){ Object.defineProperty(Event.prototype, "pageX", { get: function(){ return this.clientX + window.pageXOffset; } }); Object.defineProperty(Event.prototype, "pageY", { get: function(){ return this.clientY + window.pageYOffset; } }); } })(); // window.pageXOffset / window.pageYOffset / window.scrollX / window.scrollY (function () { "use strict"; if(!("pageXOffset" in window)) { var x = function(){ return (document.documentElement || document.body.parentNode || document.body).scrollLeft; }, y = function(){ return (document.documentElement || document.body.parentNode || document.body).scrollTop; }; Object.defineProperty(window, "pageXOffset", {get: x}); Object.defineProperty(window, "pageYOffset", {get: y}); Object.defineProperty(window, "scrollX", {get: x}); Object.defineProperty(window, "scrollY", {get: y}); } })(); // IE8 preventDefault // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener?redirectlocale=en-US&redirectslug=DOM%2FEventTarget.addEventListener#Compatibility (function() { if (!Event.prototype.preventDefault) { Event.prototype.preventDefault=function() { this.returnValue=false; }; } })(); // Element.previousElementSibling (function () { "use strict"; if (!("previousElementSibling" in document.documentElement)) { Object.defineProperty(Element.prototype, "previousElementSibling", { get: function(){ var e = this.previousSibling; while(e && 1 !== e.nodeType) { e = e.previousSibling; } return e; } }); } })(); // IE8 stopPropagation // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener?redirectlocale=en-US&redirectslug=DOM%2FEventTarget.addEventListener#Compatibility (function() { if (!Event.prototype.stopPropagation) { Event.prototype.stopPropagation=function() { this.cancelBubble=true; }; } })(); // textContent (function(){ "use strict"; if(!("textContent" in Element.prototype)){ var interfaces = "Element Text HTMLDocument HTMLCommentElement".split(" "); var haveText = {3:1, 8:1, 4:1, 7:1}; var haveNull = {9:1, 10:1, 12:1}; var srcTags = {SCRIPT:1, STYLE:1}; for(var I, i = 0; i < 4; ++i){ I = window[interfaces[i]]; I && Object.defineProperty(I.prototype, "textContent", { get: function(){ return getText(this); }, set: function(input){ var type = this.nodeType; /** Text nodes: set nodeValue */ if(haveText[type]) { this.nodeValue = input; } else if(!haveNull[type]){ /** For everything that isn't a document, DOCTYPE or notation */ var name = this.nodeName; /** IE8 throws a runtime error trying to set the innerHTML of a