From e332dcfab968e26d529af34552265a238f9149df Mon Sep 17 00:00:00 2001 From: Guillermo Rauch Date: Wed, 23 Nov 2011 14:27:37 -0800 Subject: [PATCH 01/32] Allow for multiple `.antiscroll` calls on the same elements. --- antiscroll.js | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 7f46815..e7478b6 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -7,9 +7,11 @@ $.fn.antiscroll = function (options) { return this.each(function () { - if (!$(this).data('antiscroll')) { - $(this).data('antiscroll', new $.Antiscroll(this, options)); + if ($(this).data('antiscroll')) { + $(this).data('antiscroll').destroy(); } + + $(this).data('antiscroll', new $.Antiscroll(this, options)); }); }; @@ -46,6 +48,23 @@ } } + /** + * Cleans up. + * + * @return {Antiscroll} for chaining + * @api public + */ + + Antiscroll.prototype.destroy = function () { + if (this.horizontal) { + this.horizontal.destroy(); + } + if (this.vertical) { + this.vertical.destroy(); + } + return this; + }; + /** * Scrolbar constructor. * @@ -81,6 +100,18 @@ this.hiding = setTimeout($.proxy(this, 'hide'), 3000); }; + /** + * Cleans up. + * + * @return {Scrollbar} for chaining + * @api public + */ + + Scrollbar.prototype.destroy = function () { + this.el.remove(); + return this; + }; + /** * Called upon mouseenter. * From 8dded15cc2100c9e70c8b4f402f7878736381ce0 Mon Sep 17 00:00:00 2001 From: Guillermo Rauch Date: Wed, 23 Nov 2011 15:39:52 -0800 Subject: [PATCH 02/32] If scrolled artificially show & hide after 1500ms --- antiscroll.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/antiscroll.js b/antiscroll.js index e7478b6..3507dea 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -146,7 +146,11 @@ Scrollbar.prototype.scroll = function () { if (!this.shown) { this.show(); + if (!this.enter && !this.dragging) { + this.hiding = setTimeout($.proxy(this, 'hide'), 1500); + } } + this.update(); }; From 54bfe57ba298d69f2267da4f618a95041b38d6d9 Mon Sep 17 00:00:00 2001 From: Pierre Bertet Date: Thu, 24 Nov 2011 17:52:18 +0100 Subject: [PATCH 03/32] Added a rebuild method --- antiscroll.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 3507dea..bfc7fab 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -34,6 +34,7 @@ this.options = opts || {}; this.padding = undefined == this.options.padding ? 2 : this.options.padding; this.inner = this.el.find('.antiscroll-inner'); + this.originalStyles = this.inner.attr('style') || ''; this.inner.css({ 'width': '+=' + scrollbarSize() , 'height': '+=' + scrollbarSize() @@ -66,7 +67,21 @@ }; /** - * Scrolbar constructor. + * Rebuild Antiscroll. + * + * @return {Antiscroll} for chaining + * @api public + */ + + Antiscroll.prototype.rebuild = function () { + this.destroy(); + this.inner.attr('style', this.originalStyles); + Antiscroll.call(this, this.el, this.options); + return this; + }; + + /** + * Scrollbar constructor. * * @param {Element|jQuery} element * @api public @@ -269,7 +284,7 @@ // minimum top is 0, maximum is the track height var y = Math.min(Math.max(pos, 0), trackWidth - barWidth) - innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) + innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) * y / (trackWidth - barWidth) }; @@ -280,8 +295,8 @@ */ Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { - if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || - (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() + if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || + (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() == this.innerEl.scrollWidth))) { ev.preventDefault(); return false; @@ -337,7 +352,7 @@ // minimum top is 0, maximum is the track height var y = Math.min(Math.max(pos, 0), trackHeight - barHeight) - innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) + innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) * y / (trackHeight - barHeight) }; @@ -348,8 +363,8 @@ */ Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { - if ((y > 0 && 0 == this.innerEl.scrollTop) || - (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() + if ((y > 0 && 0 == this.innerEl.scrollTop) || + (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() == this.innerEl.scrollHeight))) { ev.preventDefault(); return false; From 95fbeea5c1cc0b066fad21de9ba8f8a0429f4cdd Mon Sep 17 00:00:00 2001 From: Pierre Bertet Date: Mon, 28 Nov 2011 00:57:56 +0100 Subject: [PATCH 04/32] Removed style caching in rebuild() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Style caching was a precaution, in order to reset the styles to their previous values before calling the Antiscroll constructor. Now, this will not work anymore: var containers = $('.container'); containers.find('.antiscroll-inner').css('background', 'red'); // The background is red containers.antiscroll(); containers.each(function(){ $(this).data('antiscroll').rebuild(); }); // The red background is removed But if we assume that: - It is an expensive operation -
is supposed to be dedicated to antiscroll - Styles can still be added between the calls to antiscroll() and rebuild(), and will not be cached It’s relatively safe to remove it. --- antiscroll.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index bfc7fab..248de46 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -34,7 +34,6 @@ this.options = opts || {}; this.padding = undefined == this.options.padding ? 2 : this.options.padding; this.inner = this.el.find('.antiscroll-inner'); - this.originalStyles = this.inner.attr('style') || ''; this.inner.css({ 'width': '+=' + scrollbarSize() , 'height': '+=' + scrollbarSize() @@ -75,7 +74,7 @@ Antiscroll.prototype.rebuild = function () { this.destroy(); - this.inner.attr('style', this.originalStyles); + this.inner.attr('style', ''); Antiscroll.call(this, this.el, this.options); return this; }; From 46ec65b0889212e3fccec3474c2372fdb2bf457c Mon Sep 17 00:00:00 2001 From: Tristan Dunn Date: Mon, 28 Nov 2011 13:00:15 -0600 Subject: [PATCH 05/32] Add initialDisplay option. Fixes #8. --- antiscroll.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 3507dea..a60853a 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -95,9 +95,12 @@ this.pane.inner.bind('mousewheel', $.proxy(this, 'mousewheel')); // show - var self = this; - this.show(); - this.hiding = setTimeout($.proxy(this, 'hide'), 3000); + var initialDisplay = this.pane.options.initialDisplay; + + if (initialDisplay !== false) { + this.show(); + this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); + } }; /** From a1b14024b016b246624c8d451dacd2b1bf8b6148 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Sat, 7 Jan 2012 13:12:33 +0100 Subject: [PATCH 06/32] - added a refresh method to the api - updated demo to reflect changes - updated jquery to 1.7 because of this bug http://stackoverflow.com/questions/7825448/webkit-issues-with-event-layerx-and-event-layery --- antiscroll.js | 25 +- deps/jquery.js | 2908 ++++++++++++++++++++++++++---------------------- index.html | 66 +- 3 files changed, 1662 insertions(+), 1337 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index a60853a..ed267b2 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -39,14 +39,33 @@ , 'height': '+=' + scrollbarSize() }); - if (this.inner.get(0).scrollWidth > this.el.width()) { + this.refresh(); + }; + + /** + * refresh scrollbars + * + * @api public + */ + Antiscroll.prototype.refresh = function() { + var needHScroll = this.inner.get(0).scrollWidth > this.el.width() + , needVScroll = this.inner.get(0).scrollHeight > this.el.height(); + + if (!this.horizontal && needHScroll) { this.horizontal = new Scrollbar.Horizontal(this); + } else if (this.horizontal && !needHScroll) { + this.horizontal.destroy(); + this.horizontal = null } - if (this.inner.get(0).scrollHeight > this.el.height()) { + if (!this.vertical && needVScroll) { this.vertical = new Scrollbar.Vertical(this); + } else if (this.vertical && !needVScroll) { + this.vertical.destroy(); + this.vertical = null } - } + + }; /** * Cleans up. diff --git a/deps/jquery.js b/deps/jquery.js index 11e6d06..478740e 100644 --- a/deps/jquery.js +++ b/deps/jquery.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.6.4 + * jQuery JavaScript Library v1.7 * http://jquery.com/ * * Copyright 2011, John Resig @@ -11,7 +11,7 @@ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * - * Date: Mon Sep 12 18:54:48 2011 -0400 + * Date: Thu Nov 3 16:18:21 2011 -0400 */ (function( window, undefined ) { @@ -140,7 +140,7 @@ jQuery.fn = jQuery.prototype = { // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; - doc = (context ? context.ownerDocument || context : document); + doc = ( context ? context.ownerDocument || context : document ); // If a single string is passed in and it's a single tag // just do a createElement and skip the rest @@ -157,7 +157,7 @@ jQuery.fn = jQuery.prototype = { } else { ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; } return jQuery.merge( this, selector ); @@ -187,7 +187,7 @@ jQuery.fn = jQuery.prototype = { // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { - return (context || rootjQuery).find( selector ); + return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) @@ -201,7 +201,7 @@ jQuery.fn = jQuery.prototype = { return rootjQuery.ready( selector ); } - if (selector.selector !== undefined) { + if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } @@ -213,7 +213,7 @@ jQuery.fn = jQuery.prototype = { selector: "", // The current version of jQuery being used - jquery: "1.6.4", + jquery: "1.7", // The default length of a jQuery object is 0 length: 0, @@ -258,7 +258,7 @@ jQuery.fn = jQuery.prototype = { ret.context = this.context; if ( name === "find" ) { - ret.selector = this.selector + (this.selector ? " " : "") + selector; + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; } else if ( name ) { ret.selector = this.selector + "." + name + "(" + selector + ")"; } @@ -279,7 +279,7 @@ jQuery.fn = jQuery.prototype = { jQuery.bindReady(); // Add the callback - readyList.done( fn ); + readyList.add( fn ); return this; }, @@ -434,7 +434,7 @@ jQuery.extend({ } // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); + readyList.fireWith( document, [ jQuery ] ); // Trigger any bound ready events if ( jQuery.fn.trigger ) { @@ -448,7 +448,7 @@ jQuery.extend({ return; } - readyList = jQuery._Deferred(); + readyList = jQuery.Callbacks( "once memory" ); // Catch cases where $(document).ready() is called after the // browser event has already occurred. @@ -504,8 +504,8 @@ jQuery.extend({ return obj && typeof obj === "object" && "setInterval" in obj; }, - isNaN: function( obj ) { - return obj == null || !rdigit.test( obj ) || isNaN( obj ); + isNumeric: function( obj ) { + return obj != null && rdigit.test( obj ) && !isNaN( obj ); }, type: function( obj ) { @@ -573,7 +573,7 @@ jQuery.extend({ .replace( rvalidtokens, "]" ) .replace( rvalidbraces, "")) ) { - return (new Function( "return " + data ))(); + return ( new Function( "return " + data ) )(); } jQuery.error( "Invalid JSON: " + data ); @@ -703,18 +703,22 @@ jQuery.extend({ return ret; }, - inArray: function( elem, array ) { - if ( !array ) { - return -1; - } + inArray: function( elem, array, i ) { + var len; - if ( indexOf ) { - return indexOf.call( array, elem ); - } + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } } } @@ -850,7 +854,7 @@ jQuery.extend({ }, now: function() { - return (new Date()).getTime(); + return ( new Date() ).getTime(); }, // Use of jQuery.browser is frowned upon. @@ -952,193 +956,378 @@ function doScrollCheck() { jQuery.ready(); } +// Expose jQuery as an AMD module, but only for AMD loaders that +// understand the issues with loading multiple versions of jQuery +// in a page that all might call define(). The loader will indicate +// they have special allowances for multiple jQuery versions by +// specifying define.amd.jQuery = true. Register as a named module, +// since jQuery can be concatenated with other files that may use define, +// but not use a proper concatenation script that understands anonymous +// AMD modules. A named AMD is safest and most robust way to register. +// Lowercase jquery is used because AMD module names are derived from +// file names, and jQuery is normally delivered in a lowercase file name. +if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); +} + return jQuery; })(); -var // Promise methods - promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ), - // Static reference to slice - sliceDeferred = [].slice; +// String to Object flags format cache +var flagsCache = {}; -jQuery.extend({ - // Create a simple deferred (one callbacks list) - _Deferred: function() { - var // callbacks list - callbacks = [], - // stored [ context , args ] - fired, - // to avoid firing when already doing so - firing, - // flag to know if the deferred has been cancelled - cancelled, - // the deferred itself - deferred = { - - // done( f1, f2, ...) - done: function() { - if ( !cancelled ) { - var args = arguments, - i, - length, - elem, - type, - _fired; - if ( fired ) { - _fired = fired; - fired = 0; - } - for ( i = 0, length = args.length; i < length; i++ ) { - elem = args[ i ]; - type = jQuery.type( elem ); - if ( type === "array" ) { - deferred.done.apply( deferred, elem ); - } else if ( type === "function" ) { - callbacks.push( elem ); +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } } } - if ( _fired ) { - deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); - } } - return this; - }, - - // resolve with given context and args - resolveWith: function( context, args ) { - if ( !cancelled && !fired && !firing ) { - // make sure args are available (#8421) - args = args || []; - firing = 1; - try { - while( callbacks[ 0 ] ) { - callbacks.shift().apply( context, args ); - } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; } - finally { - fired = [ context, args ]; - firing = 0; + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); } - return this; - }, + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!memory; + } + }; - // resolve with this as context and given arguments - resolve: function() { - deferred.resolveWith( this, arguments ); - return this; - }, + return self; +}; - // Has this deferred been resolved? - isResolved: function() { - return !!( firing || fired ); - }, - // Cancel - cancel: function() { - cancelled = 1; - callbacks = []; - return this; - } - }; - return deferred; - }, - // Full fledged deferred (two callbacks list) +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + Deferred: function( func ) { - var deferred = jQuery._Deferred(), - failDeferred = jQuery._Deferred(), - promise; - // Add errorDeferred methods, then and promise - jQuery.extend( deferred, { - then: function( doneCallbacks, failCallbacks ) { - deferred.done( doneCallbacks ).fail( failCallbacks ); - return this; - }, - always: function() { - return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList }, - fail: failDeferred.done, - rejectWith: failDeferred.resolveWith, - reject: failDeferred.resolve, - isRejected: failDeferred.isResolved, - pipe: function( fnDone, fnFail ) { - return jQuery.Deferred(function( newDefer ) { - jQuery.each( { - done: [ fnDone, "resolve" ], - fail: [ fnFail, "reject" ] - }, function( handler, data ) { - var fn = data[ 0 ], - action = data[ 1 ], - returned; - if ( jQuery.isFunction( fn ) ) { - deferred[ handler ](function() { - returned = fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise().then( newDefer.resolve, newDefer.reject ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); - } - }); - } else { - deferred[ handler ]( newDefer[ action ] ); + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + return deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; } - }); - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - if ( obj == null ) { - if ( promise ) { - return promise; } - promise = obj = {}; + return obj; } - var i = promiseMethods.length; - while( i-- ) { - obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; - } - return obj; - } - }); - // Make sure only one callback list will be used - deferred.done( failDeferred.cancel ).fail( deferred.cancel ); - // Unexpose cancel - delete deferred.cancel; + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + // Call given func if any if ( func ) { func.call( deferred, deferred ); } + + // All done! return deferred; }, // Deferred helper when: function( firstParam ) { - var args = arguments, + var args = sliceDeferred.call( arguments, 0 ), i = 0, length = args.length, + pValues = new Array( length ), count = length, + pCount = length, deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? firstParam : - jQuery.Deferred(); + jQuery.Deferred(), + promise = deferred.promise(); function resolveFunc( i ) { return function( value ) { args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; if ( !( --count ) ) { - // Strange bug in FF4: - // Values changed onto the arguments object sometimes end up as undefined values - // outside the $.when method. Cloning the object into a fresh array solves the issue - deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); + deferred.resolveWith( deferred, args ); } }; } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } if ( length > 1 ) { - for( ; i < length; i++ ) { - if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { - args[ i ].promise().then( resolveFunc(i), deferred.reject ); + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); } else { --count; } @@ -1149,12 +1338,13 @@ jQuery.extend({ } else if ( deferred !== firstParam ) { deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); } - return deferred.promise(); + return promise; } }); + jQuery.support = (function() { var div = document.createElement( "div" ), @@ -1179,7 +1369,7 @@ jQuery.support = (function() { // Preliminary tests div.setAttribute("className", "t"); - div.innerHTML = "
a"; + div.innerHTML = "
a"; all = div.getElementsByTagName( "*" ); @@ -1218,12 +1408,15 @@ jQuery.support = (function() { // Make sure that element opacity exists // (IE uses filter instead) // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.55$/.test( a.style.opacity ), + opacity: /^0.55/.test( a.style.opacity ), // Verify style float existence // (IE uses styleFloat instead of cssFloat) cssFloat: !!a.style.cssFloat, + // Make sure unknown elements (like HTML5 elems) are handled appropriately + unknownElems: !!div.getElementsByTagName( "nav" ).length, + // Make sure that if no value is specified for a checkbox // that it defaults to "on". // (WebKit defaults to "" instead) @@ -1236,6 +1429,9 @@ jQuery.support = (function() { // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) getSetAttribute: div.className !== "t", + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + // Will be defined later submitBubbles: true, changeBubbles: true, @@ -1273,7 +1469,7 @@ jQuery.support = (function() { div.cloneNode( true ).fireEvent( "onclick" ); } - // Check if a radio maintains it's value + // Check if a radio maintains its value // after being appended to the DOM input = document.createElement("input"); input.value = "t"; @@ -1283,7 +1479,7 @@ jQuery.support = (function() { input.setAttribute("checked", "checked"); div.appendChild( input ); fragment = document.createDocumentFragment(); - fragment.appendChild( div.firstChild ); + fragment.appendChild( div.lastChild ); // WebKit doesn't clone checked state correctly in fragments support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; @@ -1293,7 +1489,11 @@ jQuery.support = (function() { // Figure out if the W3C box model works as expected div.style.width = div.style.paddingLeft = "1px"; - body = document.getElementsByTagName( "body" )[ 0 ]; + // We don't want to do body-related feature tests on frameset + // documents, which lack a body. So we use + // document.getElementsByTagName("body")[0], which is undefined in + // frameset documents, while document.body isn’t. (7398) + body = document.getElementsByTagName("body")[ 0 ]; // We use our own, invisible, body unless the body is already present // in which case we use a div (#9239) testElement = document.createElement( body ? "div" : "body" ); @@ -1308,8 +1508,8 @@ jQuery.support = (function() { if ( body ) { jQuery.extend( testElementStyle, { position: "absolute", - left: "-1000px", - top: "-1000px" + left: "-999px", + top: "-999px" }); } for ( i in testElementStyle ) { @@ -1375,12 +1575,8 @@ jQuery.support = (function() { ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; } - // Remove the body element we added - testElement.innerHTML = ""; - testElementParent.removeChild( testElement ); - // Technique from Juriy Zaytsev - // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ // We only care about the case where non-standard event systems // are used, namely in IE. Short-circuiting here helps us to // avoid an eval call (in setAttribute) which can cause CSP @@ -1401,6 +1597,67 @@ jQuery.support = (function() { } } + // Run fixed position tests at doc ready to avoid a crash + // related to the invisible body in IE8 + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + conMarginTop = 1, + ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;", + vb = "visibility:hidden;border:0;", + style = "style='" + ptlm + "border:5px solid #000;padding:0;'", + html = "
" + + "" + + "
"; + + // Reconstruct a container + body = document.getElementsByTagName("body")[0]; + if ( !body ) { + // Return for frameset docs that don't have a body + // These tests cannot be done + return; + } + + container = document.createElement("div"); + container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct a test element + testElement = document.createElement("div"); + testElement.style.cssText = ptlm + vb; + + testElement.innerHTML = html; + container.appendChild( testElement ); + outer = testElement.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + body.removeChild( container ); + testElement = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); + // Null connected elements to avoid leaks in IE testElement = fragment = select = opt = body = marginDiv = div = input = null; @@ -1437,7 +1694,6 @@ jQuery.extend({ hasData: function( elem ) { elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); }, @@ -1446,7 +1702,7 @@ jQuery.extend({ return; } - var thisCache, ret, + var privateCache, thisCache, ret, internalKey = jQuery.expando, getByName = typeof name === "string", @@ -1460,11 +1716,12 @@ jQuery.extend({ // Only defining an ID for JS objects if its cache already exists allows // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando, + isEvents = name === "events"; // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all - if ( (!id || (pvt && id && (cache[ id ] && !cache[ id ][ internalKey ]))) && getByName && data === undefined ) { + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { return; } @@ -1481,9 +1738,8 @@ jQuery.extend({ if ( !cache[ id ] ) { cache[ id ] = {}; - // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery - // metadata on plain JS objects when the object is serialized using - // JSON.stringify + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify if ( !isNode ) { cache[ id ].toJSON = jQuery.noop; } @@ -1493,34 +1749,33 @@ jQuery.extend({ // shallow copied over onto the existing cache if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { - cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); + cache[ id ] = jQuery.extend( cache[ id ], name ); } else { - cache[ id ] = jQuery.extend(cache[ id ], name); + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); } } - thisCache = cache[ id ]; + privateCache = thisCache = cache[ id ]; - // Internal jQuery data is stored in a separate object inside the object's data + // jQuery data() is stored in a separate object inside the object's internal data // cache in order to avoid key collisions between internal data and user-defined - // data - if ( pvt ) { - if ( !thisCache[ internalKey ] ) { - thisCache[ internalKey ] = {}; + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; } - thisCache = thisCache[ internalKey ]; + thisCache = thisCache.data; } if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } - // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should - // not attempt to inspect the internal events object using jQuery.data, as this - // internal data object is undocumented and subject to change. - if ( name === "events" && !thisCache[name] ) { - return thisCache[ internalKey ] && thisCache[ internalKey ].events; + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; } // Check for both converted-to-camel and non-converted data property names @@ -1548,7 +1803,7 @@ jQuery.extend({ return; } - var thisCache, + var thisCache, i, l, // Reference to internal data cache key internalKey = jQuery.expando, @@ -1569,28 +1824,41 @@ jQuery.extend({ if ( name ) { - thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + thisCache = pvt ? cache[ id ] : cache[ id ].data; if ( thisCache ) { - // Support interoperable removal of hyphenated or camelcased keys - if ( !thisCache[ name ] ) { + // Support space separated names + if ( jQuery.isArray( name ) ) { + name = name; + } else if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } } - delete thisCache[ name ]; + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } // If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed - if ( !isEmptyDataObject(thisCache) ) { + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { return; } } } // See jQuery.data for more information - if ( pvt ) { - delete cache[ id ][ internalKey ]; + if ( !pvt ) { + delete cache[ id ].data; // Don't destroy the parent cache unless the internal data object // had been the only thing left in it @@ -1599,8 +1867,6 @@ jQuery.extend({ } } - var internalCache = cache[ id ][ internalKey ]; - // Browsers that fail expando deletion also refuse to delete expandos on // the window, but it will allow it on all other JS objects; other browsers // don't care @@ -1611,23 +1877,9 @@ jQuery.extend({ cache[ id ] = null; } - // We destroyed the entire user cache at once because it's faster than - // iterating through each key, but we need to continue to persist internal - // data if it existed - if ( internalCache ) { - cache[ id ] = {}; - // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery - // metadata on plain JS objects when the object is serialized using - // JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = jQuery.noop; - } - - cache[ id ][ internalKey ] = internalCache; - - // Otherwise, we need to eliminate the expando on the node to avoid + // We destroyed the cache and need to eliminate the expando on the node to avoid // false lookups in the cache for entries that no longer exist - } else if ( isNode ) { + if ( isNode ) { // IE does not allow us to delete expando properties from nodes, // nor does it have a removeAttribute function on Document nodes; // we must handle all of these cases @@ -1662,14 +1914,15 @@ jQuery.extend({ jQuery.fn.extend({ data: function( key, value ) { - var data = null; + var parts, attr, name, + data = null; if ( typeof key === "undefined" ) { if ( this.length ) { data = jQuery.data( this[0] ); - if ( this[0].nodeType === 1 ) { - var attr = this[0].attributes, name; + if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { + attr = this[0].attributes; for ( var i = 0, l = attr.length; i < l; i++ ) { name = attr[i].name; @@ -1679,6 +1932,7 @@ jQuery.fn.extend({ dataAttr( this[0], name, data[ name ] ); } } + jQuery._data( this[0], "parsedAttrs", true ); } } @@ -1690,7 +1944,7 @@ jQuery.fn.extend({ }); } - var parts = key.split("."); + parts = key.split("."); parts[1] = parts[1] ? "." + parts[1] : ""; if ( value === undefined ) { @@ -1739,7 +1993,7 @@ function dataAttr( elem, key, data ) { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : - !jQuery.isNaN( data ) ? parseFloat( data ) : + jQuery.isNumeric( data ) ? parseFloat( data ) : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} @@ -1755,11 +2009,14 @@ function dataAttr( elem, key, data ) { return data; } -// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON -// property to be considered empty objects; this property always exists in -// order to make sure JSON.stringify does not expose internal metadata +// checks a cache object for emptiness function isEmptyDataObject( obj ) { for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } if ( name !== "toJSON" ) { return false; } @@ -1775,17 +2032,17 @@ function handleQueueMarkDefer( elem, type, src ) { var deferDataKey = type + "defer", queueDataKey = type + "queue", markDataKey = type + "mark", - defer = jQuery.data( elem, deferDataKey, undefined, true ); + defer = jQuery._data( elem, deferDataKey ); if ( defer && - ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) && - ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) { + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { // Give room for hard-coded callbacks to fire first // and eventually mark/queue something else on the element setTimeout( function() { - if ( !jQuery.data( elem, queueDataKey, undefined, true ) && - !jQuery.data( elem, markDataKey, undefined, true ) ) { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { jQuery.removeData( elem, deferDataKey, true ); - defer.resolve(); + defer.fire(); } }, 0 ); } @@ -1795,8 +2052,8 @@ jQuery.extend({ _mark: function( elem, type ) { if ( elem ) { - type = (type || "fx") + "mark"; - jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); } }, @@ -1809,9 +2066,9 @@ jQuery.extend({ if ( elem ) { type = type || "fx"; var key = type + "mark", - count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 ); + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); if ( count ) { - jQuery.data( elem, key, count, true ); + jQuery._data( elem, key, count ); } else { jQuery.removeData( elem, key, true ); handleQueueMarkDefer( elem, type, "mark" ); @@ -1820,13 +2077,15 @@ jQuery.extend({ }, queue: function( elem, type, data ) { + var q; if ( elem ) { - type = (type || "fx") + "queue"; - var q = jQuery.data( elem, type, undefined, true ); + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !q || jQuery.isArray(data) ) { - q = jQuery.data( elem, type, jQuery.makeArray(data), true ); + q = jQuery._data( elem, type, jQuery.makeArray(data) ); } else { q.push( data ); } @@ -1840,7 +2099,7 @@ jQuery.extend({ var queue = jQuery.queue( elem, type ), fn = queue.shift(), - defer; + hooks = {}; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { @@ -1851,16 +2110,17 @@ jQuery.extend({ // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { - queue.unshift("inprogress"); + queue.unshift( "inprogress" ); } - fn.call(elem, function() { - jQuery.dequeue(elem, type); - }); + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); } if ( !queue.length ) { - jQuery.removeData( elem, type + "queue", true ); + jQuery.removeData( elem, type + "queue " + type + ".run", true ); handleQueueMarkDefer( elem, type, "queue" ); } } @@ -1892,14 +2152,14 @@ jQuery.fn.extend({ // Based off of the plugin by Clint Helfers, with permission. // http://blindsignals.com/index.php/2009/07/jquery-delay/ delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; - return this.queue( type, function() { - var elem = this; - setTimeout(function() { - jQuery.dequeue( elem, type ); - }, time ); + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; }); }, clearQueue: function( type ) { @@ -1930,9 +2190,9 @@ jQuery.fn.extend({ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && - jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { count++; - tmp.done( resolve ); + tmp.add( resolve ); } } resolve(); @@ -1950,7 +2210,8 @@ var rclass = /[\n\t\r]/g, rfocusable = /^(?:button|input|object|select|textarea)$/i, rclickable = /^a(?:rea)?$/i, rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - nodeHook, boolHook; + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; jQuery.fn.extend({ attr: function( name, value ) { @@ -1962,11 +2223,11 @@ jQuery.fn.extend({ jQuery.removeAttr( this, name ); }); }, - + prop: function( name, value ) { return jQuery.access( this, name, value, true, jQuery.prop ); }, - + removeProp: function( name ) { name = jQuery.propFix[ name ] || name; return this.each(function() { @@ -2025,7 +2286,7 @@ jQuery.fn.extend({ } if ( (value && typeof value === "string") || value === undefined ) { - classNames = (value || "").split( rspace ); + classNames = ( value || "" ).split( rspace ); for ( i = 0, l = this.length; i < l; i++ ) { elem = this[ i ]; @@ -2086,8 +2347,10 @@ jQuery.fn.extend({ }, hasClass: function( selector ) { - var className = " " + selector + " "; - for ( var i = 0, l = this.length; i < l; i++ ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { return true; } @@ -2097,9 +2360,9 @@ jQuery.fn.extend({ }, val: function( value ) { - var hooks, ret, + var hooks, ret, isFunction, elem = this[0]; - + if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; @@ -2110,9 +2373,9 @@ jQuery.fn.extend({ ret = elem.value; - return typeof ret === "string" ? + return typeof ret === "string" ? // handle most common string cases - ret.replace(rreturn, "") : + ret.replace(rreturn, "") : // handle cases where value is null/undef or number ret == null ? "" : ret; } @@ -2120,7 +2383,7 @@ jQuery.fn.extend({ return undefined; } - var isFunction = jQuery.isFunction( value ); + isFunction = jQuery.isFunction( value ); return this.each(function( i ) { var self = jQuery(this), val; @@ -2168,7 +2431,7 @@ jQuery.extend({ }, select: { get: function( elem ) { - var value, + var value, i, max, option, index = elem.selectedIndex, values = [], options = elem.options, @@ -2180,8 +2443,10 @@ jQuery.extend({ } // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; // Don't return options that are disabled or in a disabled optgroup if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && @@ -2233,15 +2498,11 @@ jQuery.extend({ height: true, offset: true }, - - attrFix: { - // Always normalize to ensure hook usage - tabindex: "tabIndex" - }, - + attr: function( elem, name, value, pass ) { - var nType = elem.nodeType; - + var ret, hooks, notxml, + nType = elem.nodeType; + // don't get/set attributes on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return undefined; @@ -2256,25 +2517,13 @@ jQuery.extend({ return jQuery.prop( elem, name, value ); } - var ret, hooks, - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - // Normalize the name if needed + // All attributes are lowercase + // Grab necessary hook if one is defined if ( notxml ) { - name = jQuery.attrFix[ name ] || name; - - hooks = jQuery.attrHooks[ name ]; - - if ( !hooks ) { - // Use boolHook for boolean attributes - if ( rboolean.test( name ) ) { - hooks = boolHook; - - // Use nodeHook if available( IE6/7 ) - } else if ( nodeHook ) { - hooks = nodeHook; - } - } + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); } if ( value !== undefined ) { @@ -2305,17 +2554,26 @@ jQuery.extend({ } }, - removeAttr: function( elem, name ) { - var propName; + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, + i = 0; + if ( elem.nodeType === 1 ) { - name = jQuery.attrFix[ name ] || name; + attrNames = ( value || "" ).split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ].toLowerCase(); + propName = jQuery.propFix[ name ] || name; - jQuery.attr( elem, name, "" ); - elem.removeAttribute( name ); + // See #9699 for explanation of this approach (setting first, then removal) + jQuery.attr( elem, name, "" ); + elem.removeAttribute( getSetAttribute ? name : propName ); - // Set corresponding property to false for boolean attributes - if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { - elem[ propName ] = false; + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && propName in elem ) { + elem[ propName ] = false; + } } } }, @@ -2374,17 +2632,17 @@ jQuery.extend({ frameborder: "frameBorder", contenteditable: "contentEditable" }, - + prop: function( elem, name, value ) { - var nType = elem.nodeType; + var ret, hooks, notxml, + nType = elem.nodeType; // don't get/set properties on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return undefined; } - var ret, hooks, - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); if ( notxml ) { // Fix name and attach hooks @@ -2397,7 +2655,7 @@ jQuery.extend({ return ret; } else { - return (elem[ name ] = value); + return ( elem[ name ] = value ); } } else { @@ -2409,7 +2667,7 @@ jQuery.extend({ } } }, - + propHooks: { tabIndex: { get: function( elem ) { @@ -2427,16 +2685,17 @@ jQuery.extend({ } }); -// Add the tabindex propHook to attrHooks for back-compat -jQuery.attrHooks.tabIndex = jQuery.propHooks.tabIndex; +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; // Hook for boolean attributes boolHook = { get: function( elem, name ) { // Align boolean attributes with corresponding properties // Fall back to attribute presence where some booleans are not supported - var attrNode; - return jQuery.prop( elem, name ) === true || ( attrNode = elem.getAttributeNode( name ) ) && attrNode.nodeValue !== false ? + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? name.toLowerCase() : undefined; }, @@ -2461,16 +2720,20 @@ boolHook = { }; // IE6/7 do not support getting/setting some attributes with get/setAttribute -if ( !jQuery.support.getSetAttribute ) { - +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true + }; + // Use this for any attribute in IE6/7 // This fixes almost every IE6/7 issue nodeHook = jQuery.valHooks.button = { get: function( elem, name ) { var ret; ret = elem.getAttributeNode( name ); - // Return undefined if nodeValue is empty string - return ret && ret.nodeValue !== "" ? + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? ret.nodeValue : undefined; }, @@ -2481,10 +2744,13 @@ if ( !jQuery.support.getSetAttribute ) { ret = document.createAttribute( name ); elem.setAttributeNode( ret ); } - return (ret.nodeValue = value + ""); + return ( ret.nodeValue = value + "" ); } }; + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) // This is for removals jQuery.each([ "width", "height" ], function( i, name ) { @@ -2497,6 +2763,18 @@ if ( !jQuery.support.getSetAttribute ) { } }); }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; } @@ -2520,7 +2798,7 @@ if ( !jQuery.support.style ) { return elem.style.cssText.toLowerCase() || undefined; }, set: function( elem, value ) { - return (elem.style.cssText = "" + value); + return ( elem.style.cssText = "" + value ); } }; } @@ -2545,6 +2823,11 @@ if ( !jQuery.support.optSelected ) { }); } +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + // Radios and checkboxes getter/setter if ( !jQuery.support.checkOn ) { jQuery.each([ "radio", "checkbox" ], function() { @@ -2560,7 +2843,7 @@ jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { set: function( elem, value ) { if ( jQuery.isArray( value ) ) { - return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0); + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); } } }); @@ -2574,111 +2857,122 @@ var rnamespaces = /\.(.*)$/, rperiod = /\./g, rspaces = / /g, rescape = /[^\w\s.|`]/g, - fcleanup = function( nm ) { - return nm.replace(rescape, "\\$&"); + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /\bhover(\.\S+)?/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || elem.id === m[2]) && + (!m[3] || m[3].test( elem.className )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); }; /* - * A number of helper functions used for managing events. - * Many of the ideas behind this code originated from - * Dean Edwards' addEvent library. + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { - // Bind an event to an element - // Original by Dean Edwards - add: function( elem, types, handler, data ) { - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } + add: function( elem, types, handler, data, selector ) { - if ( handler === false ) { - handler = returnFalse; - } else if ( !handler ) { - // Fixes bug #7229. Fix recommended by jdalton + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // 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 )) ) { return; } - var handleObjIn, handleObj; - + // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; } - // Make sure that the function being executed has a unique ID + // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } - // Init the element's event structure - var elemData = jQuery._data( elem ); - - // If no elemData is found then we must be trying to bind to one of the - // banned noData elements - if ( !elemData ) { - return; - } - - var events = elemData.events, - eventHandle = elemData.handle; - + // Init the element's event structure and main handler, if this is the first + events = elemData.events; if ( !events ) { elemData.events = events = {}; } - + eventHandle = elemData.handle; if ( !eventHandle ) { elemData.handle = eventHandle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.handle.apply( eventHandle.elem, arguments ) : + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; } - // Add elem as a property of the handle function - // This is to prevent a memory leak with non-native events in IE. - eventHandle.elem = elem; - // Handle multiple events separated by a space // jQuery(...).bind("mouseover mouseout", fn); - types = types.split(" "); + types = hoverHack(types).split( " " ); + for ( t = 0; t < types.length; t++ ) { - var type, i = 0, namespaces; + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); - while ( (type = types[ i++ ]) ) { - handleObj = handleObjIn ? - jQuery.extend({}, handleObjIn) : - { handler: handler, data: data }; + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; - // Namespaced event handlers - if ( type.indexOf(".") > -1 ) { - namespaces = type.split("."); - type = namespaces.shift(); - handleObj.namespace = namespaces.slice(0).sort().join("."); + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; - } else { - namespaces = []; - handleObj.namespace = ""; - } + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; - handleObj.type = type; - if ( !handleObj.guid ) { - handleObj.guid = handler.guid; - } + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + namespace: namespaces.join(".") + }, handleObjIn ); - // Get the current list of functions bound to this event - var handlers = events[ type ], - special = jQuery.event.special[ type ] || {}; + // Delegated event; pre-analyze selector so it's processed quickly on event dispatch + if ( selector ) { + handleObj.quick = quickParse( selector ); + if ( !handleObj.quick && jQuery.expr.match.POS.test( selector ) ) { + handleObj.isPositional = true; + } + } - // Init the event handler queue + // Init the event handler queue if we're the first + handlers = events[ type ]; if ( !handlers ) { handlers = events[ type ] = []; + handlers.delegateCount = 0; - // Check for a special event handler - // Only use addEventListener/attachEvent if the special - // events handler returns false + // Only use addEventListener/attachEvent if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { // Bind the global event handler to the element if ( elem.addEventListener ) { @@ -2698,10 +2992,14 @@ jQuery.event = { } } - // Add the function to the element's handler list - handlers.push( handleObj ); + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } - // Keep track of which events have been used, for event optimization + // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } @@ -2712,129 +3010,87 @@ jQuery.event = { global: {}, // Detach an event or set of events from an element - remove: function( elem, types, handler, pos ) { - // don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - if ( handler === false ) { - handler = returnFalse; - } + remove: function( elem, types, handler, selector ) { - var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ), - events = elemData && elemData.events; + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, namespaces, origCount, + j, events, special, handle, eventType, handleObj; - if ( !elemData || !events ) { + if ( !elemData || !(events = elemData.events) ) { return; } - // types is actually an event object here - if ( types && types.type ) { - handler = types.handler; - types = types.type; - } - - // Unbind all events for the element - if ( !types || typeof types === "string" && types.charAt(0) === "." ) { - types = types || ""; + // Once for each type.namespace in types; type may be omitted + types = hoverHack( types || "" ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = tns[2]; - for ( type in events ) { - jQuery.event.remove( elem, type + types ); - } - - return; - } - - // Handle multiple events separated by a space - // jQuery(...).unbind("mouseover mouseout", fn); - types = types.split(" "); - - while ( (type = types[ i++ ]) ) { - origType = type; - handleObj = null; - all = type.indexOf(".") < 0; - namespaces = []; - - if ( !all ) { - // Namespaced event handlers - namespaces = type.split("."); - type = namespaces.shift(); - - namespace = new RegExp("(^|\\.)" + - jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); - } - - eventType = events[ type ]; - - if ( !eventType ) { - continue; - } - - if ( !handler ) { - for ( j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( all || namespace.test( handleObj.namespace ) ) { - jQuery.event.remove( elem, origType, handleObj.handler, j ); - eventType.splice( j--, 1 ); - } + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + namespaces = namespaces? "." + namespaces : ""; + for ( j in events ) { + jQuery.event.remove( elem, j + namespaces, handler, selector ); } - - continue; + return; } special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; - for ( j = pos || 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; + // Only need to loop for special events or selective removal + if ( handler || namespaces || selector || special.remove ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; - if ( handler.guid === handleObj.guid ) { - // remove the given handler for the given type - if ( all || namespace.test( handleObj.namespace ) ) { - if ( pos == null ) { - eventType.splice( j--, 1 ); - } + if ( !handler || handler.guid === handleObj.guid ) { + if ( !namespaces || namespaces.test( handleObj.namespace ) ) { + if ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) { + eventType.splice( j--, 1 ); - if ( special.remove ) { - special.remove.call( elem, handleObj ); + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } } } - - if ( pos != null ) { - break; - } } + } else { + // Removing all events + eventType.length = 0; } - // remove generic event handler if no more handlers exist - if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } - ret = null; delete events[ type ]; } } // Remove the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { - var handle = elemData.handle; + handle = elemData.handle; if ( handle ) { handle.elem = null; } - delete elemData.events; - delete elemData.handle; - - if ( jQuery.isEmptyObject( elemData ) ) { - jQuery.removeData( elem, undefined, true ); - } + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); } }, - + // Events that are safe to short-circuit if no handlers are attached. // Native DOM events should not be added, they may have inline handlers. customEvent: { @@ -2844,18 +3100,23 @@ jQuery.event = { }, trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + // Event object or event type var type = event.type || event, namespaces = [], - exclusive; + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; - if ( type.indexOf("!") >= 0 ) { + if ( type.indexOf( "!" ) >= 0 ) { // Exclusive events trigger only for the exact event (no namespaces) type = type.slice(0, -1); exclusive = true; } - if ( type.indexOf(".") >= 0 ) { + if ( type.indexOf( "." ) >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); @@ -2877,230 +3138,300 @@ jQuery.event = { new jQuery.Event( type ); event.type = type; + event.isTrigger = true; event.exclusive = exclusive; - event.namespace = namespaces.join("."); - event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)"); - + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + // triggerHandler() and global events don't bubble or run the default action if ( onlyHandlers || !elem ) { event.preventDefault(); - event.stopPropagation(); } // Handle a global trigger if ( !elem ) { + // TODO: Stop taunting the data cache; remove global events and always attach to document - jQuery.each( jQuery.cache, function() { - // internalKey variable is just used to make it easier to find - // and potentially change this stuff later; currently it just - // points to jQuery.expando - var internalKey = jQuery.expando, - internalCache = this[ internalKey ]; - if ( internalCache && internalCache.events && internalCache.events[ type ] ) { - jQuery.event.trigger( event, data, internalCache.handle.elem ); + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); } - }); - return; - } - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + } return; } // Clean up the event in case it is being reused event.result = undefined; - event.target = elem; + if ( !event.target ) { + event.target = elem; + } // Clone any incoming data and prepend the event, creating the handler arg list data = data != null ? jQuery.makeArray( data ) : []; data.unshift( event ); - var cur = elem, - // IE doesn't like method names with a colon (#3533, #8272) - ontype = type.indexOf(":") < 0 ? "on" + type : ""; + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + old = null; + for ( cur = elem.parentNode; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length; i++ ) { - // Fire event on the current element, then bubble up the DOM tree - do { - var handle = jQuery._data( cur, "handle" ); + cur = eventPath[i][0]; + event.type = eventPath[i][1]; - event.currentTarget = cur; + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } - - // Trigger an inline bound script - if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { - event.result = false; - event.preventDefault(); + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) ) { + handle.apply( cur, data ); } - // Bubble up to document, then to window - cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; - } while ( cur && !event.isPropagationStopped() ); + if ( event.isPropagationStopped() ) { + break; + } + } + event.type = type; // If nobody prevented the default action, do it now if ( !event.isDefaultPrevented() ) { - var old, - special = jQuery.event.special[ type ] || {}; - if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction)() check here because IE6/7 fails that test. - // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. - try { - if ( ontype && elem[ type ] ) { - // Don't re-trigger an onFOO event when we call its FOO() method - old = elem[ ontype ]; + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { - if ( old ) { - elem[ ontype ] = null; - } + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; - jQuery.event.triggered = type; - elem[ type ](); + if ( old ) { + elem[ ontype ] = null; } - } catch ( ieError ) {} - if ( old ) { - elem[ ontype ] = old; - } + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; - jQuery.event.triggered = undefined; + if ( old ) { + elem[ ontype ] = old; + } + } } } - + return event.result; }, - handle: function( event ) { + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event || window.event ); - // Snapshot the handlers list since a called handler may add/remove events. - var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0), + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), run_all = !event.exclusive && !event.namespace, - args = Array.prototype.slice.call( arguments, 0 ); + specialHandle = ( jQuery.event.special[ event.type ] || {} ).handle, + handlerQueue = [], + i, j, cur, ret, selMatch, matched, matches, handleObj, sel, hit, related; - // Use the fix-ed Event rather than the (read-only) native event + // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event; - event.currentTarget = this; - - for ( var j = 0, l = handlers.length; j < l; j++ ) { - var handleObj = handlers[ j ]; - - // Triggered event must 1) be non-exclusive and have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event. - if ( run_all || event.namespace_re.test( handleObj.namespace ) ) { - // Pass in a reference to the handler function itself - // So that we can later remove it - event.handler = handleObj.handler; - event.data = handleObj.data; - event.handleObj = handleObj; - - var ret = handleObj.handler.apply( this, args ); - - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); + event.delegateTarget = this; + + // Determine handlers that should run if there are delegated events + // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) { + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + selMatch = {}; + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + hit = selMatch[ sel ]; + + if ( handleObj.isPositional ) { + // Since .is() does not work for positionals; see http://jsfiddle.net/eJ4yd/3/ + hit = ( hit || (selMatch[ sel ] = jQuery( sel )) ).index( cur ) >= 0; + } else if ( hit === undefined ) { + hit = selMatch[ sel ] = ( handleObj.quick ? quickIs( cur, handleObj.quick ) : jQuery( cur ).is( sel ) ); + } + if ( hit ) { + matches.push( handleObj ); } } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } - if ( event.isImmediatePropagationStopped() ) { - break; + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( specialHandle || handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } } } } + return event.result; }, - props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement wheelDelta".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, fix: function( event ) { if ( event[ jQuery.expando ] ) { return event; } - // store a copy of the original event object - // and "clone" to set read-only properties - var originalEvent = event; + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + event = jQuery.Event( originalEvent ); - for ( var i = this.props.length, prop; i; ) { - prop = this.props[ --i ]; + for ( i = copy.length; i; ) { + prop = copy[ --i ]; event[ prop ] = originalEvent[ prop ]; } - // Fix target property, if necessary + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) if ( !event.target ) { - // Fixes #1925 where srcElement might not be defined either - event.target = event.srcElement || document; + event.target = originalEvent.srcElement || document; } - // check if target is a textnode (safari) + // Target should not be a text node (#504, Safari) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } - // Add relatedTarget, if necessary - if ( !event.relatedTarget && event.fromElement ) { - event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; - } - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && event.clientX != null ) { - var eventDocument = event.target.ownerDocument || document, - doc = eventDocument.documentElement, - body = eventDocument.body; - - event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); - event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); - } - - // Add which for key events - if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { - event.which = event.charCode != null ? event.charCode : event.keyCode; - } - - // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) - if ( !event.metaKey && event.ctrlKey ) { + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { event.metaKey = event.ctrlKey; } - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && event.button !== undefined ) { - event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); - } - - return event; + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; }, - // Deprecated, use jQuery.guid instead - guid: 1E8, - - // Deprecated, use jQuery.proxy instead - proxy: jQuery.proxy, - special: { ready: { // Make sure the ready event is setup - setup: jQuery.bindReady, - teardown: jQuery.noop + setup: jQuery.bindReady }, - live: { - add: function( handleObj ) { - jQuery.event.add( this, - liveConvert( handleObj.origType, handleObj.selector ), - jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); - }, - - remove: function( handleObj ) { - jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); - } + focus: { + delegateType: "focusin", + noBubble: true + }, + blur: { + delegateType: "focusout", + noBubble: true }, beforeunload: { @@ -3117,9 +3448,35 @@ jQuery.event = { } } } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } } }; +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + jQuery.removeEvent = document.removeEventListener ? function( elem, type, handle ) { if ( elem.removeEventListener ) { @@ -3134,7 +3491,7 @@ jQuery.removeEvent = document.removeEventListener ? jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword - if ( !this.preventDefault ) { + if ( !(this instanceof jQuery.Event) ) { return new jQuery.Event( src, props ); } @@ -3145,8 +3502,8 @@ jQuery.Event = function( src, props ) { // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || - src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; // Event type } else { @@ -3158,9 +3515,8 @@ jQuery.Event = function( src, props ) { jQuery.extend( this, props ); } - // timeStamp is buggy for some events on Firefox(#3843) - // So we won't rely on the native value - this.timeStamp = jQuery.now(); + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; @@ -3216,216 +3572,132 @@ jQuery.Event.prototype = { isImmediatePropagationStopped: returnFalse }; -// Checks if an event happened on an element within another element -// Used in jQuery.event.special.mouseenter and mouseleave handlers -var withinElement = function( event ) { - - // Check if mouse(over|out) are still within the same parent element - var related = event.relatedTarget, - inside = false, - eventType = event.type; - - event.type = event.data; - - if ( related !== this ) { - - if ( related ) { - inside = jQuery.contains( this, related ); - } - - if ( !inside ) { - - jQuery.event.handle.apply( this, arguments ); - - event.type = eventType; - } - } -}, - -// In case of event delegation, we only need to rename the event.type, -// liveHandler will take care of the rest. -delegate = function( event ) { - event.type = event.data; - jQuery.event.handle.apply( this, arguments ); -}; - -// Create mouseenter and mouseleave events +// Create mouseenter/leave events using mouseover/out and event-time checks jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout" }, function( orig, fix ) { - jQuery.event.special[ orig ] = { - setup: function( data ) { - jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); - }, - teardown: function( data ) { - jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + jQuery.event.special[ orig ] = jQuery.event.special[ fix ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + oldType, ret; + + // For a real mouseover/out, always call the handler; for + // mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || handleObj.origType === event.type || (related !== target && !jQuery.contains( target, related )) ) { + oldType = event.type; + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = oldType; + } + return ret; } }; }); -// submit delegation +// IE submit delegation if ( !jQuery.support.submitBubbles ) { jQuery.event.special.submit = { - setup: function( data, namespaces ) { - if ( !jQuery.nodeName( this, "form" ) ) { - jQuery.event.add(this, "click.specialSubmit", function( e ) { - // Avoid triggering error on non-existent type attribute in IE VML (#7071) - var elem = e.target, - type = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.type : ""; - - if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { - trigger( "submit", this, arguments ); - } - }); - - jQuery.event.add(this, "keypress.specialSubmit", function( e ) { - var elem = e.target, - type = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.type : ""; + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } - if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { - trigger( "submit", this, arguments ); - } - }); + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + // Form was submitted, bubble the event up the tree + if ( this.parentNode ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, - } else { + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { return false; } - }, - teardown: function( namespaces ) { - jQuery.event.remove( this, ".specialSubmit" ); + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); } }; - } -// change delegation, happens here so we have bind. +// IE change delegation and checkbox/radio fix if ( !jQuery.support.changeBubbles ) { - var changeFilters, - - getVal = function( elem ) { - var type = jQuery.nodeName( elem, "input" ) ? elem.type : "", - val = elem.value; - - if ( type === "radio" || type === "checkbox" ) { - val = elem.checked; - - } else if ( type === "select-multiple" ) { - val = elem.selectedIndex > -1 ? - jQuery.map( elem.options, function( elem ) { - return elem.selected; - }).join("-") : - ""; - - } else if ( jQuery.nodeName( elem, "select" ) ) { - val = elem.selectedIndex; - } - - return val; - }, - - testChange = function testChange( e ) { - var elem = e.target, data, val; - - if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { - return; - } - - data = jQuery._data( elem, "_change_data" ); - val = getVal(elem); - - // the current data will be also retrieved by beforeactivate - if ( e.type !== "focusout" || elem.type !== "radio" ) { - jQuery._data( elem, "_change_data", val ); - } - - if ( data === undefined || val === data ) { - return; - } - - if ( data != null || val ) { - e.type = "change"; - e.liveFired = undefined; - jQuery.event.trigger( e, arguments[1], elem ); - } - }; - jQuery.event.special.change = { - filters: { - focusout: testChange, - - beforedeactivate: testChange, - click: function( e ) { - var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + setup: function() { - if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) { - testChange.call( this, e ); + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); } - }, - - // Change has to be called before submit - // Keydown will be called before keypress, which is used in submit-event delegation - keydown: function( e ) { - var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; - if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) || - (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || - type === "select-multiple" ) { - testChange.call( this, e ); + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; } - }, - - // Beforeactivate happens also before the previous element is blurred - // with this event you can't trigger a change event, but you can store - // information - beforeactivate: function( e ) { - var elem = e.target; - jQuery._data( elem, "_change_data", getVal(elem) ); - } + }); }, - setup: function( data, namespaces ) { - if ( this.type === "file" ) { - return false; - } + handle: function( event ) { + var elem = event.target; - for ( var type in changeFilters ) { - jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); } - - return rformElems.test( this.nodeName ); }, - teardown: function( namespaces ) { - jQuery.event.remove( this, ".specialChange" ); + teardown: function() { + jQuery.event.remove( this, "._change" ); return rformElems.test( this.nodeName ); } }; - - changeFilters = jQuery.event.special.change.filters; - - // Handle when the input is .focus()'d - changeFilters.focus = changeFilters.beforeactivate; -} - -function trigger( type, elem, args ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - // Don't pass args or remember liveFired; they apply to the donor event. - var event = jQuery.extend( {}, args[ 0 ] ); - event.type = type; - event.originalEvent = {}; - event.liveFired = undefined; - jQuery.event.handle.call( elem, event ); - if ( event.isDefaultPrevented() ) { - args[ 0 ].preventDefault(); - } } // Create "bubbling" focus and blur events @@ -3433,7 +3705,10 @@ if ( !jQuery.support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0; + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; jQuery.event.special[ fix ] = { setup: function() { @@ -3447,89 +3722,120 @@ if ( !jQuery.support.focusinBubbles ) { } } }; + }); +} - function handler( donor ) { - // Donor event is always a native one; fix it and switch its type. - // Let focusin/out handler cancel the donor focus/blur event. - var e = jQuery.event.fix( donor ); - e.type = fix; - e.originalEvent = {}; - jQuery.event.trigger( e, null, e.target ); - if ( e.isDefaultPrevented() ) { - donor.preventDefault(); - } - } - }); -} - -jQuery.each(["bind", "one"], function( i, name ) { - jQuery.fn[ name ] = function( type, data, fn ) { - var handler; - - // Handle object literals - if ( typeof type === "object" ) { - for ( var key in type ) { - this[ name ](key, data, type[key], fn); - } - return this; - } - - if ( arguments.length === 2 || data === false ) { - fn = data; - data = undefined; - } - - if ( name === "one" ) { - handler = function( event ) { - jQuery( this ).unbind( event, handler ); - return fn.apply( this, arguments ); - }; - handler.guid = fn.guid || jQuery.guid++; - } else { - handler = fn; - } - - if ( type === "unload" && name !== "one" ) { - this.one( type, data, fn ); +jQuery.fn.extend({ - } else { - for ( var i = 0, l = this.length; i < l; i++ ) { - jQuery.event.add( this[i], type, handler, data ); + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); } + return this; } - return this; - }; -}); - -jQuery.fn.extend({ - unbind: function( type, fn ) { - // Handle object literals - if ( typeof type === "object" && !type.preventDefault ) { - for ( var key in type ) { - this.unbind(key, type[key]); + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } - } else { - for ( var i = 0, l = this.length; i < l; i++ ) { - jQuery.event.remove( this[i], type, fn ); + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on.call( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); return this; }, delegate: function( selector, types, data, fn ) { - return this.live( types, data, fn, selector ); + return this.on( types, selector, data, fn ); }, - undelegate: function( selector, types, fn ) { - if ( arguments.length === 0 ) { - return this.unbind( "live" ); - - } else { - return this.die( types, null, fn, selector ); - } + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); }, trigger: function( type, data ) { @@ -3537,7 +3843,6 @@ jQuery.fn.extend({ jQuery.event.trigger( type, data, this ); }); }, - triggerHandler: function( type, data ) { if ( this[0] ) { return jQuery.event.trigger( type, data, this[0], true ); @@ -3551,8 +3856,8 @@ jQuery.fn.extend({ i = 0, toggler = function( event ) { // Figure out which function to execute - var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); // Make sure that clicks stop event.preventDefault(); @@ -3575,178 +3880,9 @@ jQuery.fn.extend({ } }); -var liveMap = { - focus: "focusin", - blur: "focusout", - mouseenter: "mouseover", - mouseleave: "mouseout" -}; - -jQuery.each(["live", "die"], function( i, name ) { - jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { - var type, i = 0, match, namespaces, preType, - selector = origSelector || this.selector, - context = origSelector ? this : jQuery( this.context ); - - if ( typeof types === "object" && !types.preventDefault ) { - for ( var key in types ) { - context[ name ]( key, data, types[key], selector ); - } - - return this; - } - - if ( name === "die" && !types && - origSelector && origSelector.charAt(0) === "." ) { - - context.unbind( origSelector ); - - return this; - } - - if ( data === false || jQuery.isFunction( data ) ) { - fn = data || returnFalse; - data = undefined; - } - - types = (types || "").split(" "); - - while ( (type = types[ i++ ]) != null ) { - match = rnamespaces.exec( type ); - namespaces = ""; - - if ( match ) { - namespaces = match[0]; - type = type.replace( rnamespaces, "" ); - } - - if ( type === "hover" ) { - types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); - continue; - } - - preType = type; - - if ( liveMap[ type ] ) { - types.push( liveMap[ type ] + namespaces ); - type = type + namespaces; - - } else { - type = (liveMap[ type ] || type) + namespaces; - } - - if ( name === "live" ) { - // bind live handler - for ( var j = 0, l = context.length; j < l; j++ ) { - jQuery.event.add( context[j], "live." + liveConvert( type, selector ), - { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); - } - - } else { - // unbind live handler - context.unbind( "live." + liveConvert( type, selector ), fn ); - } - } - - return this; - }; -}); - -function liveHandler( event ) { - var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, - elems = [], - selectors = [], - events = jQuery._data( this, "events" ); - - // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) - if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { - return; - } - - if ( event.namespace ) { - namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); - } - - event.liveFired = this; - - var live = events.live.slice(0); - - for ( j = 0; j < live.length; j++ ) { - handleObj = live[j]; - - if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { - selectors.push( handleObj.selector ); - - } else { - live.splice( j--, 1 ); - } - } - - match = jQuery( event.target ).closest( selectors, event.currentTarget ); - - for ( i = 0, l = match.length; i < l; i++ ) { - close = match[i]; - - for ( j = 0; j < live.length; j++ ) { - handleObj = live[j]; - - if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { - elem = close.elem; - related = null; - - // Those two events require additional checking - if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { - event.type = handleObj.preType; - related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; - - // Make sure not to accidentally match a child element with the same selector - if ( related && jQuery.contains( elem, related ) ) { - related = elem; - } - } - - if ( !related || related !== elem ) { - elems.push({ elem: elem, handleObj: handleObj, level: close.level }); - } - } - } - } - - for ( i = 0, l = elems.length; i < l; i++ ) { - match = elems[i]; - - if ( maxLevel && match.level > maxLevel ) { - break; - } - - event.currentTarget = match.elem; - event.data = match.handleObj.data; - event.handleObj = match.handleObj; - - ret = match.handleObj.origHandler.apply( match.elem, arguments ); - - if ( ret === false || event.isPropagationStopped() ) { - maxLevel = match.level; - - if ( ret === false ) { - stop = false; - } - if ( event.isImmediatePropagationStopped() ) { - break; - } - } - } - - return stop; -} - -function liveConvert( type, selector ) { - return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&"); -} - jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { // Handle event binding jQuery.fn[ name ] = function( data, fn ) { @@ -3763,6 +3899,14 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl if ( jQuery.attrFn ) { jQuery.attrFn[ name ] = true; } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } }); @@ -3776,11 +3920,13 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl (function(){ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), done = 0, toString = Object.prototype.toString, hasDuplicate = false, baseHasDuplicate = true, rBackslash = /\\/g, + rReturn = /\r\n/g, rNonWord = /\W/; // Here we check if the JavaScript engine is using some sort of @@ -3832,7 +3978,7 @@ var Sizzle = function( selector, context, results, seed ) { if ( parts.length > 1 && origPOS.exec( selector ) ) { if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { - set = posProcess( parts[0] + parts[1], context ); + set = posProcess( parts[0] + parts[1], context, seed ); } else { set = Expr.relative[ parts[0] ] ? @@ -3846,7 +3992,7 @@ var Sizzle = function( selector, context, results, seed ) { selector += parts.shift(); } - set = posProcess( selector, set ); + set = posProcess( selector, set, seed ); } } @@ -3965,18 +4111,17 @@ Sizzle.matchesSelector = function( node, expr ) { }; Sizzle.find = function( expr, context, isXML ) { - var set; + var set, i, len, match, type, left; if ( !expr ) { return []; } - for ( var i = 0, l = Expr.order.length; i < l; i++ ) { - var match, - type = Expr.order[i]; + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { - var left = match[1]; + left = match[1]; match.splice( 1, 1 ); if ( left.substr( left.length - 1 ) !== "\\" ) { @@ -4002,17 +4147,18 @@ Sizzle.find = function( expr, context, isXML ) { Sizzle.filter = function( expr, set, inplace, not ) { var match, anyFound, + type, found, item, filter, left, + i, pass, old = expr, result = [], curLoop = set, isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); while ( expr && set.length ) { - for ( var type in Expr.filter ) { + for ( type in Expr.filter ) { if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { - var found, item, - filter = Expr.filter[ type ], - left = match[1]; + filter = Expr.filter[ type ]; + left = match[1]; anyFound = false; @@ -4038,10 +4184,10 @@ Sizzle.filter = function( expr, set, inplace, not ) { } if ( match ) { - for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { if ( item ) { found = filter( item, match, i, curLoop ); - var pass = not ^ !!found; + pass = not ^ found; if ( inplace && found != null ) { if ( pass ) { @@ -4095,6 +4241,45 @@ Sizzle.error = function( msg ) { throw "Syntax error, unrecognized expression: " + msg; }; +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + var Expr = Sizzle.selectors = { order: [ "ID", "NAME", "TAG" ], @@ -4482,7 +4667,7 @@ var Expr = Sizzle.selectors = { return filter( elem, i, match, array ); } else if ( name === "contains" ) { - return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; } else if ( name === "not" ) { var not = match[3]; @@ -4501,7 +4686,10 @@ var Expr = Sizzle.selectors = { }, CHILD: function( elem, match ) { - var type = match[1], + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], node = elem; switch ( type ) { @@ -4529,18 +4717,18 @@ var Expr = Sizzle.selectors = { return true; case "nth": - var first = match[2], - last = match[3]; + first = match[2]; + last = match[3]; if ( first === 1 && last === 0 ) { return true; } - var doneName = match[0], - parent = elem.parentNode; + doneName = match[0]; + parent = elem.parentNode; - if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { - var count = 0; + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; for ( node = parent.firstChild; node; node = node.nextSibling ) { if ( node.nodeType === 1 ) { @@ -4548,10 +4736,10 @@ var Expr = Sizzle.selectors = { } } - parent.sizcache = doneName; + parent[ expando ] = doneName; } - var diff = elem.nodeIndex - last; + diff = elem.nodeIndex - last; if ( first === 0 ) { return diff === 0; @@ -4567,7 +4755,7 @@ var Expr = Sizzle.selectors = { }, TAG: function( elem, match ) { - return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; }, CLASS: function( elem, match ) { @@ -4577,7 +4765,9 @@ var Expr = Sizzle.selectors = { ATTR: function( elem, match ) { var name = match[1], - result = Expr.attrHandle[ name ] ? + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? Expr.attrHandle[ name ]( elem ) : elem[ name ] != null ? elem[ name ] : @@ -4588,6 +4778,8 @@ var Expr = Sizzle.selectors = { return result == null ? type === "!=" : + !type && Sizzle.attr ? + result != null : type === "=" ? value === check : type === "*=" ? @@ -4768,26 +4960,6 @@ if ( document.documentElement.compareDocumentPosition ) { }; } -// Utility function for retreiving the text value of an array of DOM nodes -Sizzle.getText = function( elems ) { - var ret = "", elem; - - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; - - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; - - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += Sizzle.getText( elem.childNodes ); - } - } - - return ret; -}; - // Check to see if the browser returns elements by name when // querying by getElementById (and provide a workaround) (function(){ @@ -5065,13 +5237,13 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { elem = elem[dir]; while ( elem ) { - if ( elem.sizcache === doneName ) { + if ( elem[ expando ] === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 && !isXML ){ - elem.sizcache = doneName; + elem[ expando ] = doneName; elem.sizset = i; } @@ -5098,14 +5270,14 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { elem = elem[dir]; while ( elem ) { - if ( elem.sizcache === doneName ) { + if ( elem[ expando ] === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 ) { if ( !isXML ) { - elem.sizcache = doneName; + elem[ expando ] = doneName; elem.sizset = i; } @@ -5153,7 +5325,7 @@ Sizzle.isXML = function( elem ) { return documentElement ? documentElement.nodeName !== "HTML" : false; }; -var posProcess = function( selector, context ) { +var posProcess = function( selector, context, seed ) { var match, tmpSet = [], later = "", @@ -5169,13 +5341,16 @@ var posProcess = function( selector, context ) { selector = Expr.relative[selector] ? selector + "*" : selector; for ( var i = 0, l = root.length; i < l; i++ ) { - Sizzle( selector, root[i], tmpSet ); + Sizzle( selector, root[i], tmpSet, seed ); } return Sizzle.filter( later, tmpSet ); }; // EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.filters; @@ -5261,43 +5436,33 @@ jQuery.fn.extend({ }, is: function( selector ) { - return !!selector && ( typeof selector === "string" ? - jQuery.filter( selector, this ).length > 0 : - this.filter( selector ).length > 0 ); + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); }, closest: function( selectors, context ) { var ret = [], i, l, cur = this[0]; - // Array + // Array (deprecated as of jQuery 1.7) if ( jQuery.isArray( selectors ) ) { - var match, selector, - matches = {}, - level = 1; - - if ( cur && selectors.length ) { - for ( i = 0, l = selectors.length; i < l; i++ ) { - selector = selectors[i]; - - if ( !matches[ selector ] ) { - matches[ selector ] = POS.test( selector ) ? - jQuery( selector, context || this.context ) : - selector; - } - } + var level = 1; - while ( cur && cur.ownerDocument && cur !== context ) { - for ( selector in matches ) { - match = matches[ selector ]; + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { - if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) { - ret.push({ selector: selector, elem: cur, level: level }); - } + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); } - - cur = cur.parentNode; - level++; } + + cur = cur.parentNode; + level++; } return ret; @@ -5504,7 +5669,7 @@ function winnow( elements, qualifier, keep ) { } else if ( qualifier.nodeType ) { return jQuery.grep(elements, function( elem, i ) { - return (elem === qualifier) === keep; + return ( elem === qualifier ) === keep; }); } else if ( typeof qualifier === "string" ) { @@ -5520,20 +5685,38 @@ function winnow( elements, qualifier, keep ) { } return jQuery.grep(elements, function( elem, i ) { - return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; }); } -var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, +function createSafeFragment( document ) { + var list = nodeNames.split( " " ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr article aside audio canvas datalist details figcaption figure footer " + + "header hgroup mark meter nav output progress section summary time video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, rleadingWhitespace = /^\s+/, rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, rtagName = /<([\w:]+)/, rtbody = /", "" ], area: [ 1, "", "" ], _default: [ 0, "", "" ] - }; + }, + safeFragment = createSafeFragment( document ); wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; @@ -5728,7 +5912,7 @@ jQuery.fn.extend({ null; // See if we can take a shortcut and just use innerHTML - } else if ( typeof value === "string" && !rnocache.test( value ) && + } else if ( typeof value === "string" && !rnoInnerhtml.test( value ) && (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) && !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) { @@ -5854,7 +6038,7 @@ jQuery.fn.extend({ // in certain situations (Bug #8070). // Fragments from the fragment cache must always be cloned and never used // in place. - results.cacheable || (l > 1 && i < lastIndex) ? + results.cacheable || ( l > 1 && i < lastIndex ) ? jQuery.clone( fragment, true, true ) : fragment ); @@ -5883,27 +6067,26 @@ function cloneCopyEvent( src, dest ) { return; } - var internalKey = jQuery.expando, - oldData = jQuery.data( src ), - curData = jQuery.data( dest, oldData ); - - // Switch to use the internal data object, if it exists, for the next - // stage of data copying - if ( (oldData = oldData[ internalKey ]) ) { - var events = oldData.events; - curData = curData[ internalKey ] = jQuery.extend({}, oldData); + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; - if ( events ) { - delete curData.handle; - curData.events = {}; + if ( events ) { + delete curData.handle; + curData.events = {}; - for ( var type in events ) { - for ( var i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data ); - } + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data ); } } } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } } function cloneFixAttributes( src, dest ) { @@ -5965,14 +6148,15 @@ function cloneFixAttributes( src, dest ) { } jQuery.buildFragment = function( args, nodes, scripts ) { - var fragment, cacheable, cacheresults, doc; - - // nodes may contain either an explicit document object, - // a jQuery collection or context object. - // If nodes[0] contains a valid object to assign to doc - if ( nodes && nodes[0] ) { - doc = nodes[0].ownerDocument || nodes[0]; - } + var fragment, cacheable, cacheresults, doc, + first = args[ 0 ]; + + // nodes may contain either an explicit document object, + // a jQuery collection or context object. + // If nodes[0] contains a valid object to assign to doc + if ( nodes && nodes[0] ) { + doc = nodes[0].ownerDocument || nodes[0]; + } // Ensure that an attr object doesn't incorrectly stand in as a document object // Chrome and Firefox seem to allow this to occur and will throw exception @@ -5985,12 +6169,15 @@ jQuery.buildFragment = function( args, nodes, scripts ) { // Cloning options loses the selected state, so don't cache them // IE 6 doesn't like it when you put or elements in a fragment // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache - if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document && - args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) { + // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501 + if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document && + first.charAt(0) === "<" && !rnocache.test( first ) && + (jQuery.support.checkClone || !rchecked.test( first )) && + (!jQuery.support.unknownElems && rnoshimcache.test( first )) ) { cacheable = true; - cacheresults = jQuery.fragments[ args[0] ]; + cacheresults = jQuery.fragments[ first ]; if ( cacheresults && cacheresults !== 1 ) { fragment = cacheresults; } @@ -6002,7 +6189,7 @@ jQuery.buildFragment = function( args, nodes, scripts ) { } if ( cacheable ) { - jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1; + jQuery.fragments[ first ] = cacheresults ? fragment : 1; } return { fragment: fragment, cacheable: cacheable }; @@ -6028,7 +6215,7 @@ jQuery.each({ } else { for ( var i = 0, l = insert.length; i < l; i++ ) { - var elems = (i > 0 ? this.clone(true) : this).get(); + var elems = ( i > 0 ? this.clone(true) : this ).get(); jQuery( insert[i] )[ original ]( elems ); ret = ret.concat( elems ); } @@ -6039,10 +6226,10 @@ jQuery.each({ }); function getAll( elem ) { - if ( "getElementsByTagName" in elem ) { + if ( typeof elem.getElementsByTagName !== "undefined" ) { return elem.getElementsByTagName( "*" ); - } else if ( "querySelectorAll" in elem ) { + } else if ( typeof elem.querySelectorAll !== "undefined" ) { return elem.querySelectorAll( "*" ); } else { @@ -6058,9 +6245,11 @@ function fixDefaultChecked( elem ) { } // Finds all inputs and passes them to fixDefaultChecked function findInputs( elem ) { - if ( jQuery.nodeName( elem, "input" ) ) { + var nodeName = ( elem.nodeName || "" ).toLowerCase(); + if ( nodeName === "input" ) { fixDefaultChecked( elem ); - } else if ( "getElementsByTagName" in elem ) { + // Skip scripts, get other children + } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) { jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); } } @@ -6148,11 +6337,20 @@ jQuery.extend({ elem = elem.replace(rxhtmlTag, "<$1>"); // Trim whitespace, otherwise indexOf won't work as expected - var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), + var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(), wrap = wrapMap[ tag ] || wrapMap._default, depth = wrap[0], div = context.createElement("div"); + // Append wrapper element to unknown element safe doc fragment + if ( context === document ) { + // Use the fragment we've already created for this document + safeFragment.appendChild( div ); + } else { + // Use a fragment created with the owner document + createSafeFragment( context ).appendChild( div ); + } + // Go to html and back, then peel off extra wrappers div.innerHTML = wrap[1] + elem + wrap[2]; @@ -6233,7 +6431,9 @@ jQuery.extend({ }, cleanData: function( elems ) { - var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special, + var data, id, + cache = jQuery.cache, + special = jQuery.event.special, deleteExpando = jQuery.support.deleteExpando; for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { @@ -6244,7 +6444,7 @@ jQuery.extend({ id = elem[ jQuery.expando ]; if ( id ) { - data = cache[ id ] && cache[ id ][ internalKey ]; + data = cache[ id ]; if ( data && data.events ) { for ( var type in data.events ) { @@ -6506,7 +6706,7 @@ if ( !jQuery.support.opacity ) { set: function( elem, value ) { var style = elem.style, currentStyle = elem.currentStyle, - opacity = jQuery.isNaN( value ) ? "" : "alpha(opacity=" + value * 100 + ")", + opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", filter = currentStyle && currentStyle.filter || style.filter || ""; // IE has trouble with opacity if it does not have layout @@ -6580,25 +6780,32 @@ if ( document.defaultView && document.defaultView.getComputedStyle ) { if ( document.documentElement.currentStyle ) { currentStyle = function( elem, name ) { - var left, + var left, rsLeft, uncomputed, ret = elem.currentStyle && elem.currentStyle[ name ], - rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ], style = elem.style; + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret === null && style && (uncomputed = style[ name ]) ) { + ret = uncomputed; + } + // From the awesome hack by Dean Edwards // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 // If we're not dealing with a regular pixel number // but a number that has a weird ending, we need to convert it to pixels if ( !rnumpx.test( ret ) && rnum.test( ret ) ) { + // Remember the original values left = style.left; + rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; // Put in the new values to get a computed value out if ( rsLeft ) { elem.runtimeStyle.left = elem.currentStyle.left; } - style.left = name === "fontSize" ? "1em" : (ret || 0); + style.left = name === "fontSize" ? "1em" : ( ret || 0 ); ret = style.pixelLeft + "px"; // Revert the changed values @@ -6666,7 +6873,7 @@ if ( jQuery.expr && jQuery.expr.filters ) { var width = elem.offsetWidth, height = elem.offsetHeight; - return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none"); + return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none"); }; jQuery.expr.filters.visible = function( elem ) { @@ -6720,7 +6927,7 @@ var r20 = /%20/g, // Document location segments ajaxLocParts, - + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression allTypes = ["*/"] + ["*"]; @@ -6759,7 +6966,7 @@ function addToPrefiltersOrTransports( structure ) { placeBefore; // For each dataType in the dataTypeExpression - for(; i < length; i++ ) { + for ( ; i < length; i++ ) { dataType = dataTypes[ i ]; // We control if we're asked to add before // any existing element @@ -6790,7 +6997,7 @@ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqX executeOnly = ( structure === prefilters ), selection; - for(; i < length && ( executeOnly || !selection ); i++ ) { + for ( ; i < length && ( executeOnly || !selection ); i++ ) { selection = list[ i ]( options, originalOptions, jqXHR ); // If we got redirected to another dataType // we try there if executing only and not done already @@ -6821,7 +7028,7 @@ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqX function ajaxExtend( target, src ) { var key, deep, flatOptions = jQuery.ajaxSettings.flatOptions || {}; - for( key in src ) { + for ( key in src ) { if ( src[ key ] !== undefined ) { ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; } @@ -7080,7 +7287,7 @@ jQuery.extend({ jQuery( callbackContext ) : jQuery.event, // Deferreds deferred = jQuery.Deferred(), - completeDeferred = jQuery._Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), // Status-dependent callbacks statusCode = s.statusCode || {}, // ifModified key @@ -7230,7 +7437,7 @@ jQuery.extend({ // We extract error from statusText // then normalize statusText and status for non-aborts error = statusText; - if( !statusText || status ) { + if ( !statusText || status ) { statusText = "error"; if ( status < 0 ) { status = 0; @@ -7259,7 +7466,7 @@ jQuery.extend({ } // Complete - completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] ); + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); if ( fireGlobals ) { globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); @@ -7274,14 +7481,14 @@ jQuery.extend({ deferred.promise( jqXHR ); jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail; - jqXHR.complete = completeDeferred.done; + jqXHR.complete = completeDeferred.add; // Status-dependent callbacks jqXHR.statusCode = function( map ) { if ( map ) { var tmp; if ( state < 2 ) { - for( tmp in map ) { + for ( tmp in map ) { statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; } } else { @@ -7358,7 +7565,7 @@ jQuery.extend({ ret = s.url.replace( rts, "$1_=" + ts ); // if nothing was replaced, add timestamp to the end - s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); } } @@ -7536,7 +7743,7 @@ function ajaxHandleResponses( s, jqXHR, responses ) { firstDataType; // Fill responseXXX fields - for( type in responseFields ) { + for ( type in responseFields ) { if ( type in responses ) { jqXHR[ responseFields[type] ] = responses[ type ]; } @@ -7615,13 +7822,13 @@ function ajaxConvert( s, response ) { conv2; // For each dataType in the chain - for( i = 1; i < length; i++ ) { + for ( i = 1; i < length; i++ ) { // Create converters map // with lowercased keys if ( i === 1 ) { - for( key in s.converters ) { - if( typeof key === "string" ) { + for ( key in s.converters ) { + if ( typeof key === "string" ) { converters[ key.toLowerCase() ] = s.converters[ key ]; } } @@ -7632,7 +7839,7 @@ function ajaxConvert( s, response ) { current = dataTypes[ i ]; // If current is auto dataType, update it to prev - if( current === "*" ) { + if ( current === "*" ) { current = prev; // If no auto and dataTypes are actually different } else if ( prev !== "*" && prev !== current ) { @@ -7644,7 +7851,7 @@ function ajaxConvert( s, response ) { // If there is no direct converter, search transitively if ( !conv ) { conv2 = undefined; - for( conv1 in converters ) { + for ( conv1 in converters ) { tmp = conv1.split( " " ); if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) { conv2 = converters[ tmp[1] + " " + current ]; @@ -8083,11 +8290,11 @@ jQuery.fn.extend({ var elem, display; if ( speed || speed === 0 ) { - return this.animate( genFx("show", 3), speed, easing, callback); + return this.animate( genFx("show", 3), speed, easing, callback ); } else { for ( var i = 0, j = this.length; i < j; i++ ) { - elem = this[i]; + elem = this[ i ]; if ( elem.style ) { display = elem.style.display; @@ -8101,8 +8308,8 @@ jQuery.fn.extend({ // Set elements which have been overridden with display: none // in a stylesheet to whatever the default browser style is // for such an element - if ( display === "" && jQuery.css( elem, "display" ) === "none" ) { - jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName)); + if ( display === "" && jQuery.css(elem, "display") === "none" ) { + jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) ); } } } @@ -8110,13 +8317,13 @@ jQuery.fn.extend({ // Set the display of most of the elements in a second loop // to avoid the constant reflow for ( i = 0; i < j; i++ ) { - elem = this[i]; + elem = this[ i ]; if ( elem.style ) { display = elem.style.display; if ( display === "" || display === "none" ) { - elem.style.display = jQuery._data(elem, "olddisplay") || ""; + elem.style.display = jQuery._data( elem, "olddisplay" ) || ""; } } } @@ -8130,12 +8337,17 @@ jQuery.fn.extend({ return this.animate( genFx("hide", 3), speed, easing, callback); } else { - for ( var i = 0, j = this.length; i < j; i++ ) { - if ( this[i].style ) { - var display = jQuery.css( this[i], "display" ); + var elem, display, + i = 0, + j = this.length; + + for ( ; i < j; i++ ) { + elem = this[i]; + if ( elem.style ) { + display = jQuery.css( elem, "display" ); - if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) { - jQuery._data( this[i], "olddisplay", display ); + if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) { + jQuery._data( elem, "olddisplay", display ); } } } @@ -8180,7 +8392,7 @@ jQuery.fn.extend({ }, animate: function( prop, speed, easing, callback ) { - var optall = jQuery.speed(speed, easing, callback); + var optall = jQuery.speed( speed, easing, callback ); if ( jQuery.isEmptyObject( prop ) ) { return this.each( optall.complete, [ false ] ); @@ -8189,7 +8401,7 @@ jQuery.fn.extend({ // Do not change referenced properties as per-property easing will be lost prop = jQuery.extend( {}, prop ); - return this[ optall.queue === false ? "each" : "queue" ](function() { + function doAnimation() { // XXX 'this' does not always have a nodeName when running the // test suite @@ -8200,9 +8412,9 @@ jQuery.fn.extend({ var opt = jQuery.extend( {}, optall ), isElement = this.nodeType === 1, hidden = isElement && jQuery(this).is(":hidden"), - name, val, p, - display, e, - parts, start, end, unit; + name, val, p, e, + parts, start, end, unit, + method; // will store per property easing and be used to determine when an animation is complete opt.animatedProperties = {}; @@ -8238,25 +8450,17 @@ jQuery.fn.extend({ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ]; // Set display property to inline-block for height/width - // animations on inline elements that are having width/height - // animated + // animations on inline elements that are having width/height animated if ( jQuery.css( this, "display" ) === "inline" && jQuery.css( this, "float" ) === "none" ) { - if ( !jQuery.support.inlineBlockNeedsLayout ) { + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) { this.style.display = "inline-block"; } else { - display = defaultDisplay( this.nodeName ); - - // inline-level elements accept inline-block; - // block-level elements need to be inline with layout - if ( display === "inline" ) { - this.style.display = "inline-block"; - - } else { - this.style.display = "inline"; - this.style.zoom = 1; - } + this.style.zoom = 1; } } } @@ -8270,8 +8474,17 @@ jQuery.fn.extend({ e = new jQuery.fx( this, opt, p ); val = prop[ p ]; - if ( rfxtypes.test(val) ) { - e[ val === "toggle" ? hidden ? "show" : "hide" : val ](); + if ( rfxtypes.test( val ) ) { + + // Tracks whether to show or hide based on private + // data attached to the element + method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 ); + if ( method ) { + jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" ); + e[ method ](); + } else { + e[ val ](); + } } else { parts = rfxnum.exec( val ); @@ -8284,7 +8497,7 @@ jQuery.fn.extend({ // We need to compute starting value if ( unit !== "px" ) { jQuery.style( this, p, (end || 1) + unit); - start = ((end || 1) / e.cur()) * start; + start = ( (end || 1) / e.cur() ) * start; jQuery.style( this, p, start + unit); } @@ -8303,39 +8516,71 @@ jQuery.fn.extend({ // For JS strict compliance return true; - }); + } + + return optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); }, - stop: function( clearQueue, gotoEnd ) { - if ( clearQueue ) { - this.queue([]); + stop: function( type, clearQueue, gotoEnd ) { + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); } - this.each(function() { - var timers = jQuery.timers, - i = timers.length; + return this.each(function() { + var i, + hadTimers = false, + timers = jQuery.timers, + data = jQuery._data( this ); + // clear marker counters if we know they won't be if ( !gotoEnd ) { jQuery._unmark( true, this ); } - while ( i-- ) { - if ( timers[i].elem === this ) { - if (gotoEnd) { - // force the next step to be the last - timers[i](true); - } - timers.splice(i, 1); + function stopQueue( elem, data, i ) { + var hooks = data[ i ]; + jQuery.removeData( elem, i, true ); + hooks.stop( gotoEnd ); + } + + if ( type == null ) { + for ( i in data ) { + if ( data[ i ].stop && i.indexOf(".run") === i.length - 4 ) { + stopQueue( this, data, i ); + } } + } else if ( data[ i = type + ".run" ] && data[ i ].stop ){ + stopQueue( this, data, i ); } - }); - // start the next in the queue if the last step wasn't forced - if ( !gotoEnd ) { - this.dequeue(); - } + for ( i = timers.length; i--; ) { + if ( timers[ i ].elem === this && (type == null || timers[ i ].queue === type) ) { + if ( gotoEnd ) { - return this; + // force the next step to be the last + timers[ i ]( true ); + } else { + timers[ i ].saveState(); + } + hadTimers = true; + timers.splice( i, 1 ); + } + } + + // start the next in the queue if the last step wasn't forced + // timers currently will call their complete callbacks, which will dequeue + // but only if they were gotoEnd + if ( !( gotoEnd && hadTimers ) ) { + jQuery.dequeue( this, type ); + } + }); } }); @@ -8354,7 +8599,7 @@ function clearFxNow() { function genFx( type, num ) { var obj = {}; - jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() { + jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() { obj[ this ] = type; }); @@ -8363,9 +8608,9 @@ function genFx( type, num ) { // Generate shortcuts for custom animations jQuery.each({ - slideDown: genFx("show", 1), - slideUp: genFx("hide", 1), - slideToggle: genFx("toggle", 1), + slideDown: genFx( "show", 1 ), + slideUp: genFx( "hide", 1 ), + slideToggle: genFx( "toggle", 1 ), fadeIn: { opacity: "show" }, fadeOut: { opacity: "hide" }, fadeToggle: { opacity: "toggle" } @@ -8377,25 +8622,31 @@ jQuery.each({ jQuery.extend({ speed: function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { complete: fn || !fn && easing || jQuery.isFunction( speed ) && speed, duration: speed, - easing: fn && easing || easing && !jQuery.isFunction(easing) && easing + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing }; opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : - opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default; + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } // Queueing opt.old = opt.complete; + opt.complete = function( noUnmark ) { if ( jQuery.isFunction( opt.old ) ) { opt.old.call( this ); } - if ( opt.queue !== false ) { - jQuery.dequeue( this ); + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); } else if ( noUnmark !== false ) { jQuery._unmark( this ); } @@ -8409,7 +8660,7 @@ jQuery.extend({ return firstNum + diff * p; }, swing: function( p, n, firstNum, diff ) { - return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum; + return ( ( -Math.cos( p*Math.PI ) / 2 ) + 0.5 ) * diff + firstNum; } }, @@ -8432,12 +8683,12 @@ jQuery.fx.prototype = { this.options.step.call( this.elem, this.now, this ); } - (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this ); + ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this ); }, // Get the current size cur: function() { - if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) { + if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) { return this.elem[ this.prop ]; } @@ -8455,17 +8706,22 @@ jQuery.fx.prototype = { fx = jQuery.fx; this.startTime = fxNow || createFxNow(); - this.start = from; this.end = to; - this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" ); - this.now = this.start; + this.now = this.start = from; this.pos = this.state = 0; + this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" ); function t( gotoEnd ) { - return self.step(gotoEnd); + return self.step( gotoEnd ); } + t.queue = this.options.queue; t.elem = this.elem; + t.saveState = function() { + if ( self.options.hide && jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) { + jQuery._data( self.elem, "fxshow" + self.prop, self.start ); + } + }; if ( t() && jQuery.timers.push(t) && !timerId ) { timerId = setInterval( fx.tick, fx.interval ); @@ -8474,14 +8730,20 @@ jQuery.fx.prototype = { // Simple 'show' function show: function() { + var dataShow = jQuery._data( this.elem, "fxshow" + this.prop ); + // Remember where we started, so that we can go back to it later - this.options.orig[this.prop] = jQuery.style( this.elem, this.prop ); + this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop ); this.options.show = true; // Begin the animation - // Make sure that we start at a small width/height to avoid any - // flash of content - this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur()); + // Make sure that we start at a small width/height to avoid any flash of content + if ( dataShow !== undefined ) { + // This show is picking up where a previous hide or show left off + this.custom( this.cur(), dataShow ); + } else { + this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() ); + } // Start by showing the element jQuery( this.elem ).show(); @@ -8490,20 +8752,20 @@ jQuery.fx.prototype = { // Simple 'hide' function hide: function() { // Remember where we started, so that we can go back to it later - this.options.orig[this.prop] = jQuery.style( this.elem, this.prop ); + this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop ); this.options.hide = true; // Begin the animation - this.custom(this.cur(), 0); + this.custom( this.cur(), 0 ); }, // Each step of an animation step: function( gotoEnd ) { - var t = fxNow || createFxNow(), + var p, n, complete, + t = fxNow || createFxNow(), done = true, elem = this.elem, - options = this.options, - i, n; + options = this.options; if ( gotoEnd || t >= options.duration + this.startTime ) { this.now = this.end; @@ -8512,8 +8774,8 @@ jQuery.fx.prototype = { options.animatedProperties[ this.prop ] = true; - for ( i in options.animatedProperties ) { - if ( options.animatedProperties[i] !== true ) { + for ( p in options.animatedProperties ) { + if ( options.animatedProperties[ p ] !== true ) { done = false; } } @@ -8522,25 +8784,36 @@ jQuery.fx.prototype = { // Reset the overflow if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) { - jQuery.each( [ "", "X", "Y" ], function (index, value) { - elem.style[ "overflow" + value ] = options.overflow[index]; + jQuery.each( [ "", "X", "Y" ], function( index, value ) { + elem.style[ "overflow" + value ] = options.overflow[ index ]; }); } // Hide the element if the "hide" operation was done if ( options.hide ) { - jQuery(elem).hide(); + jQuery( elem ).hide(); } // Reset the properties, if the item has been hidden or shown if ( options.hide || options.show ) { - for ( var p in options.animatedProperties ) { - jQuery.style( elem, p, options.orig[p] ); + for ( p in options.animatedProperties ) { + jQuery.style( elem, p, options.orig[ p ] ); + jQuery.removeData( elem, "fxshow" + p, true ); + // Toggle data is no longer needed + jQuery.removeData( elem, "toggle" + p, true ); } } // Execute the complete function - options.complete.call( elem ); + // in the event that the complete function throws an exception + // we must ensure it won't be called twice. #5684 + + complete = options.complete; + if ( complete ) { + + options.complete = false; + complete.call( elem ); + } } return false; @@ -8554,8 +8827,8 @@ jQuery.fx.prototype = { this.state = n / options.duration; // Perform the easing function, defaults to swing - this.pos = jQuery.easing[ options.animatedProperties[ this.prop ] ]( this.state, n, 0, 1, options.duration ); - this.now = this.start + ((this.end - this.start) * this.pos); + this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration ); + this.now = this.start + ( (this.end - this.start) * this.pos ); } // Perform the next step of the animation this.update(); @@ -8567,9 +8840,15 @@ jQuery.fx.prototype = { jQuery.extend( jQuery.fx, { tick: function() { - for ( var timers = jQuery.timers, i = 0 ; i < timers.length ; ++i ) { - if ( !timers[i]() ) { - timers.splice(i--, 1); + var timer, + timers = jQuery.timers, + i = 0; + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); } } @@ -8599,7 +8878,7 @@ jQuery.extend( jQuery.fx, { _default: function( fx ) { if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) { - fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit; + fx.elem.style[ fx.prop ] = fx.now + fx.unit; } else { fx.elem[ fx.prop ] = fx.now; } @@ -8607,6 +8886,14 @@ jQuery.extend( jQuery.fx, { } }); +// Adds width/height step functions +// Do not set anything below 0 +jQuery.each([ "width", "height" ], function( i, prop ) { + jQuery.fx.step[ prop ] = function( fx ) { + jQuery.style( fx.elem, prop, Math.max(0, fx.now) ); + }; +}); + if ( jQuery.expr && jQuery.expr.filters ) { jQuery.expr.filters.animated = function( elem ) { return jQuery.grep(jQuery.timers, function( fn ) { @@ -8623,7 +8910,6 @@ function defaultDisplay( nodeName ) { var body = document.body, elem = jQuery( "<" + nodeName + ">" ).appendTo( body ), display = elem.css( "display" ); - elem.remove(); // If the simple way fails, @@ -8651,7 +8937,6 @@ function defaultDisplay( nodeName ) { iframeDoc.body.appendChild( elem ); display = jQuery.css( elem, "display" ); - body.removeChild( iframe ); } @@ -8728,8 +9013,6 @@ if ( "getBoundingClientRect" in document.documentElement ) { return jQuery.offset.bodyOffset( elem ); } - jQuery.offset.initialize(); - var computedStyle, offsetParent = elem.offsetParent, prevOffsetParent = elem, @@ -8742,7 +9025,7 @@ if ( "getBoundingClientRect" in document.documentElement ) { left = elem.offsetLeft; while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) { - if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) { + if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) { break; } @@ -8754,7 +9037,7 @@ if ( "getBoundingClientRect" in document.documentElement ) { top += elem.offsetTop; left += elem.offsetLeft; - if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) { + if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) { top += parseFloat( computedStyle.borderTopWidth ) || 0; left += parseFloat( computedStyle.borderLeftWidth ) || 0; } @@ -8763,7 +9046,7 @@ if ( "getBoundingClientRect" in document.documentElement ) { offsetParent = elem.offsetParent; } - if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) { + if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) { top += parseFloat( computedStyle.borderTopWidth ) || 0; left += parseFloat( computedStyle.borderLeftWidth ) || 0; } @@ -8776,7 +9059,7 @@ if ( "getBoundingClientRect" in document.documentElement ) { left += body.offsetLeft; } - if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) { + if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) { top += Math.max( docElem.scrollTop, body.scrollTop ); left += Math.max( docElem.scrollLeft, body.scrollLeft ); } @@ -8786,46 +9069,12 @@ if ( "getBoundingClientRect" in document.documentElement ) { } jQuery.offset = { - initialize: function() { - var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0, - html = "
"; - - jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } ); - - container.innerHTML = html; - body.insertBefore( container, body.firstChild ); - innerDiv = container.firstChild; - checkDiv = innerDiv.firstChild; - td = innerDiv.nextSibling.firstChild.firstChild; - - this.doesNotAddBorder = (checkDiv.offsetTop !== 5); - this.doesAddBorderForTableAndCells = (td.offsetTop === 5); - - checkDiv.style.position = "fixed"; - checkDiv.style.top = "20px"; - - // safari subtracts parent border width here which is 5px - this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15); - checkDiv.style.position = checkDiv.style.top = ""; - - innerDiv.style.overflow = "hidden"; - innerDiv.style.position = "relative"; - - this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5); - - this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop); - - body.removeChild( container ); - jQuery.offset.initialize = jQuery.noop; - }, bodyOffset: function( body ) { var top = body.offsetTop, left = body.offsetLeft; - jQuery.offset.initialize(); - - if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) { + if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { top += parseFloat( jQuery.css(body, "marginTop") ) || 0; left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; } @@ -8845,7 +9094,7 @@ jQuery.offset = { curOffset = curElem.offset(), curCSSTop = jQuery.css( elem, "top" ), curCSSLeft = jQuery.css( elem, "left" ), - calculatePosition = (position === "absolute" || position === "fixed") && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, + calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, props = {}, curPosition = {}, curTop, curLeft; // need to be able to calculate position if either top or left is auto and position is either absolute or fixed @@ -8862,11 +9111,11 @@ jQuery.offset = { options = options.call( elem, i, curOffset ); } - if (options.top != null) { - props.top = (options.top - curOffset.top) + curTop; + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; } - if (options.left != null) { - props.left = (options.left - curOffset.left) + curLeft; + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; } if ( "using" in options ) { @@ -8879,6 +9128,7 @@ jQuery.offset = { jQuery.fn.extend({ + position: function() { if ( !this[0] ) { return null; @@ -8981,16 +9231,20 @@ jQuery.each([ "Height", "Width" ], function( i, name ) { // innerHeight and innerWidth jQuery.fn[ "inner" + name ] = function() { var elem = this[0]; - return elem && elem.style ? + return elem ? + elem.style ? parseFloat( jQuery.css( elem, type, "padding" ) ) : + this[ type ]() : null; }; // outerHeight and outerWidth jQuery.fn[ "outer" + name ] = function( margin ) { var elem = this[0]; - return elem && elem.style ? + return elem ? + elem.style ? parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + this[ type ]() : null; }; @@ -9030,7 +9284,7 @@ jQuery.each([ "Height", "Width" ], function( i, name ) { var orig = jQuery.css( elem, type ), ret = parseFloat( orig ); - return jQuery.isNaN( ret ) ? orig : ret; + return jQuery.isNumeric( ret ) ? ret : orig; // Set the width or height on the element (default to pixels if value is unitless) } else { @@ -9043,4 +9297,4 @@ jQuery.each([ "Height", "Width" ], function( i, name ) { // Expose jQuery to the global object window.jQuery = window.$ = jQuery; -})(window); +})( window ); \ No newline at end of file diff --git a/index.html b/index.html index b9266b9..363e499 100644 --- a/index.html +++ b/index.html @@ -113,6 +113,16 @@ margin: 0 5px; padding: 3px 0; } + + .action { + color: #0069d6; + cursor: pointer; + } + + .action:hover { + color: #00438a; + } + @@ -120,9 +130,42 @@ @@ -144,12 +187,6 @@

Antiscroll

- - - - - - @@ -247,6 +284,21 @@

Antiscroll


Download + +
+ +

X rows

+ + +

X cols

+ +
  • Add col
  • +
  • Remove col
  • + + From dcdbe9d1162f7610396bca1656cdb2861cacb88d Mon Sep 17 00:00:00 2001 From: Damian Suarez Date: Mon, 20 Feb 2012 15:33:46 -0300 Subject: [PATCH 07/32] improvement in options property --- antiscroll.js | 77 +++++++++++++++++---------------------------------- 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 81d47c3..17152f4 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -31,41 +31,33 @@ function Antiscroll (el, opts) { this.el = $(el); - this.options = opts || {}; - this.padding = undefined == this.options.padding ? 2 : this.options.padding; + + // options + opts = opts || {}; + this.options = { + x: true + , y: true + , padding: 2 + }; + + for (var k in this.options) + this.options[k] = typeof opts[k] != 'undefined' ? opts[k] : this.options[k]; + + this.padding = this.options.padding; this.inner = this.el.find('.antiscroll-inner'); this.inner.css({ 'width': '+=' + scrollbarSize() , 'height': '+=' + scrollbarSize() }); - this.refresh(); - }; - - /** - * refresh scrollbars - * - * @api public - */ - Antiscroll.prototype.refresh = function() { - var needHScroll = this.inner.get(0).scrollWidth > this.el.width() - , needVScroll = this.inner.get(0).scrollHeight > this.el.height(); - - if (!this.horizontal && needHScroll) { + if (this.inner.get(0).scrollWidth > this.el.width()) { this.horizontal = new Scrollbar.Horizontal(this); - } else if (this.horizontal && !needHScroll) { - this.horizontal.destroy(); - this.horizontal = null } - if (!this.vertical && needVScroll) { + if (this.inner.get(0).scrollHeight > this.el.height()) { this.vertical = new Scrollbar.Vertical(this); - } else if (this.vertical && !needVScroll) { - this.vertical.destroy(); - this.vertical = null } - - }; + } /** * Cleans up. @@ -85,21 +77,7 @@ }; /** - * Rebuild Antiscroll. - * - * @return {Antiscroll} for chaining - * @api public - */ - - Antiscroll.prototype.rebuild = function () { - this.destroy(); - this.inner.attr('style', ''); - Antiscroll.call(this, this.el, this.options); - return this; - }; - - /** - * Scrollbar constructor. + * Scrolbar constructor. * * @param {Element|jQuery} element * @api public @@ -128,12 +106,9 @@ this.pane.inner.bind('mousewheel', $.proxy(this, 'mousewheel')); // show - var initialDisplay = this.pane.options.initialDisplay; - - if (initialDisplay !== false) { - this.show(); - this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); - } + var self = this; + this.show(); + this.hiding = setTimeout($.proxy(this, 'hide'), 3000); }; /** @@ -305,7 +280,7 @@ // minimum top is 0, maximum is the track height var y = Math.min(Math.max(pos, 0), trackWidth - barWidth) - innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) + innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) * y / (trackWidth - barWidth) }; @@ -316,8 +291,8 @@ */ Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { - if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || - (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() + if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || + (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() == this.innerEl.scrollWidth))) { ev.preventDefault(); return false; @@ -373,7 +348,7 @@ // minimum top is 0, maximum is the track height var y = Math.min(Math.max(pos, 0), trackHeight - barHeight) - innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) + innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) * y / (trackHeight - barHeight) }; @@ -384,8 +359,8 @@ */ Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { - if ((y > 0 && 0 == this.innerEl.scrollTop) || - (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() + if ((y > 0 && 0 == this.innerEl.scrollTop) || + (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() == this.innerEl.scrollHeight))) { ev.preventDefault(); return false; From 3bf6497bd69eb0e9aeeed84a6dca4dfebbb5f762 Mon Sep 17 00:00:00 2001 From: Damian Suarez Date: Mon, 20 Feb 2012 15:35:59 -0300 Subject: [PATCH 08/32] explicitly determine the adding of the scrollbars through options parameter --- antiscroll.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 17152f4..571158d 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -50,11 +50,11 @@ , 'height': '+=' + scrollbarSize() }); - if (this.inner.get(0).scrollWidth > this.el.width()) { + if (this.inner.get(0).scrollWidth > this.el.width() && this.options.x) { this.horizontal = new Scrollbar.Horizontal(this); } - if (this.inner.get(0).scrollHeight > this.el.height()) { + if (this.inner.get(0).scrollHeight > this.el.height() && this.options.y) { this.vertical = new Scrollbar.Vertical(this); } } From 5fdbc2fc3fc21fe9603d04b4a58caa33969abd2e Mon Sep 17 00:00:00 2001 From: Damian Suarez Date: Mon, 20 Feb 2012 16:11:42 -0300 Subject: [PATCH 09/32] remove for() for options propertes. Add this.x and this.y --- antiscroll.js | 77 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 571158d..258254d 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -31,33 +31,45 @@ function Antiscroll (el, opts) { this.el = $(el); + this.options = opts || {}; - // options - opts = opts || {}; - this.options = { - x: true - , y: true - , padding: 2 - }; + this.x = false !== this.options.x; + this.y = false !== this.options.y; + this.padding = undefined == this.options.padding ? 2 : this.options.padding; - for (var k in this.options) - this.options[k] = typeof opts[k] != 'undefined' ? opts[k] : this.options[k]; - - this.padding = this.options.padding; this.inner = this.el.find('.antiscroll-inner'); this.inner.css({ 'width': '+=' + scrollbarSize() , 'height': '+=' + scrollbarSize() }); - if (this.inner.get(0).scrollWidth > this.el.width() && this.options.x) { + this.refresh(); + }; + + /** + * refresh scrollbars + * + * @api public + */ + Antiscroll.prototype.refresh = function() { + var needHScroll = this.inner.get(0).scrollWidth > this.el.width() + , needVScroll = this.inner.get(0).scrollHeight > this.el.height(); + + if (!this.horizontal && needHScroll && this.x) { this.horizontal = new Scrollbar.Horizontal(this); + } else if (this.horizontal && !needHScroll) { + this.horizontal.destroy(); + this.horizontal = null } - if (this.inner.get(0).scrollHeight > this.el.height() && this.options.y) { + if (!this.vertical && needVScroll && this.y) { this.vertical = new Scrollbar.Vertical(this); + } else if (this.vertical && !needVScroll) { + this.vertical.destroy(); + this.vertical = null } - } + + }; /** * Cleans up. @@ -77,7 +89,21 @@ }; /** - * Scrolbar constructor. + * Rebuild Antiscroll. + * + * @return {Antiscroll} for chaining + * @api public + */ + + Antiscroll.prototype.rebuild = function () { + this.destroy(); + this.inner.attr('style', ''); + Antiscroll.call(this, this.el, this.options); + return this; + }; + + /** + * Scrollbar constructor. * * @param {Element|jQuery} element * @api public @@ -106,9 +132,12 @@ this.pane.inner.bind('mousewheel', $.proxy(this, 'mousewheel')); // show - var self = this; - this.show(); - this.hiding = setTimeout($.proxy(this, 'hide'), 3000); + var initialDisplay = this.pane.options.initialDisplay; + + if (initialDisplay !== false) { + this.show(); + this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); + } }; /** @@ -280,7 +309,7 @@ // minimum top is 0, maximum is the track height var y = Math.min(Math.max(pos, 0), trackWidth - barWidth) - innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) + innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) * y / (trackWidth - barWidth) }; @@ -291,8 +320,8 @@ */ Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { - if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || - (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() + if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || + (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() == this.innerEl.scrollWidth))) { ev.preventDefault(); return false; @@ -348,7 +377,7 @@ // minimum top is 0, maximum is the track height var y = Math.min(Math.max(pos, 0), trackHeight - barHeight) - innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) + innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) * y / (trackHeight - barHeight) }; @@ -359,8 +388,8 @@ */ Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { - if ((y > 0 && 0 == this.innerEl.scrollTop) || - (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() + if ((y > 0 && 0 == this.innerEl.scrollTop) || + (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() == this.innerEl.scrollHeight))) { ev.preventDefault(); return false; From 87debe64a4e9b7fffd0b701acfe261332882f096 Mon Sep 17 00:00:00 2001 From: othree_kao Date: Tue, 6 Mar 2012 12:07:58 +0800 Subject: [PATCH 10/32] Add antiscroll wrapper --- scrollbar.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 scrollbar.js diff --git a/scrollbar.js b/scrollbar.js new file mode 100644 index 0000000..e50c6b9 --- /dev/null +++ b/scrollbar.js @@ -0,0 +1,52 @@ +/*global define: false, require: false, $: false */ + +(function () { + "use strict"; + + $.fn.scrollbar = function (opts) { + opts = opts || {}; + + return $(this).each(function () { + if ($.data(this, 'antiscrolled')) { return; } + + $.data(this, 'antiscrolled', 'true'); + + var wrappers = $('
    '), + inners = $('
    '), + target = $(this), + cssfloat = target.css('float'), + width = target.outerWidth(), + height = target.outerHeight(), + position = target.css('position'), + left = target.css('left'), + top = target.css('top'); + + target + .css({width: 'auto', height: 'auto', left: 'auto', top: 'auto', position: 'static'}) + .after(wrappers); + + inners.append(target); + + wrappers + .css({float: cssfloat, left: left, top: top}) + .find('.box') + .css({width: width, height: height}) + .end() + .find('.antiscroll-inner') + .css({width: width, height: height}) + .append(inners) + .end() + .antiscroll(opts); + + //For Safari 5.0.5 + if (inners.width() !== width) { + wrappers + .find('.antiscroll-inner') + .css({width: 2 * width - inners.width(), height: height + width - inners.width()}); + } + + }); + }; + +}()); + From 969dc8519df2ccb661ecac3a2153d2ab23093284 Mon Sep 17 00:00:00 2001 From: othree_kao Date: Wed, 14 Mar 2012 14:32:56 +0800 Subject: [PATCH 11/32] Use correct jQuery object --- scrollbar.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scrollbar.js b/scrollbar.js index e50c6b9..eb53a4c 100644 --- a/scrollbar.js +++ b/scrollbar.js @@ -1,6 +1,6 @@ -/*global define: false, require: false, $: false */ +/*global define: false, require: false, jQuery: false */ -(function () { +(function ($) { "use strict"; $.fn.scrollbar = function (opts) { @@ -48,5 +48,5 @@ }); }; -}()); +}(jQuery)); From aec5cd7c7dfbc172511f1b3b5d9d5509d548272b Mon Sep 17 00:00:00 2001 From: Damian Krzeminski Date: Fri, 16 Mar 2012 18:13:58 -0700 Subject: [PATCH 12/32] Optimize scrollbar size computing If scrollbar size is 0 we were computing it every time scrollbarSize was called. It's especially wastefull on webkit browsers were scrollbar size can be set to 0 through CSS. --- antiscroll.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 258254d..dc70c58 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -43,7 +43,7 @@ , 'height': '+=' + scrollbarSize() }); - this.refresh(); + this.refresh(); }; /** @@ -69,7 +69,7 @@ this.vertical = null } - }; + }; /** * Cleans up. @@ -417,7 +417,7 @@ var size; function scrollbarSize () { - if (!size) { + if (size === undefined) { var div = $( '
    ' From 61d2f9a4a739c209b5cf0b4c63760d1ff5e593c7 Mon Sep 17 00:00:00 2001 From: nulltask Date: Thu, 22 Mar 2012 06:02:58 +0900 Subject: [PATCH 13/32] add ios native style border to scrollbar --- antiscroll.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/antiscroll.css b/antiscroll.css index 521099a..801155f 100644 --- a/antiscroll.css +++ b/antiscroll.css @@ -10,6 +10,9 @@ -webkit-border-radius: 7px; -moz-border-radius: 7px; border-radius: 7px; + -webkit-box-shadow: 0 0 1px #fff; + -moz-box-shadow: 0 0 1px #fff; + box-shadow: 0 0 1px #fff; position: absolute; opacity: 0; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); From afbd33fa837100c37656a441fac1645f046e10d8 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 26 Mar 2012 01:46:26 +0200 Subject: [PATCH 14/32] update the scrollbar size when refreshing --- antiscroll.js | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index ed267b2..3b2ad2a 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -34,10 +34,11 @@ this.options = opts || {}; this.padding = undefined == this.options.padding ? 2 : this.options.padding; this.inner = this.el.find('.antiscroll-inner'); - this.inner.css({ - 'width': '+=' + scrollbarSize() - , 'height': '+=' + scrollbarSize() - }); + + cssMap = {} + if (this.x) cssMap.width = '+=' + scrollbarSize(); + if (this.y) cssMap.height = '+=' + scrollbarSize(); + this.inner.css(cssMap); this.refresh(); }; @@ -51,21 +52,28 @@ var needHScroll = this.inner.get(0).scrollWidth > this.el.width() , needVScroll = this.inner.get(0).scrollHeight > this.el.height(); - if (!this.horizontal && needHScroll) { - this.horizontal = new Scrollbar.Horizontal(this); - } else if (this.horizontal && !needHScroll) { - this.horizontal.destroy(); - this.horizontal = null + if (this.x) { + if (!this.horizontal && needHScroll) { + this.horizontal = new Scrollbar.Horizontal(this); + } else if (this.horizontal && !needHScroll) { + this.horizontal.destroy(); + this.horizontal = null; + } else if (this.horizontal) { + this.horizontal.update(); + } } - if (!this.vertical && needVScroll) { - this.vertical = new Scrollbar.Vertical(this); - } else if (this.vertical && !needVScroll) { - this.vertical.destroy(); - this.vertical = null + if (this.y) { + if (!this.vertical && needVScroll) { + this.vertical = new Scrollbar.Vertical(this); + } else if (this.vertical && !needVScroll) { + this.vertical.destroy(); + this.vertical = null + } else if (this.vertical) { + this.vertical.update(); + } } - - }; + }; /** * Cleans up. From 161d425afee1b23908a3a38844c512b01a3d6553 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 26 Mar 2012 10:01:09 +0200 Subject: [PATCH 15/32] =?UTF-8?q?fogot=20var=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- antiscroll.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/antiscroll.js b/antiscroll.js index 3b2ad2a..63964f1 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -35,7 +35,7 @@ this.padding = undefined == this.options.padding ? 2 : this.options.padding; this.inner = this.el.find('.antiscroll-inner'); - cssMap = {} + var cssMap = {}; if (this.x) cssMap.width = '+=' + scrollbarSize(); if (this.y) cssMap.height = '+=' + scrollbarSize(); this.inner.css(cssMap); From 7b38c0873ad9faa413bd46ddffc6d3183ed7aba0 Mon Sep 17 00:00:00 2001 From: Devin Rhode Date: Thu, 26 Apr 2012 02:59:45 -0700 Subject: [PATCH 16/32] I noticed the shade of the scrollbars was a hair off native, so I used Digital Color meter, and they are the exact same f'n color! --- antiscroll.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antiscroll.css b/antiscroll.css index 801155f..dae1369 100644 --- a/antiscroll.css +++ b/antiscroll.css @@ -5,8 +5,8 @@ } .antiscroll-scrollbar { - background: #666; - background: rgba(0, 0, 0, .6); + background: #747474; + background: rgba(0, 0, 0, 0.54); -webkit-border-radius: 7px; -moz-border-radius: 7px; border-radius: 7px; From cbeacec52a39c46c8ebd6c60563fe63e58ac9ff6 Mon Sep 17 00:00:00 2001 From: Devin Rhode Date: Thu, 26 Apr 2012 15:09:57 -0700 Subject: [PATCH 17/32] @Guille, I measured the hover color, 0.04 alpha off. LOLZ --- antiscroll.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antiscroll.css b/antiscroll.css index dae1369..0dd3b27 100644 --- a/antiscroll.css +++ b/antiscroll.css @@ -5,8 +5,8 @@ } .antiscroll-scrollbar { - background: #747474; - background: rgba(0, 0, 0, 0.54); + background: gray; + background: rgba(0, 0, 0, 0.5); -webkit-border-radius: 7px; -moz-border-radius: 7px; border-radius: 7px; From 515ac53e32e8cc787ce6db6dfb7a1a796bb0f15f Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Sun, 29 Apr 2012 17:39:23 +0300 Subject: [PATCH 18/32] fixed some missing ; --- antiscroll.js | 69 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 63964f1..2940274 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -1,4 +1,3 @@ - (function ($) { /** @@ -32,7 +31,11 @@ function Antiscroll (el, opts) { this.el = $(el); this.options = opts || {}; + + this.x = false !== this.options.x; + this.y = false !== this.options.y; this.padding = undefined == this.options.padding ? 2 : this.options.padding; + this.inner = this.el.find('.antiscroll-inner'); var cssMap = {}; @@ -40,7 +43,7 @@ if (this.y) cssMap.height = '+=' + scrollbarSize(); this.inner.css(cssMap); - this.refresh(); + this.refresh(); }; /** @@ -68,7 +71,7 @@ this.vertical = new Scrollbar.Vertical(this); } else if (this.vertical && !needVScroll) { this.vertical.destroy(); - this.vertical = null + this.vertical = null; } else if (this.vertical) { this.vertical.update(); } @@ -93,7 +96,21 @@ }; /** - * Scrolbar constructor. + * Rebuild Antiscroll. + * + * @return {Antiscroll} for chaining + * @api public + */ + + Antiscroll.prototype.rebuild = function () { + this.destroy(); + this.inner.attr('style', ''); + Antiscroll.call(this, this.el, this.options); + return this; + }; + + /** + * Scrollbar constructor. * * @param {Element|jQuery} element * @api public @@ -165,7 +182,7 @@ if (!this.dragging) { this.hide(); } - } + }; /** * Called upon wrap scroll. @@ -203,7 +220,7 @@ var pane = this.pane , move = $.proxy(this, 'mousemove') - , self = this + , self = this; $(document) .mousemove(move) @@ -216,7 +233,7 @@ if (!self.enter) { self.hide(); } - }) + }); }; /** @@ -260,7 +277,7 @@ Scrollbar.Horizontal = function (pane) { this.el = $('
    '); Scrollbar.call(this, pane); - } + }; /** * Inherits from Scrollbar. @@ -277,12 +294,12 @@ Scrollbar.Horizontal.prototype.update = function () { var paneWidth = this.pane.el.width() , trackWidth = paneWidth - this.pane.padding * 2 - , innerEl = this.pane.inner.get(0) + , innerEl = this.pane.inner.get(0); this.el .css('width', trackWidth * paneWidth / innerEl.scrollWidth) - .css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth) - } + .css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth); + }; /** * Called upon drag. @@ -294,13 +311,13 @@ var trackWidth = this.pane.el.width() - this.pane.padding * 2 , pos = ev.pageX - this.startPageX , barWidth = this.el.width() - , innerEl = this.pane.inner.get(0) + , innerEl = this.pane.inner.get(0); // minimum top is 0, maximum is the track height - var y = Math.min(Math.max(pos, 0), trackWidth - barWidth) + var y = Math.min(Math.max(pos, 0), trackWidth - barWidth); - innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) - * y / (trackWidth - barWidth) + innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) + * y / (trackWidth - barWidth); }; /** @@ -310,8 +327,8 @@ */ Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { - if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || - (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() + if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || + (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() == this.innerEl.scrollWidth))) { ev.preventDefault(); return false; @@ -344,11 +361,11 @@ Scrollbar.Vertical.prototype.update = function () { var paneHeight = this.pane.el.height() , trackHeight = paneHeight - this.pane.padding * 2 - , innerEl = this.innerEl + , innerEl = this.innerEl; this.el .css('height', trackHeight * paneHeight / innerEl.scrollHeight) - .css('top', trackHeight * innerEl.scrollTop / innerEl.scrollHeight) + .css('top', trackHeight * innerEl.scrollTop / innerEl.scrollHeight); }; /** @@ -362,13 +379,13 @@ , trackHeight = paneHeight - this.pane.padding * 2 , pos = ev.pageY - this.startPageY , barHeight = this.el.height() - , innerEl = this.innerEl + , innerEl = this.innerEl; // minimum top is 0, maximum is the track height - var y = Math.min(Math.max(pos, 0), trackHeight - barHeight) + var y = Math.min(Math.max(pos, 0), trackHeight - barHeight); - innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) - * y / (trackHeight - barHeight) + innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) + * y / (trackHeight - barHeight); }; /** @@ -378,8 +395,8 @@ */ Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { - if ((y > 0 && 0 == this.innerEl.scrollTop) || - (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() + if ((y > 0 && 0 == this.innerEl.scrollTop) || + (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() == this.innerEl.scrollHeight))) { ev.preventDefault(); return false; @@ -407,7 +424,7 @@ var size; function scrollbarSize () { - if (!size) { + if (size === undefined) { var div = $( '
    ' From fb2d82ca38a10e107c70ebbec35aaa60f18d4eb6 Mon Sep 17 00:00:00 2001 From: Jake Luer Date: Wed, 9 May 2012 11:33:16 -0400 Subject: [PATCH 19/32] autoHide option to prevent scrollbars from disappearing --- antiscroll.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index dc70c58..06ff4db 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -51,6 +51,7 @@ * * @api public */ + Antiscroll.prototype.refresh = function() { var needHScroll = this.inner.get(0).scrollWidth > this.el.width() , needVScroll = this.inner.get(0).scrollHeight > this.el.height(); @@ -68,7 +69,6 @@ this.vertical.destroy(); this.vertical = null } - }; /** @@ -254,7 +254,8 @@ */ Scrollbar.prototype.hide = function () { - if (this.shown) { + var autoHide = this.pane.options.autoHide; + if (autoHide !== false && this.shown) { // check for dragging this.el.removeClass('antiscroll-scrollbar-shown'); this.shown = false; From e745c0a1567bbc3d2f67aaa1af421670632d67f5 Mon Sep 17 00:00:00 2001 From: Fontaine Shu Date: Tue, 29 May 2012 17:55:48 -0700 Subject: [PATCH 20/32] fixed some performance and cleanup issues Set this.horizontal and this.vertical properties to null when destroyed and unbound scroll and mousewheel event listeners tied to the scrollbar when it is destroyed. --- antiscroll.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index dc70c58..b16268f 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -44,7 +44,7 @@ }); this.refresh(); - }; + } /** * refresh scrollbars @@ -81,9 +81,11 @@ Antiscroll.prototype.destroy = function () { if (this.horizontal) { this.horizontal.destroy(); + this.horizontal = null } if (this.vertical) { this.vertical.destroy(); + this.vertical = null } return this; }; @@ -126,10 +128,12 @@ this.el.mousedown($.proxy(this, 'mousedown')); // scrolling - this.pane.inner.scroll($.proxy(this, 'scroll')); + this.innerPaneScrollListener = $.proxy(this, 'scroll'); + this.pane.inner.scroll(this.innerPaneScrollListener); // wheel -optional- - this.pane.inner.bind('mousewheel', $.proxy(this, 'mousewheel')); + this.innerPaneMouseWheelListener = $.proxy(this, 'mousewheel'); + this.pane.inner.bind('mousewheel', this.innerPaneMouseWheelListener); // show var initialDisplay = this.pane.options.initialDisplay; @@ -138,7 +142,7 @@ this.show(); this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); } - }; + } /** * Cleans up. @@ -149,6 +153,8 @@ Scrollbar.prototype.destroy = function () { this.el.remove(); + this.pane.inner.unbind('scroll', this.innerPaneScrollListener); + this.pane.inner.unbind('mousewheel', this.innerPaneMouseWheelListener); return this; }; @@ -175,7 +181,7 @@ if (!this.dragging) { this.hide(); } - } + }; /** * Called upon wrap scroll. @@ -270,7 +276,7 @@ Scrollbar.Horizontal = function (pane) { this.el = $('
    '); Scrollbar.call(this, pane); - } + }; /** * Inherits from Scrollbar. @@ -292,7 +298,7 @@ this.el .css('width', trackWidth * paneWidth / innerEl.scrollWidth) .css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth) - } + }; /** * Called upon drag. @@ -408,7 +414,7 @@ function f() {}; f.prototype = ctorB.prototype; ctorA.prototype = new f; - }; + } /** * Scrollbar size detection. @@ -435,6 +441,6 @@ } return size; - }; + } })(jQuery); From 30eeadf3974eaca7b8363802fe17114da8c8971e Mon Sep 17 00:00:00 2001 From: Fontaine Shu Date: Thu, 5 Jul 2012 22:00:27 -0700 Subject: [PATCH 21/32] enforced a minimum height for the scrollbar --- antiscroll.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index b16268f..9fcc19d 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -360,11 +360,21 @@ Scrollbar.Vertical.prototype.update = function () { var paneHeight = this.pane.el.height() , trackHeight = paneHeight - this.pane.padding * 2 - , innerEl = this.innerEl + , innerEl = this.innerEl; + + var scrollbarHeight = trackHeight * paneHeight / innerEl.scrollHeight; + scrollbarHeight = scrollbarHeight < 20 ? 20 : scrollbarHeight; + + var topPos = trackHeight * innerEl.scrollTop / innerEl.scrollHeight; + + if((topPos + scrollbarHeight) > trackHeight) { + var diff = (topPos + scrollbarHeight) - trackHeight; + topPos = topPos - diff - 3; + } this.el - .css('height', trackHeight * paneHeight / innerEl.scrollHeight) - .css('top', trackHeight * innerEl.scrollTop / innerEl.scrollHeight) + .css('height', scrollbarHeight) + .css('top', topPos) }; /** From ecf95e3ffad2e6d5802b70c0cb40861a77c68955 Mon Sep 17 00:00:00 2001 From: othree_kao Date: Wed, 5 Sep 2012 19:11:50 +0800 Subject: [PATCH 22/32] Add force options, make antiscroll more robust to work between windows --- antiscroll.js | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index a60853a..0482b92 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -39,11 +39,11 @@ , 'height': '+=' + scrollbarSize() }); - if (this.inner.get(0).scrollWidth > this.el.width()) { + if (this.options.forceHorizontal || this.inner.get(0).scrollWidth > this.el.width()) { this.horizontal = new Scrollbar.Horizontal(this); } - if (this.inner.get(0).scrollHeight > this.el.height()) { + if (this.options.forceVertical || this.inner.get(0).scrollHeight > this.el.height()) { this.vertical = new Scrollbar.Vertical(this); } } @@ -99,7 +99,9 @@ if (initialDisplay !== false) { this.show(); - this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); + if (this.pane.options.autoHide) { + this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); + } } }; @@ -136,7 +138,9 @@ this.enter = false; if (!this.dragging) { - this.hide(); + if (this.pane.options.autoHide) { + this.hide(); + } } } @@ -150,7 +154,9 @@ if (!this.shown) { this.show(); if (!this.enter && !this.dragging) { - this.hiding = setTimeout($.proxy(this, 'hide'), 1500); + if (this.pane.options.autoHide) { + this.hiding = setTimeout($.proxy(this, 'hide'), 1500); + } } } @@ -172,19 +178,19 @@ this.startPageX = ev.pageX - parseInt(this.el.css('left'), 10); // prevent crazy selections on IE - document.onselectstart = function () { return false; }; + this.el[0].ownerDocument.onselectstart = function () { return false; }; var pane = this.pane , move = $.proxy(this, 'mousemove') , self = this - $(document) + $(this.el[0].ownerDocument) .mousemove(move) .mouseup(function () { self.dragging = false; - document.onselectstart = null; + this.onselectstart = null; - $(document).unbind('mousemove', move); + $(this).unbind('mousemove', move); if (!self.enter) { self.hide(); @@ -199,8 +205,7 @@ */ Scrollbar.prototype.show = function (duration) { - if (!this.shown) { - this.update(); + if (!this.shown && this.update()) { this.el.addClass('antiscroll-scrollbar-shown'); if (this.hiding) { clearTimeout(this.hiding); @@ -231,7 +236,7 @@ */ Scrollbar.Horizontal = function (pane) { - this.el = $('
    '); + this.el = $('
    ', pane.el); Scrollbar.call(this, pane); } @@ -254,7 +259,9 @@ this.el .css('width', trackWidth * paneWidth / innerEl.scrollWidth) - .css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth) + .css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth); + + return paneWidth < innerEl.scrollWidth; } /** @@ -298,7 +305,7 @@ */ Scrollbar.Vertical = function (pane) { - this.el = $('
    '); + this.el = $('
    ', pane.el); Scrollbar.call(this, pane); }; @@ -321,7 +328,9 @@ this.el .css('height', trackHeight * paneHeight / innerEl.scrollHeight) - .css('top', trackHeight * innerEl.scrollTop / innerEl.scrollHeight) + .css('top', trackHeight * innerEl.scrollTop / innerEl.scrollHeight); + + return paneHeight < innerEl.scrollHeight; }; /** From ace6299d8a0056f6df0d03107d8aa289e8475085 Mon Sep 17 00:00:00 2001 From: Radagaisus Date: Thu, 6 Sep 2012 23:23:49 +0300 Subject: [PATCH 23/32] fix #30 - mousewhell rounding error --- antiscroll.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 06ff4db..d91122a 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -322,7 +322,7 @@ Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || - (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() + (x > 0 && (this.innerEl.scrollLeft + Math.ceil(this.pane.el.width()) == this.innerEl.scrollWidth))) { ev.preventDefault(); return false; @@ -390,7 +390,7 @@ Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { if ((y > 0 && 0 == this.innerEl.scrollTop) || - (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() + (y < 0 && (this.innerEl.scrollTop + Math.ceil(this.pane.el.height()) == this.innerEl.scrollHeight))) { ev.preventDefault(); return false; From 7e1eca49d92a6f26211619704ba2551cab5b8f48 Mon Sep 17 00:00:00 2001 From: Alexandre Rocha Lima e Marcondes Date: Mon, 29 Oct 2012 11:49:46 -0200 Subject: [PATCH 24/32] * Fix on vertical scroll update * Coding style fixes * options.Force refactoring --- antiscroll.js | 56 ++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 4b2bc60..f7241b8 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -33,14 +33,14 @@ this.el = $(el); this.options = opts || {}; - this.x = false !== this.options.x; - this.y = false !== this.options.y; + this.x = (false !== this.options.x) || this.options.forceHorizontal; + this.y = (false !== this.options.y) || this.options.forceVertical; this.padding = undefined == this.options.padding ? 2 : this.options.padding; this.inner = this.el.find('.antiscroll-inner'); this.inner.css({ - 'width': '+=' + scrollbarSize() - , 'height': '+=' + scrollbarSize() + 'width': '+=' + scrollbarSize(), + 'height': '+=' + scrollbarSize() }); this.refresh(); @@ -53,17 +53,17 @@ */ Antiscroll.prototype.refresh = function() { - var needHScroll = this.inner.get(0).scrollWidth > this.el.width() - , needVScroll = this.inner.get(0).scrollHeight > this.el.height(); + var needHScroll = this.inner.get(0).scrollWidth > this.el.width(), + needVScroll = this.inner.get(0).scrollHeight > this.el.height(); - if (this.options.forceHorizontal || (!this.horizontal && needHScroll && this.x)) { + if (!this.horizontal && needHScroll && this.x) { this.horizontal = new Scrollbar.Horizontal(this); } else if (this.horizontal && !needHScroll) { this.horizontal.destroy(); this.horizontal = null } - if (this.options.forceVertical || (!this.vertical && needVScroll && this.y)) { + if (!this.vertical && needVScroll && this.y) { this.vertical = new Scrollbar.Vertical(this); } else if (this.vertical && !needVScroll) { this.vertical.destroy(); @@ -223,9 +223,9 @@ // prevent crazy selections on IE this.el[0].ownerDocument.onselectstart = function () { return false; }; - var pane = this.pane - , move = $.proxy(this, 'mousemove') - , self = this + var pane = this.pane, + move = $.proxy(this, 'mousemove'), + self = this $(this.el[0].ownerDocument) .mousemove(move) @@ -297,9 +297,9 @@ */ Scrollbar.Horizontal.prototype.update = function () { - var paneWidth = this.pane.el.width() - , trackWidth = paneWidth - this.pane.padding * 2 - , innerEl = this.pane.inner.get(0) + var paneWidth = this.pane.el.width(), + trackWidth = paneWidth - this.pane.padding * 2, + innerEl = this.pane.inner.get(0) this.el .css('width', trackWidth * paneWidth / innerEl.scrollWidth) @@ -315,10 +315,10 @@ */ Scrollbar.Horizontal.prototype.mousemove = function (ev) { - var trackWidth = this.pane.el.width() - this.pane.padding * 2 - , pos = ev.pageX - this.startPageX - , barWidth = this.el.width() - , innerEl = this.pane.inner.get(0) + var trackWidth = this.pane.el.width() - this.pane.padding * 2, + pos = ev.pageX - this.startPageX, + barWidth = this.el.width(), + innerEl = this.pane.inner.get(0) // minimum top is 0, maximum is the track height var y = Math.min(Math.max(pos, 0), trackWidth - barWidth) @@ -366,9 +366,9 @@ */ Scrollbar.Vertical.prototype.update = function () { - var paneHeight = this.pane.el.height() - , trackHeight = paneHeight - this.pane.padding * 2 - , innerEl = this.innerEl; + var paneHeight = this.pane.el.height(), + trackHeight = paneHeight - this.pane.padding * 2, + innerEl = this.innerEl; var scrollbarHeight = trackHeight * paneHeight / innerEl.scrollHeight; scrollbarHeight = scrollbarHeight < 20 ? 20 : scrollbarHeight; @@ -382,7 +382,9 @@ this.el .css('height', scrollbarHeight) - .css('top', topPos) + .css('top', topPos); + + return paneHeight < innerEl.scrollHeight; }; /** @@ -392,11 +394,11 @@ */ Scrollbar.Vertical.prototype.mousemove = function (ev) { - var paneHeight = this.pane.el.height() - , trackHeight = paneHeight - this.pane.padding * 2 - , pos = ev.pageY - this.startPageY - , barHeight = this.el.height() - , innerEl = this.innerEl + var paneHeight = this.pane.el.height(), + trackHeight = paneHeight - this.pane.padding * 2, + pos = ev.pageY - this.startPageY, + barHeight = this.el.height(), + innerEl = this.innerEl // minimum top is 0, maximum is the track height var y = Math.min(Math.max(pos, 0), trackHeight - barHeight) From 30b739432a32673c63e00382082e3f921710dc98 Mon Sep 17 00:00:00 2001 From: Alexandre Rocha Lima e Marcondes Date: Mon, 29 Oct 2012 13:18:20 -0200 Subject: [PATCH 25/32] * Auto hide fixes --- antiscroll.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index f7241b8..2991e35 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -35,6 +35,7 @@ this.x = (false !== this.options.x) || this.options.forceHorizontal; this.y = (false !== this.options.y) || this.options.forceVertical; + this.autoHide = false !== this.options.autoHide; this.padding = undefined == this.options.padding ? 2 : this.options.padding; this.inner = this.el.find('.antiscroll-inner'); @@ -140,7 +141,7 @@ if (initialDisplay !== false) { this.show(); - if (this.pane.options.autoHide) { + if (this.pane.autoHide) { this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); } } @@ -181,7 +182,7 @@ this.enter = false; if (!this.dragging) { - if (this.pane.options.autoHide) { + if (this.pane.autoHide) { this.hide(); } } @@ -197,7 +198,7 @@ if (!this.shown) { this.show(); if (!this.enter && !this.dragging) { - if (this.pane.options.autoHide) { + if (this.pane.autoHide) { this.hiding = setTimeout($.proxy(this, 'hide'), 1500); } } @@ -265,8 +266,7 @@ */ Scrollbar.prototype.hide = function () { - var autoHide = this.pane.options.autoHide; - if (autoHide !== false && this.shown) { + if (this.pane.autoHide !== false && this.shown) { // check for dragging this.el.removeClass('antiscroll-scrollbar-shown'); this.shown = false; From 080bb302b27ff39d97f9d6abcee78c7551fade8e Mon Sep 17 00:00:00 2001 From: Alexandre Rocha Lima e Marcondes Date: Mon, 29 Oct 2012 13:28:35 -0200 Subject: [PATCH 26/32] * Performance fixes --- antiscroll.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index 2991e35..1310ae4 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -335,7 +335,7 @@ Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || - (x > 0 && (this.innerEl.scrollLeft + Math.ceil(this.pane.el.width()) + (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() == this.innerEl.scrollWidth))) { ev.preventDefault(); return false; @@ -415,7 +415,7 @@ Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { if ((y > 0 && 0 == this.innerEl.scrollTop) || - (y < 0 && (this.innerEl.scrollTop + Math.ceil(this.pane.el.height()) + (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() == this.innerEl.scrollHeight))) { ev.preventDefault(); return false; From 5504c58a0cc99ce92d481d102a5582ed57725fe1 Mon Sep 17 00:00:00 2001 From: Alexandre Rocha Lima e Marcondes Date: Wed, 31 Oct 2012 10:39:00 -0200 Subject: [PATCH 27/32] * recovering issue #30 bugfix --- antiscroll.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index f0529cf..00cf8cc 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -343,7 +343,7 @@ Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || - (x > 0 && (this.innerEl.scrollLeft + this.pane.el.width() + (x > 0 && (this.innerEl.scrollLeft + Math.ceil(this.pane.el.width()) == this.innerEl.scrollWidth))) { ev.preventDefault(); return false; @@ -423,7 +423,7 @@ Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { if ((y > 0 && 0 == this.innerEl.scrollTop) || - (y < 0 && (this.innerEl.scrollTop + this.pane.el.height() + (y < 0 && (this.innerEl.scrollTop + Math.ceil(this.pane.el.height()) == this.innerEl.scrollHeight))) { ev.preventDefault(); return false; From f7030d0cb195b841b3243ba6000280471be3506b Mon Sep 17 00:00:00 2001 From: Tim Coulter Date: Sun, 4 Nov 2012 08:03:29 -0500 Subject: [PATCH 28/32] We don't need to resize the inner div if an axis is turned off. --- antiscroll.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index d91122a..125d670 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -39,8 +39,8 @@ this.inner = this.el.find('.antiscroll-inner'); this.inner.css({ - 'width': '+=' + scrollbarSize() - , 'height': '+=' + scrollbarSize() + 'width': '+=' + (this.y ? scrollbarSize() : 0) + , 'height': '+=' + (this.x ? scrollbarSize() : 0) }); this.refresh(); From 0140be87b72e0ca46e150a486d3ba89509aecc5b Mon Sep 17 00:00:00 2001 From: Damian Krzeminski Date: Sun, 18 Nov 2012 21:50:32 -0700 Subject: [PATCH 29/32] Change scrollbar width calculation Antiscroll stopped working properly in Chrome v25 (it's still in dev channel). For some reason native scrollbars are displayed under the antiscroll generated scrollbars. The browser behaves as if ::scrollbar pseudo class styles (setting scrollbar width/height to 0) were ignored. I am not really sure what's causing that, but when trying to investigate the problem I realized that scrollbarSize did not work properly in Chrome. It was returning 0 and not the native scrollbar width. We now calculate the size in the container with 'antiscroll-inner' class applied. If ::scrollbar CSS is applied properly the width is 0, and we don't have to hide native scrollbars manually. Otherwise scrollbars are hidden by adjusting .antiscroll-inner size. That fixes Chrome v25 issue. --- antiscroll.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/antiscroll.js b/antiscroll.js index d91122a..97e00f4 100644 --- a/antiscroll.js +++ b/antiscroll.js @@ -420,15 +420,13 @@ function scrollbarSize () { if (size === undefined) { var div = $( - '
    ' + '
    ' + '
    ' ); $('body').append(div); - - var w1 = $('div', div).innerWidth(); - div.css('overflow-y', 'scroll'); + var w1 = $(div).innerWidth(); var w2 = $('div', div).innerWidth(); $(div).remove(); From 27b0aafe31ba07835c1d371886345d9f2dd34773 Mon Sep 17 00:00:00 2001 From: Denis Ivanov Date: Tue, 11 Dec 2012 00:23:16 -0800 Subject: [PATCH 30/32] Documenting installation and configuration --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a2f0d8e..d8bfa74 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # Antiscroll: cross-browser native OSX Lion scrollbars Antiscroll fixes a fundamental problem JavaScript UI developers commonly face: @@ -26,6 +25,30 @@ the scrollbars popularized by OS X Lion that retains native properties. Please click [here](http://learnboost.github.com/antiscroll/) to see it in action. +## Installation + +1. Wrap scrollable content with the class ```antiscroll-inner``` +1. Wrap the above with the class ```antiscroll-wrap``` +1. Include the following Javascript + +```javascript + $(function () { + $('.antiscroll-wrap').antiscroll(); + }); +``` + +### Configuration + +You may remove automatic scrollbar hiding by passing in a key-value to the ```antiscroll()``` function like so: + +```javascript + $(function () { + $('.antiscroll-wrap').antiscroll({ + autoHide: false + }); + }); +``` + ## What does it look like? **Firefox 8 `overflow: scroll` and antiscroll on OS X** From 732b7633d0a6d35fc3f41283a8a3e85ea91a964f Mon Sep 17 00:00:00 2001 From: Alexandre Rocha Lima e Marcondes Date: Thu, 14 Feb 2013 15:44:57 -0200 Subject: [PATCH 31/32] * Contributors update --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index d8bfa74..52102e8 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,13 @@ arbitrarily large width, therefore allowing scrolling of a single axis Scrollbar size detection based on the work of [Jonathan Sharp](http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php). +### Contributors + +- Alexandre Rocha Lima e marcondes [@arlm](https://github.com/arlm) +- Othree [@othree](https://github.com/othree) +- PG Herveou [@pgherveou](https://github.com/pgherveou) +- Fontaine Shu [@fontaineshu](https://github.com/fontaineshu) + ## Dependencies - [jQuery](http://github.com/jquery/query) From 2b0a58bb71370dc3a713fcb22dee9d9b6d7b22b7 Mon Sep 17 00:00:00 2001 From: Dave McKenna Date: Tue, 26 Feb 2013 11:55:10 -0500 Subject: [PATCH 32/32] Give scrollbar pseudo-classes their own blocks A bug in Chrome 25 on Lion requires each selector to have their own blocks. E.g. the following: .antiscroll-inner::-webkit-scrollbar, .antiscroll-inner::scrollbar {...} causes the width and height rules to be ignored by the browser resulting in both native and antiscroll scrollbars appearing at the same time. --- antiscroll.css | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/antiscroll.css b/antiscroll.css index 0dd3b27..2962629 100644 --- a/antiscroll.css +++ b/antiscroll.css @@ -44,7 +44,20 @@ overflow: scroll; } -.antiscroll-inner::-webkit-scrollbar, .antiscroll-inner::scrollbar { +/** A bug in Chrome 25 on Lion requires each selector to have their own + blocks. E.g. the following: + + .antiscroll-inner::-webkit-scrollbar, .antiscroll-inner::scrollbar {...} + + causes the width and height rules to be ignored by the browser resulting + in both native and antiscroll scrollbars appearing at the same time. + */ +.antiscroll-inner::-webkit-scrollbar { + width: 0; + height: 0; +} + +.antiscroll-inner::scrollbar { width: 0; height: 0; }
    HeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHeadHead
    BodyBodyBodyBodyBody BodyBodyBodyBodyBody