From: Richard Gibson Date: Mon, 26 Nov 2012 08:20:43 +0000 (-0500) Subject: Fix #12959: Optimize library-wide patterns X-Git-Tag: 1.9.0b1~80 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0877d424e08d57e6f0d1da9a86289fb10d0ee136;p=jquery.git Fix #12959: Optimize library-wide patterns --- diff --git a/src/.jshintrc b/src/.jshintrc index 8a47fc233..daffbf5cc 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -8,6 +8,7 @@ "maxerr": 100, "newcap": false, "quotmark": "double", + "regexdash": true, "strict": true, "sub": true, "trailing": true, diff --git a/src/ajax.js b/src/ajax.js index 6b9b4def8..d91436dd6 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -13,7 +13,7 @@ var rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, - rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, + rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, // Keep a copy of the old load method _load = jQuery.fn.load, @@ -67,7 +67,7 @@ function addToPrefiltersOrTransports( structure ) { var dataType, i = 0, - dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ); + dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || []; if ( jQuery.isFunction( func ) ) { // For each dataType in the dataTypeExpression @@ -432,7 +432,7 @@ jQuery.extend({ s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); // Extract dataTypes list - s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""]; // A cross-domain request is in order when we have a protocol:host:port mismatch if ( s.crossDomain == null ) { diff --git a/src/attributes.js b/src/attributes.js index 919733cd8..ac4b3c866 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -1,9 +1,9 @@ var nodeHook, boolHook, fixSpecified, rclass = /[\t\r\n]/g, rreturn = /\r/g, - rtype = /^(?:button|input)$/i, - rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea|)$/i, + rtype = /^(?:input|button)$/i, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, getSetAttribute = jQuery.support.getSetAttribute; @@ -34,35 +34,36 @@ jQuery.fn.extend({ }, addClass: function( value ) { - var classNames, i, l, elem, - setClass, c, cl; + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { - jQuery( this ).addClass( value.call(this, j, this.className) ); + jQuery( this ).addClass( value.call( this, j, this.className ) ); }); } - if ( value && typeof value === "string" ) { - classNames = value.split( core_rspace ); + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; - for ( i = 0, l = this.length; i < l; i++ ) { + for ( ; i < len; i++ ) { elem = this[ i ]; if ( elem.nodeType === 1 ) { - if ( !elem.className && classNames.length === 1 ) { - elem.className = value; - - } else { - setClass = " " + elem.className + " "; - - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) { - setClass += classNames[ c ] + " "; - } + cur = elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " "; + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; } - elem.className = jQuery.trim( setClass ); } + elem.className = jQuery.trim( cur ); + } } } @@ -71,30 +72,34 @@ jQuery.fn.extend({ }, removeClass: function( value ) { - var removes, className, elem, c, cl, i, l; + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { - jQuery( this ).removeClass( value.call(this, j, this.className) ); + jQuery( this ).removeClass( value.call( this, j, this.className ) ); }); } - if ( (value && typeof value === "string") || !arguments.length ) { - removes = ( value || "" ).split( core_rspace ); + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; - for ( i = 0, l = this.length; i < l; i++ ) { + for ( ; i < len; i++ ) { elem = this[ i ]; - if ( elem.nodeType === 1 && elem.className ) { - - className = (" " + elem.className + " ").replace( rclass, " " ); - - // loop over each item in the removal list - for ( c = 0, cl = removes.length; c < cl; c++ ) { - // Remove until there is nothing to remove, - while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) { - className = className.replace( " " + removes[ c ] + " " , " " ); + if ( elem.nodeType === 1 ) { + // This expression is here for better compressibility (see addClass) + cur = elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " "; + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); } } - elem.className = value ? jQuery.trim( className ) : ""; + elem.className = value ? jQuery.trim( cur ) : ""; } } } @@ -119,7 +124,7 @@ jQuery.fn.extend({ i = 0, self = jQuery( this ), state = stateVal, - classNames = value.split( core_rspace ); + classNames = value.match( core_rnotwhite ) || []; while ( (className = classNames[ i++ ]) ) { // check each className given, space separated list @@ -331,31 +336,25 @@ jQuery.extend({ }, removeAttr: function( elem, value ) { - var propName, attrNames, name, isBool, - i = 0; - - if ( value && elem.nodeType === 1 ) { - - attrNames = value.split( core_rspace ); - - for ( ; i < attrNames.length; i++ ) { - name = attrNames[ i ]; + var name, propName, isBool, + i = 0, + attrNames = value && value.match( core_rnotwhite ); - if ( name ) { - propName = jQuery.propFix[ name ] || name; - isBool = rboolean.test( name ); + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); - // See #9699 for explanation of this approach (setting first, then removal) - // Do not do this for boolean attributes (see #10870) - if ( !isBool ) { - jQuery.attr( elem, name, "" ); - } - elem.removeAttribute( getSetAttribute ? name : propName ); + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); - // Set corresponding property to false for boolean attributes - if ( isBool && propName in elem ) { - elem[ propName ] = false; - } + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; } } } diff --git a/src/callbacks.js b/src/callbacks.js index 6fda47f00..58f1402a8 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -4,7 +4,7 @@ var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { var object = optionsCache[ options ] = {}; - jQuery.each( options.split( core_rspace ), function( _, flag ) { + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; diff --git a/src/core.js b/src/core.js index 9c4988869..eee810070 100644 --- a/src/core.js +++ b/src/core.js @@ -15,14 +15,22 @@ var // Map over the $ in case of overwrite _$ = window.$, + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "@VERSION", + // Save a reference to some core methods - core_concat = Array.prototype.concat, - core_push = Array.prototype.push, - core_slice = Array.prototype.slice, - core_indexOf = Array.prototype.indexOf, - core_toString = Object.prototype.toString, - core_hasOwn = Object.prototype.hasOwnProperty, - core_trim = String.prototype.trim, + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, // Define a local copy of jQuery jQuery = function( selector, context ) { @@ -31,21 +39,17 @@ var }, // Used for matching numbers - core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source, + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, - // Used for detecting and trimming whitespace - core_rnotwhite = /\S/, - core_rspace = /\s+/, - - // List of deleted data cache ids, so we can reuse them - core_deletedIds = [], + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) - rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*|#([\w-]*))$/, // Match a standalone tag rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, @@ -54,7 +58,7 @@ var rvalidchars = /^[\],:{}\s]*$/, rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, - rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, @@ -62,7 +66,7 @@ var // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); + return letter.toUpperCase(); }, // The ready event handler and self cleanup method @@ -76,12 +80,12 @@ var document.detachEvent( "onreadystatechange", DOMContentLoaded ); jQuery.ready(); } - }, - - // [[Class]] -> type pairs - class2type = {}; + }; jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + constructor: jQuery, init: function( selector, context, rootjQuery ) { var match, elem; @@ -188,9 +192,6 @@ jQuery.fn = jQuery.prototype = { // Start with an empty selector selector: "", - // The current version of jQuery being used - jquery: "@VERSION", - // The default length of a jQuery object is 0 length: 0, @@ -560,7 +561,7 @@ jQuery.extend({ // Workarounds based on findings by Jim Driscoll // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context globalEval: function( data ) { - if ( data && core_rnotwhite.test( data ) ) { + if ( data && jQuery.trim( data ) ) { // We use execScript on Internet Explorer // We use an anonymous function so that context is window // rather than jQuery in Firefox diff --git a/src/css.js b/src/css.js index eb0aed639..334e31a92 100644 --- a/src/css.js +++ b/src/css.js @@ -8,7 +8,7 @@ var curCSS, iframe, iframeDoc, rmargin = /^margin/, rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), - rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), elemdisplay = { BODY: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, diff --git a/src/data.js b/src/data.js index a61042709..d5a25ff6c 100644 --- a/src/data.js +++ b/src/data.js @@ -188,7 +188,7 @@ jQuery.extend({ // Unique for each copy of jQuery on the page // Non-digits removed to match rinlinejQuery - expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), // The following elements throw uncatchable exceptions if you // attempt to add expando properties to them. diff --git a/src/effects.js b/src/effects.js index d5ff7464c..68fc60101 100644 --- a/src/effects.js +++ b/src/effects.js @@ -1,6 +1,6 @@ var fxNow, timerId, rfxtypes = /^(?:toggle|show|hide)$/, - rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), + rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), rrun = /queueHooks$/, animationPrefilters = [ defaultPrefilter ], tweeners = { diff --git a/src/event.js b/src/event.js index a40bc0859..565f1834a 100644 --- a/src/event.js +++ b/src/event.js @@ -1,9 +1,8 @@ -var rformElems = /^(?:textarea|input|select)$/i, - rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/, - reventTypes = /\S+/g, +var rformElems = /^(?:input|select|textarea)$/i, rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; /* * Helper functions for managing events -- not part of the public interface. @@ -13,8 +12,9 @@ jQuery.event = { add: function( elem, types, handler, data, selector ) { var elemData, eventHandle, events, - t, tns, type, namespaces, handleObj, - handleObjIn, handlers, special; + tns, type, namespaces, handleObj, + handleObjIn, handlers, special, + t = 0; // Don't attach events to noData or text/comment nodes (allow plain objects tho) if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { @@ -53,8 +53,8 @@ jQuery.event = { // Handle multiple events separated by a space // jQuery(...).bind("mouseover mouseout", fn); - types = (types || "").match( reventTypes ) || [""]; - for ( t = 0; t < types.length; t++ ) { + types = ( types || "" ).match( core_rnotwhite ) || [""]; + for ( ; t < types.length; t++ ) { tns = rtypenamespace.exec( types[t] ) || []; type = tns[1]; @@ -127,8 +127,9 @@ jQuery.event = { // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { - var t, tns, type, origType, namespaces, origCount, + var tns, type, origType, namespaces, origCount, j, events, special, eventType, handleObj, + t = 0, elemData = jQuery.hasData( elem ) && jQuery._data( elem ); if ( !elemData || !(events = elemData.events) ) { @@ -136,8 +137,8 @@ jQuery.event = { } // Once for each type.namespace in types; type may be omitted - types = (types || "").match( reventTypes ) || [""]; - for ( t = 0; t < types.length; t++ ) { + types = ( types || "" ).match( core_rnotwhite ) || [""]; + for ( ; t < types.length; t++ ) { tns = rtypenamespace.exec( types[t] ) || []; type = origType = tns[1]; namespaces = tns[2]; diff --git a/src/manipulation.js b/src/manipulation.js index 509daa6e5..6521e885e 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -15,20 +15,20 @@ function createSafeFragment( document ) { var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), rleadingWhitespace = /^\s+/, rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, rtagName = /<([\w:]+)/, rtbody = /]", "i"), - rcheckableType = /^(?:checkbox|radio)$/, + rnocache = /<(?:script|style|object|embed|applet|option)/i, + manipulation_rcheckableType = /^(?:checkbox|radio)$/i, // checked="checked" or checked rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rscriptType = /^$|\/(?:java|ecma)script/i, rscriptTypeMasked = /^true\/(.*)/, - rcleanScript = /^\s*\s*$/g, + rcleanScript = /^\s*\s*$/g, wrapMap = { option: [ 1, "" ], legend: [ 1, "
", "
" ], @@ -485,7 +485,7 @@ function cloneFixAttributes( src, dest ) { dest.innerHTML = src.innerHTML; } - } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { // IE6-8 fails to persist the checked state of a cloned checkbox // or radio button. Worse, IE6-7 fail to give the cloned element // a checked appearance if the defaultChecked value isn't also set @@ -611,7 +611,7 @@ function getAll( context, tag ) { // Used in clean, fixes the defaultChecked property function fixDefaultChecked( elem ) { - if ( rcheckableType.test( elem.type ) ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { elem.defaultChecked = elem.checked; } } diff --git a/src/serialize.js b/src/serialize.js index 0e1624926..6e8353d59 100644 --- a/src/serialize.js +++ b/src/serialize.js @@ -1,9 +1,8 @@ var r20 = /%20/g, rbracket = /\[\]$/, rCRLF = /\r?\n/g, - rcheckTypes = /^(?:checkbox|radio)$/i, rsubmitterTypes = /^(?:submit|button|image|reset)$/i, - rsubmittable = /^(?:select|textarea|input|keygen)/i; + rsubmittable = /^(?:input|select|textarea|keygen)/i; jQuery.fn.extend({ serialize: function() { @@ -20,7 +19,7 @@ jQuery.fn.extend({ // Use .is(":disabled") so that fieldset[disabled] works return this.name && !jQuery( this ).is( ":disabled" ) && rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckTypes.test( type ) ); + ( this.checked || !manipulation_rcheckableType.test( type ) ); }) .map(function( i, elem ){ var val = jQuery( this ).val(); diff --git a/src/sizzle b/src/sizzle index c488e18fa..ac765b61b 160000 --- a/src/sizzle +++ b/src/sizzle @@ -1 +1 @@ -Subproject commit c488e18facdf50de2d272a052d1e0f663ec19fd4 +Subproject commit ac765b61bd4fdc6e76f459f18e1f99c9ed6e28da