diff --git a/selectivizr2.js b/selectivizr2.js index 9013001..ed91e63 100755 --- a/selectivizr2.js +++ b/selectivizr2.js @@ -1,121 +1,121 @@ /* - selectivizr2 - selectivizr.com + selectivizr2 + selectivizr.com - Notes about this source - ----------------------- + 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: - ----------- + References: + ----------- * CSS Syntax : http://www.w3.org/TR/2003/WD-css3-syntax-20030813/#style * Selectors : http://www.w3.org/TR/css3-selectors/#selectors - * IE Compatability : http://msdn.microsoft.com/en-us/library/cc351024(VS.85).aspx + * IE Compatibility : http://msdn.microsoft.com/en-us/library/cc351024(VS.85).aspx * W3C Selector Tests : http://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 ============================ - - // Compatiable selector engines in order of CSS3 support. Note: '*' is - // a placholder 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) { + // 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; - } - } + 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 = []; @@ -123,429 +123,429 @@ } 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); - } - } + } + + // --[ 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);