diff options
author | Jörn Zaefferer <joern.zaefferer@gmail.com> | 2011-06-19 14:45:20 +0200 |
---|---|---|
committer | Jörn Zaefferer <joern.zaefferer@gmail.com> | 2011-06-19 14:45:20 +0200 |
commit | aa7f8195f8c288dbefcb92064b68cb28064ac64c (patch) | |
tree | 333cd184f8a830c1b588e3d3d0368c134648a3b6 /ui | |
parent | 391282a9aeb4e5bb6ba6655d7f1d5d125f93155a (diff) | |
parent | fb210ae1ec16cefb1e4d4dfaf7d55499cac53ab8 (diff) | |
download | jquery-ui-aa7f8195f8c288dbefcb92064b68cb28064ac64c.tar.gz jquery-ui-aa7f8195f8c288dbefcb92064b68cb28064ac64c.zip |
Merge branch 'master' into widget-delegation
Diffstat (limited to 'ui')
-rw-r--r-- | ui/jquery.effects.blind.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.bounce.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.clip.js | 57 | ||||
-rw-r--r-- | ui/jquery.effects.core.js | 8 | ||||
-rw-r--r-- | ui/jquery.effects.drop.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.fold.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.scale.js | 51 | ||||
-rw-r--r-- | ui/jquery.effects.shake.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.slide.js | 16 | ||||
-rw-r--r-- | ui/jquery.effects.transfer.js | 14 | ||||
-rw-r--r-- | ui/jquery.ui.datepicker.js | 20 | ||||
-rw-r--r-- | ui/jquery.ui.menu.js | 251 | ||||
-rw-r--r-- | ui/jquery.ui.menubar.js | 17 | ||||
-rw-r--r-- | ui/jquery.ui.popup.js | 102 | ||||
-rw-r--r-- | ui/jquery.ui.position.js | 119 | ||||
-rw-r--r-- | ui/jquery.ui.spinner.js | 25 | ||||
-rw-r--r-- | ui/jquery.ui.widget.js | 14 |
17 files changed, 462 insertions, 242 deletions
diff --git a/ui/jquery.effects.blind.js b/ui/jquery.effects.blind.js index 8ef544faa..b6485b641 100644 --- a/ui/jquery.effects.blind.js +++ b/ui/jquery.effects.blind.js @@ -21,7 +21,7 @@ $.effects.effect.blind = function( o ) { // Create element var el = $( this ), - props = [ "position", "top", "bottom", "left", "right" ], + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], mode = $.effects.setMode( el, o.mode || "hide" ), direction = o.direction || "up", vertical = rvertical.test( direction ), diff --git a/ui/jquery.effects.bounce.js b/ui/jquery.effects.bounce.js index 9e1117ce9..78fedb0ce 100644 --- a/ui/jquery.effects.bounce.js +++ b/ui/jquery.effects.bounce.js @@ -16,7 +16,7 @@ $.effects.effect.bounce = function(o) { return this.queue( function( next ) { var el = $( this ), - props = [ "position", "top", "bottom", "left", "right" ], + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], // defaults: mode = $.effects.setMode( el, o.mode || "effect" ), diff --git a/ui/jquery.effects.clip.js b/ui/jquery.effects.clip.js index 14b358dfa..dbf0d36c9 100644 --- a/ui/jquery.effects.clip.js +++ b/ui/jquery.effects.clip.js @@ -17,47 +17,52 @@ $.effects.effect.clip = function( o ) { return this.queue( function() { // Create element - var el = $( this ), - props = ['position','top','bottom','left','right','height','width'], - mode = $.effects.setMode( el, o.mode || 'hide' ), - direction = o.direction || 'vertical', - ref = { - size: (direction == 'vertical') ? 'height' : 'width', - position: (direction == 'vertical') ? 'top' : 'left' - }, + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], + mode = $.effects.setMode( el, o.mode || "hide" ), + show = mode === "show", + direction = o.direction || "vertical", + vert = direction === "vertical", + size = vert ? "height" : "width", + position = vert ? "top" : "left", animation = {}, wrapper, animate, distance; // Save & Show - $.effects.save( el, props ); el.show(); + $.effects.save( el, props ); + el.show(); // Create Wrapper - wrapper = $.effects.createWrapper( el ).css({ - overflow: 'hidden' + wrapper = $.effects.createWrapper( el ).css({ + overflow: "hidden" }); - animate = ( el[0].tagName == 'IMG' ) ? wrapper : el; - distance = animate[ ref.size ](); + animate = ( el[0].tagName === "IMG" ) ? wrapper : el; + distance = animate[ size ](); // Shift - if ( mode == 'show' ) { - animate.css( ref.size, 0 ); - animate.css( ref.position, distance / 2 ); + if ( show ) { + animate.css( size, 0 ); + animate.css( position, distance / 2 ); } // Create Animation Object: - animation[ ref.size ] = mode == 'show' ? distance : 0; - animation[ ref.position ] = mode == 'show' ? 0 : distance / 2; + animation[ size ] = show ? distance : 0; + animation[ position ] = show ? 0 : distance / 2; // Animate - animate.animate( animation, { - queue: false, - duration: o.duration, - easing: o.easing, + animate.animate( animation, { + queue: false, + duration: o.duration, + easing: o.easing, complete: function() { - mode == 'hide' && el.hide(); - $.effects.restore( el, props ); - $.effects.removeWrapper( el ); - $.isFunction( o.complete ) && o.complete.apply( el[ 0 ], arguments ); + if ( !show ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + if ( $.isFunction( o.complete ) ) { + o.complete.apply( el[ 0 ], arguments ); + } el.dequeue(); } }); diff --git a/ui/jquery.effects.core.js b/ui/jquery.effects.core.js index 7650aa8f4..00a803360 100644 --- a/ui/jquery.effects.core.js +++ b/ui/jquery.effects.core.js @@ -410,7 +410,12 @@ $.extend( $.effects, { border: "none", margin: 0, padding: 0 - }); + }), + // Store the size in case width/height are defined in % - Fixes #5245 + size = { + width: element.width(), + height: element.height() + }; element.wrap( wrapper ); wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element @@ -438,6 +443,7 @@ $.extend( $.effects, { bottom: "auto" }); } + element.css(size); return wrapper.css( props ).show(); }, diff --git a/ui/jquery.effects.drop.js b/ui/jquery.effects.drop.js index 24fb89db0..4265b737b 100644 --- a/ui/jquery.effects.drop.js +++ b/ui/jquery.effects.drop.js @@ -17,7 +17,7 @@ $.effects.effect.drop = function( o ) { return this.queue( function() { var el = $( this ), - props = [ 'position', 'top', 'bottom', 'left', 'right', 'opacity' ], + props = [ 'position', 'top', 'bottom', 'left', 'right', 'opacity', "height", "width" ], mode = $.effects.setMode( el, o.mode || 'hide' ), direction = o.direction || 'left', ref = ( direction == 'up' || direction == 'down' ) ? 'top' : 'left', diff --git a/ui/jquery.effects.fold.js b/ui/jquery.effects.fold.js index 29da090cb..6100c33a1 100644 --- a/ui/jquery.effects.fold.js +++ b/ui/jquery.effects.fold.js @@ -18,7 +18,7 @@ $.effects.effect.fold = function( o ) { // Create element var el = $( this ), - props = ['position','top','bottom','left','right'], + props = ['position','top','bottom','left','right','height','width'], mode = $.effects.setMode(el, o.mode || 'hide'), size = o.size || 15, percent = /([0-9]+)%/.exec(size), diff --git a/ui/jquery.effects.scale.js b/ui/jquery.effects.scale.js index 00f0151af..e00d82497 100644 --- a/ui/jquery.effects.scale.js +++ b/ui/jquery.effects.scale.js @@ -116,10 +116,13 @@ $.effects.effect.size = function( o ) { // Set options mode = $.effects.setMode( el, o.mode || 'effect' ), - restore = o.restore || false, + restore = o.restore || mode !== "effect", scale = o.scale || 'both', - origin = o.origin, - original, baseline, factor; + origin = o.origin || [ "middle", "center" ], + original, baseline, factor, + position = el.css( "position" ), + originalVerticalPositioning = el.css( "bottom" ) !== "auto" ? "bottom" : "top"; + originalHorizontalPositioning = el.css( "right" ) !== "auto" ? "right" : "left"; if ( mode === "show" ) { el.show(); @@ -249,7 +252,47 @@ $.effects.effect.size = function( o ) { if( mode == 'hide' ) { el.hide(); } - $.effects.restore( el, restore ? props : props1 ); + $.effects.restore( el, restore ? props : props1 ); + + // we need to recalculate our positioning based on the new scaling + if ( position === "static" ) { + el.css({ + position: "relative", + top: el.to.top, + left: el.to.left + }); + } else { + $.each([ originalVerticalPositioning, originalHorizontalPositioning ], function( idx, pos ) { + el.css( pos, function( _, str ) { + var val = parseInt( str, 10 ), + toRef = idx ? el.to.left : el.to.top, + delta = idx ? el.to.outerWidth - el.from.outerWidth: el.to.outerHeight - el.from.outerHeight, + same = origin[ idx ] === pos, + mid = origin[ idx ] === "middle" || origin[ idx ] === "center", + direction = pos == "left" || pos == "top"; + + // if original was "auto", recalculate the new value from wrapper + if ( str === "auto" ) { + return toRef + "px"; + } + + // if not setting left or top + if ( !direction ) { + + // if the position is relative, bottom/right are reversed meaning + if ( position === "relative" ) { + toRef *= -1; + + // otherwise, if its NOT a midpoint origin, compensate for the outerWidth difference + } else if ( !mid ) { + toRef -= delta * ( same ? -1 : 1 ); + } + } + return val + toRef + "px"; + }); + }); + } + $.effects.removeWrapper( el ); $.isFunction( o.complete ) && o.complete.apply( this, arguments ); // Callback el.dequeue(); diff --git a/ui/jquery.effects.shake.js b/ui/jquery.effects.shake.js index 550329ca4..52ab331e8 100644 --- a/ui/jquery.effects.shake.js +++ b/ui/jquery.effects.shake.js @@ -17,7 +17,7 @@ $.effects.effect.shake = function( o ) { return this.queue( function() { var el = $( this ), - props = [ "position", "top", "bottom", "left", "right" ], + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], mode = $.effects.setMode( el, o.mode || "effect" ), direction = o.direction || "left", distance = o.distance || 20, diff --git a/ui/jquery.effects.slide.js b/ui/jquery.effects.slide.js index 6b0296754..ccb13fa1b 100644 --- a/ui/jquery.effects.slide.js +++ b/ui/jquery.effects.slide.js @@ -18,24 +18,26 @@ $.effects.effect.slide = function( o ) { // Create element var el = $( this ), - props = ['position','top','bottom','left','right'], + props = [ "position", "top", "bottom", "left", "right", "width", "height" ], mode = $.effects.setMode( el, o.mode || 'show' ), direction = o.direction || 'left', ref = (direction == 'up' || direction == 'down') ? 'top' : 'left', motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg', distance, - animation = {}; + animation = {}, + size; // Adjust - $.effects.save( el, props ); + $.effects.save( el, props ); el.show(); + distance = o.distance || el[ ref == 'top' ? "outerHeight" : "outerWidth" ]({ + margin: true + }); + $.effects.createWrapper( el ).css({ overflow: 'hidden' - }); - - distance = o.distance || el[ ref == 'top' ? "outerHeight" : "outerWidth" ]({ - margin: true }); + if (mode == 'show') { el.css( ref, motion == 'pos' ? (isNaN(distance) ? "-" + distance : -distance) : distance ); } diff --git a/ui/jquery.effects.transfer.js b/ui/jquery.effects.transfer.js index 17d23c5fa..f0f9d9fd4 100644 --- a/ui/jquery.effects.transfer.js +++ b/ui/jquery.effects.transfer.js @@ -17,10 +17,14 @@ $.effects.effect.transfer = function( o ) { return this.queue( function() { var elem = $( this ), target = $( o.to ), + targetFixed = target.css( "position" ) === "fixed", + body = $("body"), + fixTop = targetFixed ? body.scrollTop() : 0, + fixLeft = targetFixed ? body.scrollLeft() : 0, endPosition = target.offset(), animation = { - top: endPosition.top, - left: endPosition.left, + top: endPosition.top - fixTop , + left: endPosition.left - fixLeft , height: target.innerHeight(), width: target.innerWidth() }, @@ -29,11 +33,11 @@ $.effects.effect.transfer = function( o ) { .appendTo( document.body ) .addClass( o.className ) .css({ - top: startPosition.top, - left: startPosition.left, + top: startPosition.top - fixTop , + left: startPosition.left - fixLeft , height: elem.innerHeight(), width: elem.innerWidth(), - position: 'absolute' + position: targetFixed ? "fixed" : "absolute" }) .animate( animation, o.duration, o.easing, function() { transfer.remove(); diff --git a/ui/jquery.ui.datepicker.js b/ui/jquery.ui.datepicker.js index 4c73bdfd8..ee0a86338 100644 --- a/ui/jquery.ui.datepicker.js +++ b/ui/jquery.ui.datepicker.js @@ -105,7 +105,8 @@ function Datepicker() { altFormat: '', // The date format to use for the alternate field constrainInput: true, // The input is constrained by the current date format showButtonPanel: false, // True to show button panel, false to not show it - autoSize: false // True to size the input for the date format, false to leave as is + autoSize: false, // True to size the input for the date format, false to leave as is + disabled: false // The initial disabled state }; $.extend(this._defaults, this.regional['']); this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')); @@ -114,6 +115,9 @@ function Datepicker() { $.extend(Datepicker.prototype, { /* Class name added to elements to indicate already configured with a date picker. */ markerClassName: 'hasDatepicker', + + //Keep track of the maximum number of rows displayed (see #7043) + maxRows: 4, /* Debug logging (if enabled). */ log: function () { @@ -194,6 +198,10 @@ $.extend(Datepicker.prototype, { }); this._autoSize(inst); $.data(target, PROP_NAME, inst); + //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665) + if( inst.settings.disabled ) { + this._disableDatepicker( target ); + } }, /* Make attachments based on settings. */ @@ -273,6 +281,10 @@ $.extend(Datepicker.prototype, { this._setDate(inst, this._getDefaultDate(inst), true); this._updateDatepicker(inst); this._updateAlternate(inst); + //If disabled option is true, disable the datepicker before showing it (see ticket #5665) + if( inst.settings.disabled ) { + this._disableDatepicker( target ); + } inst.dpDiv.show(); }, @@ -682,6 +694,7 @@ $.extend(Datepicker.prototype, { /* Generate the date picker content. */ _updateDatepicker: function(inst) { var self = this; + self.maxRows = 4; //Reset the max number of rows being displayed (see #7043) var borders = $.datepicker._getBorders(inst.dpDiv); instActive = inst; // for delegate hover events inst.dpDiv.empty().append(this._generateHTML(inst)); @@ -1471,6 +1484,7 @@ $.extend(Datepicker.prototype, { var html = ''; for (var row = 0; row < numMonths[0]; row++) { var group = ''; + this.maxRows = 4; for (var col = 0; col < numMonths[1]; col++) { var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay)); var cornerClass = ' ui-corner-all'; @@ -1505,7 +1519,9 @@ $.extend(Datepicker.prototype, { if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth) inst.selectedDay = Math.min(inst.selectedDay, daysInMonth); var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; - var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate + var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate + var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043) + this.maxRows = numRows; var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays)); for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows calender += '<tr>'; diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js index 03e14f124..7dd1a9ce0 100644 --- a/ui/jquery.ui.menu.js +++ b/ui/jquery.ui.menu.js @@ -1,6 +1,6 @@ /* * jQuery UI Menu @VERSION - * + * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license @@ -12,10 +12,10 @@ * jquery.ui.widget.js */ (function($) { - + var idIncrement = 0; -$.widget("ui.menu", { +$.widget( "ui.menu", { version: "@VERSION", defaultElement: "<ul>", delay: 150, @@ -29,8 +29,8 @@ $.widget("ui.menu", { var self = this; this.activeMenu = this.element; this.menuId = this.element.attr( "id" ) || "ui-menu-" + idIncrement++; - if (this.element.find(".ui-icon").length) { - this.element.addClass("ui-menu-icons"); + if ( this.element.find( ".ui-icon" ).length ) { + this.element.addClass( "ui-menu-icons" ); } this.element .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) @@ -61,7 +61,7 @@ $.widget("ui.menu", { self.focus( event, target ); } }) - .bind("mouseout.menu", function( event ) { + .bind( "mouseout.menu", function( event ) { if ( self.options.disabled ) { return; } @@ -71,7 +71,7 @@ $.widget("ui.menu", { } }); this.refresh(); - + this.element.attr( "tabIndex", 0 ).bind( "keydown.menu", function( event ) { if ( self.options.disabled ) { return; @@ -110,8 +110,8 @@ $.widget("ui.menu", { event.preventDefault(); break; case $.ui.keyCode.ENTER: - if (self.active.children("a[aria-haspopup='true']").length) { - if (self.right( event )) { + if ( self.active.children( "a[aria-haspopup='true']" ).length ) { + if ( self.right( event ) ) { event.stopImmediatePropagation(); } } @@ -129,35 +129,39 @@ $.widget("ui.menu", { break; default: event.stopPropagation(); - clearTimeout(self.filterTimer); - var prev = self.previousFilter || ""; - var character = String.fromCharCode(event.keyCode); - var skip = false; + clearTimeout( self.filterTimer ); + var match, + prev = self.previousFilter || "", + character = String.fromCharCode( event.keyCode ), + skip = false; + if (character == prev) { skip = true; } else { character = prev + character; } - function escape(value) { - return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + function escape( value ) { + return value.replace( /[-[\]{}()*+?.,\\^$|#\s]/g , "\\$&" ); } - var match = self.activeMenu.children(".ui-menu-item").filter(function() { - return new RegExp("^" + escape(character), "i").test($(this).children("a").text()); + match = self.activeMenu.children( ".ui-menu-item" ).filter( function() { + return new RegExp("^" + escape(character), "i") + .test( $( this ).children( "a" ).text() ); }); - var match = skip && match.index(self.active.next()) != -1 ? self.active.nextAll(".ui-menu-item") : match; - if (!match.length) { + match = skip && match.index(self.active.next()) != -1 ? self.active.nextAll(".ui-menu-item") : match; + if ( !match.length ) { character = String.fromCharCode(event.keyCode); - match = self.activeMenu.children(".ui-menu-item").filter(function() { - return new RegExp("^" + escape(character), "i").test($(this).children("a").text()); + match = self.activeMenu.children(".ui-menu-item").filter( function() { + return new RegExp("^" + escape(character), "i") + .test( $( this ).children( "a" ).text() ); }); } - if (match.length) { - self.focus(event, match); + if ( match.length ) { + self.focus( event, match ); if (match.length > 1) { self.previousFilter = character; - self.filterTimer = setTimeout(function() { + self.filterTimer = setTimeout( function() { delete self.previousFilter; - }, 1000); + }, 1000 ); } else { delete self.previousFilter; } @@ -167,21 +171,21 @@ $.widget("ui.menu", { } }); }, - + _destroy: function() { //destroy (sub)menus this.element .removeAttr( "aria-activedescendant" ) - .find("ul") + .find( "ul" ) .andSelf() .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) .removeAttr( "role" ) - .removeAttr("tabIndex") + .removeAttr( "tabIndex" ) .removeAttr( "aria-labelledby" ) .removeAttr( "aria-expanded" ) .removeAttr( "aria-hidden" ) .show(); - + //destroy menu items this.element.find( ".ui-menu-item" ) .unbind( ".menu" ) @@ -193,143 +197,176 @@ $.widget("ui.menu", { .removeAttr( "role" ) .removeAttr( "aria-haspopup" ) .removeAttr( "id" ) - .children(".ui-icon").remove(); + .children( ".ui-icon" ) + .remove(); }, - + refresh: function() { - var self = this; - // initialize nested menus - var submenus = this.element.find("ul:not(.ui-menu)") - .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) - .attr("role", "menu") - .hide() - .attr("aria-hidden", "true") - .attr("aria-expanded", "false") - ; - + var self = this, + + // initialize nested menus + submenus = this.element.find( "ul:not(.ui-menu)" ) + .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) + .attr( "role", "menu" ) + .hide() + .attr( "aria-hidden", "true" ) + .attr( "aria-expanded", "false" ), + // don't refresh list items that are already adapted - var items = submenus.add(this.element).children( "li:not(.ui-menu-item):has(a)" ) - .addClass( "ui-menu-item" ) - .attr( "role", "presentation" ); - + items = submenus.add( this.element ).children( "li:not(.ui-menu-item):has(a)" ) + .addClass( "ui-menu-item" ) + .attr( "role", "presentation" ); + items.children( "a" ) .addClass( "ui-corner-all" ) .attr( "tabIndex", -1 ) .attr( "role", "menuitem" ) - .attr("id", function(i) {return self.element.attr("id") + "-" + i}); - - submenus.each(function() { - var menu = $(this); - var item = menu.prev("a") - item.attr("aria-haspopup", "true") - .prepend('<span class="ui-menu-icon ui-icon ui-icon-carat-1-e"></span>'); - menu.attr("aria-labelledby", item.attr("id")); + .attr( "id", function( i ) { + return self.element.attr( "id" ) + "-" + i; + }); + + submenus.each( function() { + var menu = $( this ), + item = menu.prev( "a" ); + + item.attr( "aria-haspopup", "true" ) + .prepend( '<span class="ui-menu-icon ui-icon ui-icon-carat-1-e"></span>' ); + menu.attr( "aria-labelledby", item.attr( "id" ) ); }); }, focus: function( event, item ) { - var self = this; - + var nested, + self = this; + this.blur(); - + if ( this._hasScroll() ) { - var borderTop = parseFloat( $.curCSS( this.element[0], "borderTopWidth", true) ) || 0, - paddingTop = parseFloat( $.curCSS( this.element[0], "paddingTop", true) ) || 0, + var borderTop = parseFloat( $.curCSS( this.element[0], "borderTopWidth", true ) ) || 0, + paddingTop = parseFloat( $.curCSS( this.element[0], "paddingTop", true ) ) || 0, offset = item.offset().top - this.element.offset().top - borderTop - paddingTop, scroll = this.element.scrollTop(), elementHeight = this.element.height(), itemHeight = item.height(); + if ( offset < 0 ) { this.element.scrollTop( scroll + offset ); } else if ( offset + itemHeight > elementHeight ) { this.element.scrollTop( scroll + offset - elementHeight + itemHeight ); } } - + this.active = item.first() .children( "a" ) .addClass( "ui-state-focus" ) .end(); - self.element.attr("aria-activedescendant", self.active.children("a").attr("id")) + self.element.attr( "aria-activedescendant", self.active.children("a").attr("id") ); // highlight active parent menu item, if any this.active.parent().closest(".ui-menu-item").children("a:first").addClass("ui-state-active"); - - self.timer = setTimeout(function() { + + self.timer = setTimeout( function() { self._close(); - }, self.delay) - var nested = $(">ul", item); - if (nested.length && /^mouse/.test(event.type)) { + }, self.delay ); + + nested = $( ">ul", item ); + if ( nested.length && ( /^mouse/.test( event.type ) ) ) { self._startOpening(nested); } this.activeMenu = item.parent(); - + this._trigger( "focus", event, { item: item } ); }, - blur: function(event) { - if (!this.active) { + blur: function( event ) { + if ( !this.active ) { return; } - - clearTimeout(this.timer); - + + clearTimeout( this.timer ); + this.active.children( "a" ).removeClass( "ui-state-focus" ); this.active = null; }, - _startOpening: function(submenu) { - clearTimeout(this.timer); + _startOpening: function( submenu ) { + clearTimeout( this.timer ); var self = this; - self.timer = setTimeout(function() { + self.timer = setTimeout( function() { self._close(); - self._open(submenu); - }, self.delay); + self._open( submenu ); + }, self.delay ); }, - - _open: function(submenu) { - clearTimeout(this.timer); - this.element.find(".ui-menu").not(submenu.parents()).hide().attr("aria-hidden", "true"); + + _open: function( submenu ) { + clearTimeout( this.timer ); + this.element + .find( ".ui-menu" ) + .not( submenu.parents() ) + .hide() + .attr( "aria-hidden", "true" ); + var position = $.extend({}, { - of: this.active - }, $.type(this.options.position) == "function" - ? this.options.position(this.active) - : this.options.position - ); - submenu.show().removeAttr("aria-hidden").attr("aria-expanded", "true").position(position); + of: this.active + }, $.type(this.options.position) == "function" + ? this.options.position(this.active) + : this.options.position + ); + + submenu.show() + .removeAttr( "aria-hidden" ) + .attr( "aria-expanded", "true" ) + .position( position ); }, - + closeAll: function() { this.element - .find("ul").hide().attr("aria-hidden", "true").attr("aria-expanded", "false").end() - .find("a.ui-state-active").removeClass("ui-state-active"); + .find( "ul" ) + .hide() + .attr( "aria-hidden", "true" ) + .attr( "aria-expanded", "false" ) + .end() + .find( "a.ui-state-active" ) + .removeClass( "ui-state-active" ); + this.blur(); this.activeMenu = this.element; }, - + _close: function() { this.active.parent() - .find("ul").hide().attr("aria-hidden", "true").attr("aria-expanded", "false").end() - .find("a.ui-state-active").removeClass("ui-state-active"); + .find( "ul" ) + .hide() + .attr( "aria-hidden", "true" ) + .attr( "aria-expanded", "false" ) + .end() + .find( "a.ui-state-active" ) + .removeClass( "ui-state-active" ); }, - left: function(event) { + left: function( event ) { var newItem = this.active && this.active.parents("li:not(.ui-menubar-item)").first(); - if (newItem && newItem.length) { - this.active.parent().attr("aria-hidden", "true").attr("aria-expanded", "false").hide(); - this.focus(event, newItem); + if ( newItem && newItem.length ) { + this.active.parent() + .attr("aria-hidden", "true") + .attr("aria-expanded", "false") + .hide(); + this.focus( event, newItem ); return true; } }, - right: function(event) { - var self= this; - var newItem = this.active && this.active.children("ul").children("li").first(); - if (newItem && newItem.length) { - this._open(newItem.parent()); - var current = this.active; + right: function( event ) { + var self = this, + newItem = this.active && this.active.children("ul").children("li").first(); + + if ( newItem && newItem.length ) { + this._open( newItem.parent() ); + //timeout so Firefox will not hide activedescendant change in expanding submenu from AT - setTimeout(function(){self.focus(event, newItem)}, 20); + setTimeout( function() { + self.focus( event, newItem ); + }, 20 ); return true; } }, @@ -350,19 +387,19 @@ $.widget("ui.menu", { return this.active && !this.active.nextAll( ".ui-menu-item" ).length; }, - _move: function(direction, edge, filter, event) { + _move: function( direction, edge, filter, event ) { if ( !this.active ) { - this.focus( event, this.activeMenu.children(edge)[filter]() ); + this.focus( event, this.activeMenu.children( edge )[ filter ]() ); return; } var next = this.active[ direction + "All" ]( ".ui-menu-item" ).eq( 0 ); if ( next.length ) { this.focus( event, next ); } else { - this.focus( event, this.activeMenu.children(edge)[filter]() ); + this.focus( event, this.activeMenu.children( edge )[ filter ]() ); } }, - + nextPage: function( event ) { if ( this._hasScroll() ) { if ( !this.active || this.last() ) { diff --git a/ui/jquery.ui.menubar.js b/ui/jquery.ui.menubar.js index a0e9afb3c..39e75924e 100644 --- a/ui/jquery.ui.menubar.js +++ b/ui/jquery.ui.menubar.js @@ -21,7 +21,11 @@ $.widget( "ui.menubar", { version: "@VERSION", options: { buttons: false, - menuIcon: false + menuIcon: false, + position: { + my: "left top", + at: "left bottom" + } }, _create: function() { var that = this; @@ -39,6 +43,9 @@ $.widget( "ui.menubar", { this._hoverable( items ); items.next( "ul" ) .menu({ + position: { + within: this.options.position.within + }, select: function( event, ui ) { ui.item.parents( "ul.ui-menu:last" ).hide(); that._trigger( "select", event, ui ); @@ -119,7 +126,7 @@ $.widget( "ui.menubar", { // TODO ui-menubar-link is added above, not needed here? input.addClass( "ui-menubar-link" ).removeClass( "ui-state-default" ); }; - + }); that._bind( { keydown: function( event ) { @@ -210,11 +217,9 @@ $.widget( "ui.menubar", { var button = menu.prev().addClass( "ui-state-active" ).attr( "tabIndex", -1 ); this.active = menu .show() - .position( { - my: "left top", - at: "left bottom", + .position( $.extend({ of: button - }) + }, this.options.position ) ) .removeAttr( "aria-hidden" ) .attr( "aria-expanded", "true" ) .menu("focus", event, menu.children( "li" ).first() ) diff --git a/ui/jquery.ui.popup.js b/ui/jquery.ui.popup.js index c90755fbb..d455346dc 100644 --- a/ui/jquery.ui.popup.js +++ b/ui/jquery.ui.popup.js @@ -13,7 +13,7 @@ * jquery.ui.position.js */ (function($) { - + var idIncrement = 0; $.widget( "ui.popup", { @@ -28,34 +28,34 @@ $.widget( "ui.popup", { if ( !this.options.trigger ) { this.options.trigger = this.element.prev(); } - + if ( !this.element.attr( "id" ) ) { this.element.attr( "id", "ui-popup-" + idIncrement++ ); this.generatedId = true; } - + if ( !this.element.attr( "role" ) ) { // TODO alternatives to tooltip are dialog and menu, all three aren't generic popups - this.element.attr( "role", "tooltip" ); + this.element.attr( "role", "dialog" ); this.generatedRole = true; } - + this.options.trigger .attr( "aria-haspopup", true ) .attr( "aria-owns", this.element.attr( "id" ) ); - + this.element - .addClass("ui-popup") + .addClass( "ui-popup" ) this.close(); this._bind(this.options.trigger, { keydown: function( event ) { - // prevent space-to-open to scroll the page, only hapens for anchor ui.button - if ( this.options.trigger.is( "a:ui-button" ) && event.keyCode == $.ui.keyCode.SPACE) { - event.preventDefault() + // prevent space-to-open to scroll the page, only happens for anchor ui.button + if ( this.options.trigger.is( "a:ui-button" ) && event.keyCode == $.ui.keyCode.SPACE ) { + event.preventDefault(); } // TODO handle SPACE to open popup? only when not handled by ui.button - if ( event.keyCode == $.ui.keyCode.SPACE && this.options.trigger.is("a:not(:ui-button)") ) { + if ( event.keyCode == $.ui.keyCode.SPACE && this.options.trigger.is( "a:not(:ui-button)" ) ) { this.options.trigger.trigger( "click", event ); } // translate keydown to click @@ -79,52 +79,75 @@ $.widget( "ui.popup", { }, 1); } }); - - this._bind(this.element, { - // TODO use focusout so that element itself doesn't need to be focussable - blur: function( event ) { + + if ( !this.element.is( ":ui-menu" ) ) { + //default use case, wrap tab order in popup + this._bind({ keydown : function( event ) { + if ( event.keyCode !== $.ui.keyCode.TAB ) { + return; + } + var tabbables = $( ":tabbable", this.element ), + first = tabbables.first(), + last = tabbables.last(); + if ( event.target === last[ 0 ] && !event.shiftKey ) { + first.focus( 1 ); + event.preventDefault(); + } else if ( event.target === first[ 0 ] && event.shiftKey ) { + last.focus( 1 ); + event.preventDefault(); + } + } + }); + } + + this._bind({ + focusout: function( event ) { var that = this; // use a timer to allow click to clear it and letting that // handle the closing instead of opening again that.closeTimer = setTimeout( function() { that.close( event ); }, 100); + }, + focusin: function( event ) { + clearTimeout( this.closeTimer ); } }); this._bind({ - // TODO only triggerd on element if it can receive focus + // TODO only triggered on element if it can receive focus // bind to document instead? // either element itself or a child should be focusable keyup: function( event ) { - if (event.keyCode == $.ui.keyCode.ESCAPE && this.element.is( ":visible" )) { + if ( event.keyCode == $.ui.keyCode.ESCAPE && this.element.is( ":visible" ) ) { this.close( event ); // TODO move this to close()? would allow menu.select to call popup.close, and get focus back to trigger this.options.trigger.focus(); } } }); - + this._bind(document, { click: function( event ) { - if (this.isOpen && !$(event.target).closest(".ui-popup").length) { + if ( this.isOpen && !$(event.target).closest(".ui-popup").length ) { this.close( event ); } } }) }, - + _destroy: function() { this.element .show() .removeClass( "ui-popup" ) .removeAttr( "aria-hidden" ) - .removeAttr( "aria-expanded" ); + .removeAttr( "aria-expanded" ) + .unbind( "keypress.ui-popup"); this.options.trigger .removeAttr( "aria-haspopup" ) .removeAttr( "aria-owns" ); - + if ( this.generatedId ) { this.element.removeAttr( "id" ); } @@ -132,7 +155,7 @@ $.widget( "ui.popup", { this.element.removeAttr( "role" ); } }, - + open: function( event ) { var position = $.extend( {}, { of: this.options.trigger @@ -142,17 +165,28 @@ $.widget( "ui.popup", { .show() .attr( "aria-hidden", false ) .attr( "aria-expanded", true ) - .position( position ) - // TODO find a focussable child, otherwise put focus on element, add tabIndex=0 if not focussable - .focus(); + .position( position ); - if (this.element.is(":ui-menu")) { - this.element.menu("focus", event, this.element.children( "li" ).first() ); + if (this.element.is( ":ui-menu" )) { //popup is a menu + this.element.menu( "focus", event, this.element.children( "li" ).first() ); + this.element.focus(); + } else { + // set focus to the first tabbable element in the popup container + // if there are no tabbable elements, set focus on the popup itself + var tabbables = this.element.find( ":tabbable" ); + this.removeTabIndex = false; + if ( !tabbables.length ) { + if ( !this.element.is(":tabbable") ) { + this.element.attr("tabindex", "0"); + this.removeTabIndex = true; + } + tabbables = tabbables.add( this.element[ 0 ] ); + } + tabbables.first().focus( 1 ); } // take trigger out of tab order to allow shift-tab to skip trigger - this.options.trigger.attr("tabindex", -1); - + this.options.trigger.attr( "tabindex", -1 ); this.isOpen = true; this._trigger( "open", event ); }, @@ -163,13 +197,13 @@ $.widget( "ui.popup", { .attr( "aria-hidden", true ) .attr( "aria-expanded", false ); - this.options.trigger.attr("tabindex", 0); - + this.options.trigger.attr( "tabindex" , 0 ); + if ( this.removeTabIndex ) { + this.element.removeAttr( "tabindex" ); + } this.isOpen = false; this._trigger( "close", event ); } - - }); }(jQuery)); diff --git a/ui/jquery.ui.position.js b/ui/jquery.ui.position.js index 98b8198e2..3bae0d010 100644 --- a/ui/jquery.ui.position.js +++ b/ui/jquery.ui.position.js @@ -19,6 +19,39 @@ var rhorizontal = /left|center|right/, center = "center", _position = $.fn.position; +$.position = { + scrollbarWidth: function() { + var w1, w2, + div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ), + innerDiv = div.children()[0]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[0].clientWidth; + } + + div.remove(); + + return w1 - w2; + }, + getScrollInfo: function( within ) { + var that = within[0], + scrollHeight = within.height() < that.scrollHeight, + scrollWidth = within.width() < that.scrollWidth, + scrollbarWidth = $.position.scrollbarWidth(); + + return { + height: scrollHeight ? scrollbarWidth : 0, + width : scrollWidth ? scrollbarWidth : 0 + }; + } +}; + $.fn.position = function( options ) { if ( !options || !options.of ) { return _position.apply( this, arguments ); @@ -28,6 +61,7 @@ $.fn.position = function( options ) { options = $.extend( {}, options ); var target = $( options.of ), + within = $( options.within || window ), targetElem = target[0], collision = ( options.collision || "flip" ).split( " " ), offsets = {}, @@ -56,7 +90,7 @@ $.fn.position = function( options ) { } // force my and at to have valid horizontal and vertical positions - // if a value is missing or invalid, it will be converted to center + // if a value is missing or invalid, it will be converted to center $.each( [ "my", "at" ], function() { var pos = ( options[ this ] || "" ).split( " " ), horizontalOffset, @@ -110,7 +144,7 @@ $.fn.position = function( options ) { parseInt( offsets.at[ 1 ], 10 ) * ( rpercent.test( offsets.at[ 1 ] ) ? targetHeight / 100 : 1 ) ]; - basePosition.left += atOffset[ 0 ], + basePosition.left += atOffset[ 0 ]; basePosition.top += atOffset[ 1 ]; return this.each(function() { @@ -119,10 +153,11 @@ $.fn.position = function( options ) { elemHeight = elem.outerHeight(), marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0, marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0, + scrollInfo = $.position.getScrollInfo( within ), collisionWidth = elemWidth + marginLeft + - ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ), + ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ) + scrollInfo.width, collisionHeight = elemHeight + marginTop + - ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ), + ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ) + scrollInfo.height, position = $.extend( {}, basePosition ), myOffset = [ parseInt( offsets.my[ 0 ], 10 ) * @@ -168,7 +203,8 @@ $.fn.position = function( options ) { collisionHeight: collisionHeight, offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], my: options.my, - at: options.at + at: options.at, + within: within }); } }); @@ -183,32 +219,40 @@ $.fn.position = function( options ) { $.ui.position = { fit: { left: function( position, data ) { - var win = $( window ), - overLeft = win.scrollLeft() - data.collisionPosition.left, - overRight = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(); + var within = data.within, + win = $( window ), + isWindow = $.isWindow( data.within[0] ), + withinOffset = isWindow ? win.scrollLeft() : within.offset().left, + outerWidth = isWindow ? win.width() : within.outerWidth(), + overLeft = withinOffset - data.collisionPosition.left, + overRight = data.collisionPosition.left + data.collisionWidth - outerWidth - withinOffset; // element is wider than window or too far left -> align with left edge - if ( data.collisionWidth > win.width() || overLeft > 0 ) { - position.left = position.left + overLeft; + if ( data.collisionWidth > outerWidth || overLeft > 0 ) { + position.left += overLeft; // too far right -> align with right edge } else if ( overRight > 0 ) { - position.left = position.left - overRight; + position.left -= overRight; // adjust based on position and margin } else { position.left = Math.max( position.left - data.collisionPosition.left, position.left ); } }, top: function( position, data ) { - var win = $( window ), - overTop = win.scrollTop() - data.collisionPosition.top, - overBottom = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(); + var within = data.within, + win = $( window ), + isWindow = $.isWindow( data.within[0] ), + withinOffset = isWindow ? win.scrollTop() : within.offset().top, + outerHeight = isWindow ? win.height() : within.outerHeight(), + overTop = withinOffset - data.collisionPosition.top, + overBottom = data.collisionPosition.top + data.collisionHeight - outerHeight - withinOffset; // element is taller than window or too far up -> align with top edge - if ( data.collisionHeight > win.height() || overTop > 0 ) { - position.top = position.top + overTop; + if ( data.collisionHeight > outerHeight || overTop > 0 ) { + position.top += overTop; // too far down -> align with bottom edge } else if ( overBottom > 0 ) { - position.top = position.top - overBottom; + position.top -= overBottom; // adjust based on position and margin } else { position.top = Math.max( position.top - data.collisionPosition.top, position.top ); @@ -220,8 +264,15 @@ $.ui.position = { if ( data.at[ 0 ] === center ) { return; } - var win = $( window ), - over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(), + + var within = data.within, + win = $( window ), + isWindow = $.isWindow( data.within[0] ), + withinOffset = isWindow ? 0 : within.offset().left, + outerWidth = isWindow ? within.width() : within.outerWidth(), + overLeft = data.collisionPosition.left - withinOffset, + overRight = data.collisionPosition.left + data.collisionWidth - outerWidth - withinOffset, + left = data.my[ 0 ] === "left", myOffset = data.my[ 0 ] === "left" ? -data.elemWidth : data.my[ 0 ] === "right" ? @@ -231,19 +282,23 @@ $.ui.position = { data.targetWidth : -data.targetWidth, offset = -2 * data.offset[ 0 ]; - position.left += data.collisionPosition.left < 0 ? - myOffset + atOffset + offset : - over > 0 ? - myOffset + atOffset + offset : - 0; + if ( overLeft < 0 || overRight > 0 ) { + position.left += myOffset + atOffset + offset; + } }, top: function( position, data ) { if ( data.at[ 1 ] === center ) { return; } - var win = $( window ), - over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(), - myOffset = data.my[ 1 ] === "top" ? + var within = data.within, + win = $( window ), + isWindow = $.isWindow( data.within[0] ), + withinOffset = isWindow ? 0 : within.offset().top, + outerHeight = isWindow ? within.height() : within.outerHeight(), + overTop = data.collisionPosition.top - withinOffset, + overBottom = data.collisionPosition.top + data.collisionHeight - outerHeight - withinOffset, + top = data.my[ 1 ] === "top", + myOffset = top ? -data.elemHeight : data.my[ 1 ] === "bottom" ? data.elemHeight : @@ -252,11 +307,9 @@ $.ui.position = { data.targetHeight : -data.targetHeight, offset = -2 * data.offset[ 1 ]; - position.top += data.collisionPosition.top < 0 ? - myOffset + atOffset + offset : - over > 0 ? - myOffset + atOffset + offset : - 0; + if ( overTop < 0 || overBottom > 0 ) { + position.top += myOffset + atOffset + offset; + } } } }; @@ -267,7 +320,7 @@ if ( $.uiBackCompat !== false ) { (function( $ ) { var _position = $.fn.position; $.fn.position = function( options ) { - if ( !options || !( "offset" in options ) ) { + if ( !options || !options.offset ) { return _position.call( this, options ); } var offset = options.offset.split( " " ), diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index b4cefc982..951b336ed 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -258,12 +258,29 @@ $.widget( "ui.spinner", { : 2 : 1); + // clamp the new value + newVal = this._trimValue( newVal ); + if ( this._trigger( "spin", event, { value: newVal } ) !== false) { this.value( newVal ); this.counter++; } }, + _trimValue: function( value ) { + var options = this.options; + + if ( value > options.max) { + return options.max; + } + + if ( value < options.min ) { + return options.min; + } + + return value; + }, + _stop: function( event ) { this.counter = 0; if ( this.timer ) { @@ -280,13 +297,7 @@ $.widget( "ui.spinner", { _setOption: function( key, value ) { if ( key === "value") { - value = this._parse( value ); - if ( value < this.options.min ) { - value = this.options.min; - } - if ( value > this.options.max ) { - value = this.options.max; - } + value = this._trimValue( this._parse(value) ); } if ( key === "disabled" ) { diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js index 1c01ef7c9..cf138f774 100644 --- a/ui/jquery.ui.widget.js +++ b/ui/jquery.ui.widget.js @@ -133,7 +133,7 @@ $.widget.bridge = function( name, object ) { } var methodValue = instance[ options ].apply( instance, args ); if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue.jquery ? + returnValue = methodValue && methodValue.jquery ? returnValue.pushStack( methodValue.get() ) : methodValue; return false; @@ -239,9 +239,6 @@ $.Widget.prototype = { } if ( typeof key === "string" ) { - if ( value === undefined ) { - return this.options[ key ]; - } // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } options = {}; parts = key.split( "." ); @@ -252,8 +249,15 @@ $.Widget.prototype = { curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; curOption = curOption[ parts[ i ] ]; } - curOption[ parts.pop() ] = value; + key = parts.pop(); + if ( value === undefined ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; } else { + if ( value === undefined ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } options[ key ] = value; } } |