aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/i18n/jquery.ui.datepicker-hu.js12
-rw-r--r--ui/i18n/jquery.ui.datepicker-kk.js (renamed from ui/i18n/jquery.ui.datepicker-kz.js)4
-rw-r--r--ui/jquery.effects.core.js40
-rw-r--r--ui/jquery.effects.scale.js21
-rw-r--r--ui/jquery.ui.accordion.js26
-rw-r--r--ui/jquery.ui.autocomplete.js58
-rw-r--r--ui/jquery.ui.button.js10
-rw-r--r--ui/jquery.ui.datepicker.js29
-rw-r--r--ui/jquery.ui.dialog.js20
-rw-r--r--ui/jquery.ui.menu.js379
-rw-r--r--ui/jquery.ui.menubar.js30
-rw-r--r--ui/jquery.ui.popup.js172
-rw-r--r--ui/jquery.ui.position.js215
-rw-r--r--ui/jquery.ui.selectable.js1
-rw-r--r--ui/jquery.ui.sortable.js19
-rw-r--r--ui/jquery.ui.spinner.js306
-rw-r--r--ui/jquery.ui.tabs.js87
-rw-r--r--ui/jquery.ui.tooltip.js6
-rw-r--r--ui/jquery.ui.widget.js30
19 files changed, 882 insertions, 583 deletions
diff --git a/ui/i18n/jquery.ui.datepicker-hu.js b/ui/i18n/jquery.ui.datepicker-hu.js
index 2eea8b240..b28c268c1 100644
--- a/ui/i18n/jquery.ui.datepicker-hu.js
+++ b/ui/i18n/jquery.ui.datepicker-hu.js
@@ -2,19 +2,19 @@
/* Written by Istvan Karaszi (jquery@spam.raszi.hu). */
jQuery(function($){
$.datepicker.regional['hu'] = {
- closeText: 'bezárás',
- prevText: '« vissza',
- nextText: 'előre »',
+ closeText: 'bezár',
+ prevText: 'vissza',
+ nextText: 'előre',
currentText: 'ma',
monthNames: ['Január', 'Február', 'Március', 'Április', 'Május', 'Június',
'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'],
monthNamesShort: ['Jan', 'Feb', 'Már', 'Ápr', 'Máj', 'Jún',
'Júl', 'Aug', 'Szep', 'Okt', 'Nov', 'Dec'],
- dayNames: ['Vasárnap', 'Hétfö', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'],
+ dayNames: ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'],
dayNamesShort: ['Vas', 'Hét', 'Ked', 'Sze', 'Csü', 'Pén', 'Szo'],
dayNamesMin: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
- weekHeader: 'Hé',
- dateFormat: 'yy-mm-dd',
+ weekHeader: 'Hét',
+ dateFormat: 'yy.mm.dd.',
firstDay: 1,
isRTL: false,
showMonthAfterYear: true,
diff --git a/ui/i18n/jquery.ui.datepicker-kz.js b/ui/i18n/jquery.ui.datepicker-kk.js
index 658c21275..dcd6a65df 100644
--- a/ui/i18n/jquery.ui.datepicker-kz.js
+++ b/ui/i18n/jquery.ui.datepicker-kk.js
@@ -1,7 +1,7 @@
/* Kazakh (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Dmitriy Karasyov (dmitriy.karasyov@gmail.com). */
jQuery(function($){
- $.datepicker.regional['kz'] = {
+ $.datepicker.regional['kk'] = {
closeText: 'Жабу',
prevText: '<Алдыңғы',
nextText: 'Келесі>',
@@ -19,5 +19,5 @@ jQuery(function($){
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['kz']);
+ $.datepicker.setDefaults($.datepicker.regional['kk']);
});
diff --git a/ui/jquery.effects.core.js b/ui/jquery.effects.core.js
index daef9cb30..233b4f96d 100644
--- a/ui/jquery.effects.core.js
+++ b/ui/jquery.effects.core.js
@@ -161,7 +161,7 @@ var classAnimationActions = [ "add", "remove", "toggle" ],
// prefix used for storing data on .data()
dataSpace = "ec.storage.";
-$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function(_, prop) {
+$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
$.fx.step[ prop ] = function( fx ) {
if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
jQuery.style( fx.elem, prop, fx.end );
@@ -171,8 +171,8 @@ $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopS
});
function getElementStyles() {
- var style = document.defaultView
- ? document.defaultView.getComputedStyle(this, null)
+ var style = this.ownerDocument.defaultView
+ ? this.ownerDocument.defaultView.getComputedStyle( this, null )
: this.currentStyle,
newStyle = {},
key,
@@ -223,8 +223,8 @@ $.effects.animateClass = function( value, duration, easing, callback ) {
return this.queue( function() {
var animated = $( this ),
- baseClass = animated.attr( "class" ),
- finalClass,
+ baseClass = animated.attr( "class" ) || "",
+ applyClassChange,
allAnimations = o.children ? animated.find( "*" ).andSelf() : animated;
// map the animated objects to store the original styles.
@@ -232,18 +232,19 @@ $.effects.animateClass = function( value, duration, easing, callback ) {
var el = $( this );
return {
el: el,
- originalStyleAttr: el.attr( "style" ) || " ",
start: getElementStyles.call( this )
};
});
// apply class change
- $.each( classAnimationActions, function(i, action) {
- if ( value[ action ] ) {
- animated[ action + "Class" ]( value[ action ] );
- }
- });
- finalClass = animated.attr( "class" );
+ applyClassChange = function() {
+ $.each( classAnimationActions, function(i, action) {
+ if ( value[ action ] ) {
+ animated[ action + "Class" ]( value[ action ] );
+ }
+ });
+ };
+ applyClassChange();
// map all animated objects again - calculate new styles and diff
allAnimations = allAnimations.map(function() {
@@ -275,16 +276,15 @@ $.effects.animateClass = function( value, duration, easing, callback ) {
$.when.apply( $, allAnimations.get() ).done(function() {
// set the final class
- animated.attr( "class", finalClass );
+ applyClassChange();
- // for each animated element
+ // for each animated element,
+ // clear all css properties that were animated
$.each( arguments, function() {
- if ( typeof this.el.attr( "style" ) === "object" ) {
- this.el.attr( "style" ).cssText = "";
- this.el.attr( "style" ).cssText = this.originalStyleAttr;
- } else {
- this.el.attr( "style", this.originalStyleAttr );
- }
+ var el = this.el;
+ $.each( this.diff, function(key) {
+ el.css( key, '' );
+ });
});
// this is guarnteed to be there if you use jQuery.speed()
diff --git a/ui/jquery.effects.scale.js b/ui/jquery.effects.scale.js
index 000fdee28..96a9269ec 100644
--- a/ui/jquery.effects.scale.js
+++ b/ui/jquery.effects.scale.js
@@ -117,9 +117,7 @@ $.effects.effect.size = function( o, done ) {
scale = o.scale || "both",
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";
+ position = el.css( "position" );
if ( mode === "show" ) {
el.show();
@@ -260,32 +258,19 @@ $.effects.effect.size = function( o, done ) {
left: el.to.left
});
} else {
- $.each([ originalVerticalPositioning, originalHorizontalPositioning ], function( idx, pos ) {
+ $.each([ "top", "left" ], 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";
+ mid = origin[ idx ] === "middle" || origin[ idx ] === "center";
// 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";
});
});
diff --git a/ui/jquery.ui.accordion.js b/ui/jquery.ui.accordion.js
index c976e3e69..8289ee81b 100644
--- a/ui/jquery.ui.accordion.js
+++ b/ui/jquery.ui.accordion.js
@@ -13,8 +13,6 @@
*/
(function( $, undefined ) {
-var lastToggle = {};
-
// TODO: use ui-accordion-header-active class and fix styling
$.widget( "ui.accordion", {
version: "@VERSION",
@@ -39,6 +37,7 @@ $.widget( "ui.accordion", {
var self = this,
options = self.options;
+ self.lastToggle = {};
self.element.addClass( "ui-accordion ui-widget ui-helper-reset" );
self.headers = self.element.find( options.header )
@@ -378,10 +377,11 @@ $.widget( "ui.accordion", {
}
animations[ animation ]({
+ widget: self,
toShow: toShow,
toHide: toHide,
- prevShow: lastToggle.toShow,
- prevHide: lastToggle.toHide,
+ prevShow: self.lastToggle.toShow,
+ prevHide: self.lastToggle.toHide,
complete: complete,
down: toShow.length && ( !toHide.length || ( toShow.index() < toHide.index() ) )
}, additional );
@@ -450,7 +450,7 @@ $.extend( $.ui.accordion, {
duration: 300
}, options, additions );
- lastToggle = options;
+ options.widget.lastToggle = options;
if ( !options.toHide.size() ) {
originalWidth = options.toShow[0].style.width;
@@ -483,11 +483,11 @@ $.extend( $.ui.accordion, {
// fix width before calculating height of hidden element
var s = options.toShow;
originalWidth = s[0].style.width;
- s.width( parseInt( s.parent().width(), 10 )
- - parseInt( s.css( "paddingLeft" ), 10 )
- - parseInt( s.css( "paddingRight" ), 10 )
- - ( parseInt( s.css( "borderLeftWidth" ), 10 ) || 0 )
- - ( parseInt( s.css( "borderRightWidth" ), 10) || 0 ) );
+ s.width( s.parent().width()
+ - parseFloat( s.css( "paddingLeft" ) )
+ - parseFloat( s.css( "paddingRight" ) )
+ - ( parseFloat( s.css( "borderLeftWidth" ) ) || 0 )
+ - ( parseFloat( s.css( "borderRightWidth" ) ) || 0 ) );
$.each( fxAttrs, function( i, prop ) {
hideProps[ prop ] = "hide";
@@ -628,8 +628,10 @@ if ( $.uiBackCompat !== false ) {
var _createIcons = prototype._createIcons;
prototype._createIcons = function() {
- this.options.icons.activeHeader = this.options.icons.activeHeader ||
- this.options.icons.headerSelected;
+ if ( this.options.icons ) {
+ this.options.icons.activeHeader = this.options.icons.activeHeader ||
+ this.options.icons.headerSelected;
+ }
_createIcons.call( this );
};
}( jQuery, jQuery.ui.accordion.prototype ) );
diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js
index 3e0163682..4e528dce4 100644
--- a/ui/jquery.ui.autocomplete.js
+++ b/ui/jquery.ui.autocomplete.js
@@ -47,11 +47,18 @@ $.widget( "ui.autocomplete", {
_create: function() {
var self = this,
- doc = this.element[ 0 ].ownerDocument,
+ // Some browsers only repeat keydown events, not keypress events,
+ // so we use the suppressKeyPress flag to determine if we've already
+ // handled the keydown event. #7269
+ // Unfortunately the code for & in keypress is the same as the up arrow,
+ // so we use the suppressKeyPressRepeat flag to avoid handling keypress
+ // events when we know the keydown event was used to modify the
+ // search term. #7799
suppressKeyPress,
+ suppressKeyPressRepeat,
suppressInput;
- this.valueMethod = this.element[ this.element.is( "input" ) ? "val" : "text" ];
+ this.valueMethod = this.element[ this.element.is( "input,textarea" ) ? "val" : "text" ];
this.element
.addClass( "ui-autocomplete-input" )
@@ -66,11 +73,13 @@ $.widget( "ui.autocomplete", {
if ( self.options.disabled || self.element.prop( "readOnly" ) ) {
suppressKeyPress = true;
suppressInput = true;
+ suppressKeyPressRepeat = true;
return;
}
suppressKeyPress = false;
suppressInput = false;
+ suppressKeyPressRepeat = false;
var keyCode = $.ui.keyCode;
switch( event.keyCode ) {
case keyCode.PAGE_UP:
@@ -110,10 +119,13 @@ $.widget( "ui.autocomplete", {
self.menu.select( event );
break;
case keyCode.ESCAPE:
- self._value( self.term );
- self.close( event );
+ if ( self.menu.element.is(":visible") ) {
+ self._value( self.term );
+ self.close( event );
+ }
break;
default:
+ suppressKeyPressRepeat = true;
// search timeout should be triggered before the input value is changed
self._searchTimeout( event );
break;
@@ -125,6 +137,9 @@ $.widget( "ui.autocomplete", {
event.preventDefault();
return;
}
+ if ( suppressKeyPressRepeat ) {
+ return;
+ }
// replicate some key handlers to allow them to repeat in Firefox and Opera
var keyCode = $.ui.keyCode;
@@ -181,7 +196,7 @@ $.widget( "ui.autocomplete", {
};
this.menu = $( "<ul></ul>" )
.addClass( "ui-autocomplete" )
- .appendTo( $( this.options.appendTo || "body", doc )[0] )
+ .appendTo( this.document.find( this.options.appendTo || "body" )[0] )
// prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
.mousedown(function( event ) {
// clicking on the scrollbar causes focus to shift to the body
@@ -191,7 +206,7 @@ $.widget( "ui.autocomplete", {
var menuElement = self.menu.element[ 0 ];
if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
setTimeout(function() {
- $( document ).one( 'mousedown', function( event ) {
+ self.document.one( 'mousedown', function( event ) {
if ( event.target !== self.element[ 0 ] &&
event.target !== menuElement &&
!$.contains( menuElement, event.target ) ) {
@@ -223,7 +238,7 @@ $.widget( "ui.autocomplete", {
previous = self.previous;
// only trigger when focus was lost (click on menu)
- if ( self.element[0] !== doc.activeElement ) {
+ if ( self.element[0] !== self.document[0].activeElement ) {
self.element.focus();
self.previous = previous;
// #6109 - IE triggers two focus events and the second
@@ -244,22 +259,24 @@ $.widget( "ui.autocomplete", {
self.close( event );
self.selectedItem = item;
- },
- blur: function( event, ui ) {
- // don't set the value of the text field if it's already correct
- // this prevents moving the cursor unnecessarily
- if ( self.menu.element.is(":visible") &&
- ( self._value() !== self.term ) ) {
- self._value( self.term );
- }
}
})
.zIndex( this.element.zIndex() + 1 )
.hide()
.data( "menu" );
+
if ( $.fn.bgiframe ) {
this.menu.element.bgiframe();
}
+
+ // turning off autocomplete prevents the browser from remembering the
+ // value when navigating through history, so we re-enable autocomplete
+ // if the page is unloaded before the widget is destroyed. #7790
+ this._bind( this.window, {
+ beforeunload: function() {
+ this.element.removeAttr( "autocomplete" );
+ }
+ });
},
_destroy: function() {
@@ -279,7 +296,7 @@ $.widget( "ui.autocomplete", {
this._initSource();
}
if ( key === "appendTo" ) {
- this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
+ this.menu.element.appendTo( this.document.find( value || "body" )[0] );
}
if ( key === "disabled" && value && this.xhr ) {
this.xhr.abort();
@@ -325,9 +342,10 @@ $.widget( "ui.autocomplete", {
_searchTimeout: function( event ) {
var self = this;
+ clearTimeout( self.searching );
self.searching = setTimeout(function() {
// only search if the value has changed
- if ( self.term != self.element.val() ) {
+ if ( self.term !== self._value() ) {
self.selectedItem = null;
self.search( null, event );
}
@@ -384,7 +402,7 @@ $.widget( "ui.autocomplete", {
this._trigger( "close", event );
}
},
-
+
_change: function( event ) {
if ( this.previous !== this._value() ) {
this._trigger( "change", event, { item: this.selectedItem } );
@@ -434,7 +452,9 @@ $.widget( "ui.autocomplete", {
_resizeMenu: function() {
var ul = this.menu.element;
ul.outerWidth( Math.max(
- ul.width( "" ).outerWidth(),
+ // Firefox wraps long text (possibly a rounding bug)
+ // so we add 1px to avoid the wrapping (#7513)
+ ul.width( "" ).outerWidth() + 1,
this.element.outerWidth()
) );
},
diff --git a/ui/jquery.ui.button.js b/ui/jquery.ui.button.js
index 89c52d007..20eb2ca89 100644
--- a/ui/jquery.ui.button.js
+++ b/ui/jquery.ui.button.js
@@ -174,7 +174,7 @@ $.widget( "ui.button", {
}
$( this ).addClass( "ui-state-active" );
lastActive = this;
- $( document ).one( "mouseup", function() {
+ self.document.one( "mouseup", function() {
lastActive = null;
});
})
@@ -319,7 +319,7 @@ $.widget( "ui.button", {
return;
}
var buttonElement = this.buttonElement.removeClass( typeClasses ),
- buttonText = $( "<span></span>" )
+ buttonText = $( "<span></span>", this.document[0] )
.addClass( "ui-button-text" )
.html( this.options.label )
.appendTo( buttonElement.empty() )
@@ -379,7 +379,7 @@ $.widget( "ui.buttonset", {
},
refresh: function() {
- var ltr = this.element.css( "direction" ) === "ltr";
+ var rtl = this.element.css( "direction" ) === "rtl";
this.buttons = this.element.find( this.options.items )
.filter( ":ui-button" )
@@ -393,10 +393,10 @@ $.widget( "ui.buttonset", {
})
.removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
.filter( ":first" )
- .addClass( ltr ? "ui-corner-left" : "ui-corner-right" )
+ .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
.end()
.filter( ":last" )
- .addClass( ltr ? "ui-corner-right" : "ui-corner-left" )
+ .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
.end()
.end();
},
diff --git a/ui/jquery.ui.datepicker.js b/ui/jquery.ui.datepicker.js
index 0b6fb2218..e09dc5a50 100644
--- a/ui/jquery.ui.datepicker.js
+++ b/ui/jquery.ui.datepicker.js
@@ -636,10 +636,10 @@ $.extend(Datepicker.prototype, {
return;
var inst = $.datepicker._getInst(input);
if ($.datepicker._curInst && $.datepicker._curInst != inst) {
- if ( $.datepicker._datepickerShowing ) {
- $.datepicker._triggerOnClose($.datepicker._curInst);
- }
$.datepicker._curInst.dpDiv.stop(true, true);
+ if ( inst && $.datepicker._datepickerShowing ) {
+ $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
+ }
}
var beforeShow = $.datepicker._get(inst, 'beforeShow');
var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
@@ -790,14 +790,6 @@ $.extend(Datepicker.prototype, {
return [position.left, position.top];
},
- /* Trigger custom callback of onClose. */
- _triggerOnClose: function(inst) {
- var onClose = this._get(inst, 'onClose');
- if (onClose)
- onClose.apply((inst.input ? inst.input[0] : null),
- [(inst.input ? inst.input.val() : ''), inst]);
- },
-
/* Hide the date picker from view.
@param input element - the input field attached to the date picker */
_hideDatepicker: function(input) {
@@ -820,8 +812,11 @@ $.extend(Datepicker.prototype, {
(showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
if (!showAnim)
postProcess();
- $.datepicker._triggerOnClose(inst);
this._datepickerShowing = false;
+ var onClose = this._get(inst, 'onClose');
+ if (onClose)
+ onClose.apply((inst.input ? inst.input[0] : null),
+ [(inst.input ? inst.input.val() : ''), inst]);
this._lastInput = null;
if (this._inDialog) {
this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
@@ -843,12 +838,16 @@ $.extend(Datepicker.prototype, {
_checkExternalClick: function(event) {
if (!$.datepicker._curInst)
return;
- var $target = $(event.target);
- if ($target[0].id != $.datepicker._mainDivId &&
+
+ var $target = $(event.target),
+ inst = $.datepicker._getInst($target[0]);
+
+ if ( ( ( $target[0].id != $.datepicker._mainDivId &&
$target.parents('#' + $.datepicker._mainDivId).length == 0 &&
!$target.hasClass($.datepicker.markerClassName) &&
!$target.hasClass($.datepicker._triggerClass) &&
- $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI))
+ $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
+ ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) )
$.datepicker._hideDatepicker();
},
diff --git a/ui/jquery.ui.dialog.js b/ui/jquery.ui.dialog.js
index 493ed07e6..9c9a9b6fc 100644
--- a/ui/jquery.ui.dialog.js
+++ b/ui/jquery.ui.dialog.js
@@ -293,7 +293,7 @@ $.widget("ui.dialog", {
// prevent tabbing out of modal dialogs
if ( options.modal ) {
- uiDialog.bind( "keypress.ui-dialog", function( event ) {
+ uiDialog.bind( "keydown.ui-dialog", function( event ) {
if ( event.keyCode !== $.ui.keyCode.TAB ) {
return;
}
@@ -372,8 +372,7 @@ $.widget("ui.dialog", {
_makeDraggable: function() {
var self = this,
- options = self.options,
- doc = $( document );
+ options = self.options;
function filteredUi( ui ) {
return {
@@ -396,8 +395,8 @@ $.widget("ui.dialog", {
},
stop: function( event, ui ) {
options.position = [
- ui.position.left - doc.scrollLeft(),
- ui.position.top - doc.scrollTop()
+ ui.position.left - self.document.scrollLeft(),
+ ui.position.top - self.document.scrollTop()
];
$( this )
.removeClass( "ui-dialog-dragging" );
@@ -715,12 +714,11 @@ $.extend( $.ui.dialog.overlay, {
$( window ).bind( "resize.dialog-overlay", $.ui.dialog.overlay.resize );
}
- var $el = ( this.oldInstances.pop() || $( "<div>" ).addClass( "ui-widget-overlay" ) )
- .appendTo( document.body )
- .css({
- width: this.width(),
- height: this.height()
- });
+ var $el = ( this.oldInstances.pop() || $( "<div>" ).addClass( "ui-widget-overlay" ) );
+ $el.appendTo( document.body ).css({
+ width: this.width(),
+ height: this.height()
+ });
if ( $.fn.bgiframe ) {
$el.bgiframe();
diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js
index 27e76d909..bf36a77fe 100644
--- a/ui/jquery.ui.menu.js
+++ b/ui/jquery.ui.menu.js
@@ -20,14 +20,16 @@ $.widget( "ui.menu", {
defaultElement: "<ul>",
delay: 150,
options: {
+ items: "ul",
position: {
my: "left top",
at: "right top"
- }
+ },
+ trigger: null
},
_create: function() {
- var self = this;
this.activeMenu = this.element;
+ this.isScrolling = false;
this.menuId = this.element.attr( "id" ) || "ui-menu-" + idIncrement++;
if ( this.element.find( ".ui-icon" ).length ) {
this.element.addClass( "ui-menu-icons" );
@@ -38,13 +40,23 @@ $.widget( "ui.menu", {
id: this.menuId,
role: "menu"
})
+ // Prevent focus from sticking to links inside menu after clicking
+ // them (focus should always stay on UL during navigation).
+ // If the link is clicked, redirect focus to the menu.
+ // TODO move to _bind below
+ .bind( "mousedown.menu", function( event ) {
+ if ( $( event.target).is( "a" ) ) {
+ event.preventDefault();
+ $( this ).focus( 1 );
+ }
+ })
// need to catch all clicks on disabled menu
// not possible through _bind
- .bind( "click.menu", function( event ) {
- if ( self.options.disabled ) {
+ .bind( "click.menu", $.proxy( function( event ) {
+ if ( this.options.disabled ) {
event.preventDefault();
}
- });
+ }, this));
this._bind({
"click .ui-menu-item:has(a)": function( event ) {
event.stopImmediatePropagation();
@@ -57,143 +69,172 @@ $.widget( "ui.menu", {
},
"mouseover .ui-menu-item": function( event ) {
event.stopImmediatePropagation();
- var target = $( event.currentTarget );
- // Remove ui-state-active class from siblings of the newly focused menu item to avoid a jump caused by adjacent elements both having a class with a border
- target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
- this.focus( event, target );
+ if ( !this.isScrolling ) {
+ var target = $( event.currentTarget );
+ // Remove ui-state-active class from siblings of the newly focused menu item to avoid a jump caused by adjacent elements both having a class with a border
+ target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
+ this.focus( event, target );
+ }
+ this.isScrolling = false;
},
+ "mouseleave": "collapseAll",
+ "mouseleave .ui-menu": "collapseAll",
"mouseout .ui-menu-item": "blur",
"focus": function( event ) {
this.focus( event, $( event.target ).children( ".ui-menu-item:first" ) );
},
- "blur": "collapseAll"
+ blur: function( event ) {
+ this._delay( function() {
+ if ( ! $.contains( this.element[0], this.document[0].activeElement ) ) {
+ this.collapseAll( event );
+ }
+ }, 0);
+ },
+ scroll: function( event ) {
+ // Keep track of scrolling to prevent mouseover from firing inadvertently when scrolling the menu
+ this.isScrolling = true;
+ }
});
this.refresh();
- this.element.attr( "tabIndex", 0 ).bind( "keydown.menu", function( event ) {
- if ( self.options.disabled ) {
- return;
- }
- switch ( event.keyCode ) {
- case $.ui.keyCode.PAGE_UP:
- self.previousPage( event );
- event.preventDefault();
- event.stopImmediatePropagation();
- break;
- case $.ui.keyCode.PAGE_DOWN:
- self.nextPage( event );
- event.preventDefault();
- event.stopImmediatePropagation();
- break;
- case $.ui.keyCode.HOME:
- self._move( "first", "first", event );
- event.preventDefault();
- event.stopImmediatePropagation();
- break;
- case $.ui.keyCode.END:
- self._move( "last", "last", event );
- event.preventDefault();
- event.stopImmediatePropagation();
- break;
- case $.ui.keyCode.UP:
- self.previous( event );
- event.preventDefault();
- event.stopImmediatePropagation();
- break;
- case $.ui.keyCode.DOWN:
- self.next( event );
- event.preventDefault();
- event.stopImmediatePropagation();
- break;
- case $.ui.keyCode.LEFT:
- if (self.collapse( event )) {
+ this.element.attr( "tabIndex", 0 );
+ this._bind({
+ "keydown": function( event ) {
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.PAGE_UP:
+ this.previousPage( event );
+ event.preventDefault();
event.stopImmediatePropagation();
- }
- event.preventDefault();
- break;
- case $.ui.keyCode.RIGHT:
- if (self.expand( event )) {
+ break;
+ case $.ui.keyCode.PAGE_DOWN:
+ this.nextPage( event );
+ event.preventDefault();
event.stopImmediatePropagation();
- }
- event.preventDefault();
- break;
- case $.ui.keyCode.ENTER:
- if ( self.active.children( "a[aria-haspopup='true']" ).length ) {
- if ( self.expand( event ) ) {
- event.stopImmediatePropagation();
- }
- }
- else {
- self.select( event );
+ break;
+ case $.ui.keyCode.HOME:
+ this._move( "first", "first", event );
+ event.preventDefault();
event.stopImmediatePropagation();
- }
- event.preventDefault();
- break;
- case $.ui.keyCode.ESCAPE:
- if ( self.collapse( event ) ) {
+ break;
+ case $.ui.keyCode.END:
+ this._move( "last", "last", event );
+ event.preventDefault();
event.stopImmediatePropagation();
- }
- event.preventDefault();
- break;
- default:
- event.stopPropagation();
- 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 , "\\$&" );
- }
- match = self.activeMenu.children( ".ui-menu-item" ).filter( function() {
- return new RegExp("^" + escape(character), "i")
- .test( $( this ).children( "a" ).text() );
- });
- 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() {
+ break;
+ case $.ui.keyCode.UP:
+ this.previous( event );
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ break;
+ case $.ui.keyCode.DOWN:
+ this.next( event );
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ break;
+ case $.ui.keyCode.LEFT:
+ if (this.collapse( event )) {
+ event.stopImmediatePropagation();
+ }
+ event.preventDefault();
+ break;
+ case $.ui.keyCode.RIGHT:
+ if (this.expand( event )) {
+ event.stopImmediatePropagation();
+ }
+ event.preventDefault();
+ break;
+ case $.ui.keyCode.ENTER:
+ if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
+ if ( this.expand( event ) ) {
+ event.stopImmediatePropagation();
+ }
+ }
+ else {
+ this.select( event );
+ event.stopImmediatePropagation();
+ }
+ event.preventDefault();
+ break;
+ case $.ui.keyCode.ESCAPE:
+ if ( this.collapse( event ) ) {
+ event.stopImmediatePropagation();
+ }
+ event.preventDefault();
+ break;
+ default:
+ event.stopPropagation();
+ clearTimeout( this.filterTimer );
+ var match,
+ prev = this.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 , "\\$&" );
+ }
+ match = this.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 > 1) {
- self.previousFilter = character;
- self.filterTimer = setTimeout( function() {
- delete self.previousFilter;
- }, 1000 );
+ match = skip && match.index(this.active.next()) != -1 ? this.active.nextAll(".ui-menu-item") : match;
+ if ( !match.length ) {
+ character = String.fromCharCode(event.keyCode);
+ match = this.activeMenu.children(".ui-menu-item").filter( function() {
+ return new RegExp("^" + escape(character), "i")
+ .test( $( this ).children( "a" ).text() );
+ });
+ }
+ if ( match.length ) {
+ this.focus( event, match );
+ if (match.length > 1) {
+ this.previousFilter = character;
+ this.filterTimer = this._delay( function() {
+ delete this.previousFilter;
+ }, 1000 );
+ } else {
+ delete this.previousFilter;
+ }
} else {
- delete self.previousFilter;
+ delete this.previousFilter;
}
- } else {
- delete self.previousFilter;
}
}
});
- this._bind( document, {
+ this._bind( this.document, {
click: function( event ) {
if ( !$( event.target ).closest( ".ui-menu" ).length ) {
this.collapseAll( event );
}
}
});
+
+ if ( this.options.trigger ) {
+ this.element.popup({
+ trigger: this.options.trigger,
+ managed: true,
+ focusPopup: $.proxy( function( event, ui ) {
+ this.focus( event, this.element.children( ".ui-menu-item" ).first() );
+ this.element.focus( 1 );
+ }, this)
+ });
+ }
},
_destroy: function() {
//destroy (sub)menus
+ if ( this.options.trigger ) {
+ this.element.popup( "destroy" );
+ }
this.element
.removeAttr( "aria-activedescendant" )
- .find( "ul" )
+ .find( ".ui-menu" )
.andSelf()
.removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
.removeAttr( "role" )
@@ -219,43 +260,38 @@ $.widget( "ui.menu", {
},
refresh: function() {
- 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" ),
+ // initialize nested menus
+ var submenus = this.element.find( this.options.items + ":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
- 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;
- });
+ var menuId = this.menuId;
+ submenus.add( this.element ).children( ":not( .ui-menu-item ):has( a )" )
+ .addClass( "ui-menu-item" )
+ .attr( "role", "presentation" )
+ .children( "a" )
+ .addClass( "ui-corner-all" )
+ .attr( "tabIndex", -1 )
+ .attr( "role", "menuitem" )
+ .attr( "id", function( i ) {
+ return menuId + "-" + 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>' );
+ .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 nested,
- self = this;
-
this.blur( event );
if ( this._hasScroll() ) {
@@ -277,18 +313,18 @@ $.widget( "ui.menu", {
.children( "a" )
.addClass( "ui-state-focus" )
.end();
- self.element.attr( "aria-activedescendant", self.active.children("a").attr("id") );
+ this.element.attr( "aria-activedescendant", this.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");
+ this.active.parent().closest( ".ui-menu-item" ).children( "a:first" ).addClass( "ui-state-active" );
- self.timer = setTimeout( function() {
- self._close();
- }, self.delay );
+ this.timer = this._delay( function() {
+ this._close();
+ }, this.delay );
- nested = $( ">ul", item );
+ var nested = $( "> .ui-menu", item );
if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
- self._startOpening(nested);
+ this._startOpening(nested);
}
this.activeMenu = item.parent();
@@ -317,11 +353,10 @@ $.widget( "ui.menu", {
return;
}
- var self = this;
- self.timer = setTimeout( function() {
- self._close();
- self._open( submenu );
- }, self.delay );
+ this.timer = this._delay( function() {
+ this._close();
+ this._open( submenu );
+ }, this.delay );
},
_open: function( submenu ) {
@@ -345,23 +380,32 @@ $.widget( "ui.menu", {
.position( position );
},
- collapseAll: function( event ) {
- this.element
- .find( "ul" )
- .hide()
- .attr( "aria-hidden", "true" )
- .attr( "aria-expanded", "false" )
- .end()
- .find( "a.ui-state-active" )
- .removeClass( "ui-state-active" );
+ collapseAll: function( event, all ) {
+
+ // if we were passed an event, look for the submenu that contains the event
+ var currentMenu = all ? this.element :
+ $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
+
+ // if we found no valid submenu ancestor, use the main menu to close all sub menus anyway
+ if ( !currentMenu.length ) {
+ currentMenu = this.element;
+ }
+
+ this._close( currentMenu );
this.blur( event );
- this.activeMenu = this.element;
+ this.activeMenu = currentMenu;
},
- _close: function() {
- this.active.parent()
- .find( "ul" )
+ // With no arguments, closes the currently active menu - if nothing is active
+ // it closes all menus. If passed an argument, it will search for menus BELOW
+ _close: function( startMenu ) {
+ if ( !startMenu ) {
+ startMenu = this.active ? this.active.parent() : this.element;
+ }
+
+ startMenu
+ .find( ".ui-menu" )
.hide()
.attr( "aria-hidden", "true" )
.attr( "aria-expanded", "false" )
@@ -371,27 +415,23 @@ $.widget( "ui.menu", {
},
collapse: function( event ) {
- var newItem = this.active && this.active.parents("li:not(.ui-menubar-item)").first();
+ var newItem = this.active && this.active.parent().closest( ".ui-menu-item", this.element );
if ( newItem && newItem.length ) {
- this.active.parent()
- .attr("aria-hidden", "true")
- .attr("aria-expanded", "false")
- .hide();
+ this._close();
this.focus( event, newItem );
return true;
}
},
expand: function( event ) {
- var self = this,
- newItem = this.active && this.active.children("ul").children("li").first();
+ var newItem = this.active && this.active.children( ".ui-menu " ).children( ".ui-menu-item" ).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 );
+ this._delay( function() {
+ this.focus( event, newItem );
}, 20 );
return true;
}
@@ -487,11 +527,16 @@ $.widget( "ui.menu", {
},
select: function( event ) {
+
// save active reference before collapseAll triggers blur
var ui = {
item: this.active
};
- this.collapseAll( event );
+ this.collapseAll( event, true );
+ if ( this.options.trigger ) {
+ $( this.options.trigger ).focus( 1 );
+ this.element.popup( "close" );
+ }
this._trigger( "select", event, ui );
}
});
diff --git a/ui/jquery.ui.menubar.js b/ui/jquery.ui.menubar.js
index 9af3aa080..673493366 100644
--- a/ui/jquery.ui.menubar.js
+++ b/ui/jquery.ui.menubar.js
@@ -94,7 +94,7 @@ $.widget( "ui.menubar", {
}
if ( ( that.open && event.type == "mouseenter" ) || event.type == "click" || that.options.autoExpand ) {
if( that.options.autoExpand ) {
- clearTimeout( that.timer );
+ clearTimeout( that.closeTimer );
}
that._open( event, menu );
@@ -123,22 +123,6 @@ $.widget( "ui.menubar", {
.attr( "aria-haspopup", "true" )
.wrapInner( "<span class='ui-button-text'></span>" );
- if ( that.options.autoExpand ) {
- input.bind( "mouseleave.menubar", function( event ) {
- that.timer = setTimeout( function() {
- that._close();
- }, 150 );
- });
- menu.bind( "mouseleave.menubar", function( event ) {
- that.timer = setTimeout( function() {
- that._close();
- }, 150 );
- })
- .bind( "mouseenter.menubar", function( event ) {
- clearTimeout( that.timer );
- });
- }
-
// TODO review if these options are a good choice, maybe they can be merged
if ( that.options.menuIcon ) {
input.addClass( "ui-state-default" ).append( "<span class='ui-button-icon-secondary ui-icon ui-icon-triangle-1-s'></span>" );
@@ -166,7 +150,17 @@ $.widget( "ui.menubar", {
focusout: function( event ) {
that.closeTimer = setTimeout( function() {
that._close( event );
- }, 100);
+ }, 150);
+ },
+ "mouseleave .ui-menubar-item": function( event ) {
+ if ( that.options.autoExpand ) {
+ that.closeTimer = setTimeout( function() {
+ that._close( event );
+ }, 150);
+ }
+ },
+ "mouseenter .ui-menubar-item": function( event ) {
+ clearTimeout( that.closeTimer );
}
});
},
diff --git a/ui/jquery.ui.popup.js b/ui/jquery.ui.popup.js
index 508209a55..128464cc2 100644
--- a/ui/jquery.ui.popup.js
+++ b/ui/jquery.ui.popup.js
@@ -14,7 +14,8 @@
*/
(function($) {
-var idIncrement = 0;
+var idIncrement = 0,
+ suppressExpandOnFocus = false;
$.widget( "ui.popup", {
version: "@VERSION",
@@ -22,6 +23,16 @@ $.widget( "ui.popup", {
position: {
my: "left top",
at: "left bottom"
+ },
+ managed: false,
+ expandOnFocus: false,
+ show: {
+ effect: "slideDown",
+ duration: "fast"
+ },
+ hide: {
+ effect: "fadeOut",
+ duration: "fast"
}
},
_create: function() {
@@ -35,9 +46,10 @@ $.widget( "ui.popup", {
}
if ( !this.element.attr( "role" ) ) {
- // TODO alternatives to tooltip are dialog and menu, all three aren't generic popups
- this.element.attr( "role", "dialog" );
- this.generatedRole = true;
+ if ( !this.options.managed ) {
+ this.element.attr( "role", "dialog" );
+ this.generatedRole = true;
+ }
}
this.options.trigger
@@ -45,42 +57,92 @@ $.widget( "ui.popup", {
.attr( "aria-owns", this.element.attr( "id" ) );
this.element
- .addClass( "ui-popup" )
- this.close();
+ .addClass( "ui-popup" );
+ this._beforeClose();
+ this.element.hide();
this._bind(this.options.trigger, {
keydown: function( event ) {
- // 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)" ) ) {
- this.options.trigger.trigger( "click", event );
- }
- // translate keydown to click
- // opens popup and let's tooltip hide itself
- if ( event.keyCode == $.ui.keyCode.DOWN ) {
- // prevent scrolling
- event.preventDefault();
- this.options.trigger.trigger( "click", event );
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.TAB:
+ // Waiting for close() will make popup hide too late, which breaks tab key behavior
+ this.element.hide();
+ this.close( event );
+ break;
+ case $.ui.keyCode.ESCAPE:
+ if ( this.isOpen ) {
+ this.close( event );
+ }
+ break;
+ case $.ui.keyCode.SPACE:
+ // prevent space-to-open to scroll the page, only happens for anchor ui.button
+ // TODO check for $.ui.button before using custom selector, once more below
+ if ( this.options.trigger.is( "a:ui-button" ) ) {
+ event.preventDefault();
+ }
+
+ else if (this.options.trigger.is( "a:not(:ui-button)" ) ) {
+ this.options.trigger.trigger( "click", event );
+ }
+ break;
+ case $.ui.keyCode.DOWN:
+ case $.ui.keyCode.UP:
+ // prevent scrolling
+ event.preventDefault();
+ clearTimeout( this.closeTimer );
+ this._delay(function() {
+ this.open( event );
+ this.focusPopup( event );
+ }, 1);
+ break;
}
},
click: function( event ) {
+ event.stopPropagation();
event.preventDefault();
+ },
+ mousedown: function( event ) {
+ var noFocus = false;
+ /* TODO: Determine in which cases focus should stay on the trigger after the popup opens
+ (should apply for any trigger that has other interaction besides opening the popup, e.g. a text field) */
+ if ( $( event.target ).is( "input" ) ) {
+ noFocus = true;
+ }
if (this.isOpen) {
- // let it propagate to close
+ suppressExpandOnFocus = true;
+ this.close();
return;
}
- var that = this;
+ this.open( event );
clearTimeout( this.closeTimer );
- setTimeout(function() {
- that.open( event );
- }, 1);
+ this._delay( function() {
+ if ( !noFocus ) {
+ this.focusPopup();
+ }
+ }, 1 );
}
});
- if ( !this.element.is( ":ui-menu" ) ) {
+ if ( this.options.expandOnFocus ) {
+ this._bind( this.options.trigger, {
+ focus : function( event ) {
+ if ( !suppressExpandOnFocus ) {
+ this._delay( function() {
+ if ( !this.isOpen ) {
+ this.open( event );
+ }
+ }, 1);
+ }
+ this._delay( function() {
+ suppressExpandOnFocus = false;
+ }, 100);
+ },
+ blur: function( event ) {
+ suppressExpandOnFocus = false;
+ }
+ });
+ }
+ if ( !this.options.managed ) {
//default use case, wrap tab order in popup
this._bind({ keydown : function( event ) {
if ( event.keyCode !== $.ui.keyCode.TAB ) {
@@ -102,38 +164,36 @@ $.widget( "ui.popup", {
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);
+ this.closeTimer = this._delay( function() {
+ this.close( event );
+ }, 150);
},
focusin: function( event ) {
clearTimeout( this.closeTimer );
+ },
+ mouseup: function( event ) {
+ clearTimeout( this.closeTimer );
}
});
this._bind({
- // 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" ) ) {
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.focusTrigger();
}
}
});
- this._bind(document, {
+ this._bind( this.document, {
click: function( event ) {
- if ( this.isOpen && !$(event.target).closest(".ui-popup").length ) {
+ if ( this.isOpen && !$( event.target ).closest( this.element.add( this.options.trigger ) ).length ) {
this.close( event );
}
}
- })
+ });
},
_destroy: function() {
@@ -161,16 +221,20 @@ $.widget( "ui.popup", {
of: this.options.trigger
}, this.options.position );
+ this._show( this.element, this.options.show );
this.element
- .show()
.attr( "aria-hidden", "false" )
.attr( "aria-expanded", "true" )
.position( position );
- if (this.element.is( ":ui-menu" )) { //popup is a menu
- this.element.menu( "focus", event, this.element.children( "li" ).first() );
- this.element.focus();
- } else {
+ // take trigger out of tab order to allow shift-tab to skip trigger
+ this.options.trigger.attr( "tabindex", -1 );
+ this.isOpen = true;
+ this._trigger( "open", event );
+ },
+
+ focusPopup: function( event ) {
+ if ( !this.options.managed ) {
// 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" );
@@ -184,18 +248,18 @@ $.widget( "ui.popup", {
}
tabbables.first().focus( 1 );
}
+ this._trigger( "focusPopup", event );
+ },
- // take trigger out of tab order to allow shift-tab to skip trigger
- this.options.trigger.attr( "tabindex", -1 );
- this.isOpen = true;
- this._trigger( "open", event );
+ focusTrigger: function( event ) {
+ suppressExpandOnFocus = true;
+ this.options.trigger.focus();
+ this._trigger( "focusTrigger", event );
},
close: function( event ) {
- this.element
- .hide()
- .attr( "aria-hidden", "true" )
- .attr( "aria-expanded", "false" );
+ this._beforeClose();
+ this._hide( this.element, this.options.hide );
this.options.trigger.attr( "tabindex" , 0 );
if ( this.removeTabIndex ) {
@@ -203,6 +267,12 @@ $.widget( "ui.popup", {
}
this.isOpen = false;
this._trigger( "close", event );
+ },
+
+ _beforeClose: function() {
+ this.element
+ .attr( "aria-hidden", "true" )
+ .attr( "aria-expanded", "false" );
}
});
diff --git a/ui/jquery.ui.position.js b/ui/jquery.ui.position.js
index 23a98b491..9a520f845 100644
--- a/ui/jquery.ui.position.js
+++ b/ui/jquery.ui.position.js
@@ -39,15 +39,16 @@ $.position = {
return w1 - w2;
},
- getScrollInfo: function( within ) {
- var that = within[0],
- scrollHeight = within.height() < that.scrollHeight,
- scrollWidth = within.width() < that.scrollWidth,
- scrollbarWidth = $.position.scrollbarWidth();
+ getScrollInfo: function(within) {
+ var notWindow = within[0] !== window,
+ overflowX = notWindow ? within.css( "overflow-x" ) : "",
+ overflowY = notWindow ? within.css( "overflow-y" ) : "",
+ scrollbarWidth = overflowX === "auto" || overflowX === "scroll" ? $.position.scrollbarWidth() : 0,
+ scrollbarHeight = overflowY === "auto" || overflowY === "scroll" ? $.position.scrollbarWidth() : 0;
return {
- height: scrollHeight ? scrollbarWidth : 0,
- width : scrollWidth ? scrollbarWidth : 0
+ height: within.height() < within[0].scrollHeight ? scrollbarHeight : 0,
+ width: within.width() < within[0].scrollWidth ? scrollbarWidth : 0
};
}
};
@@ -182,13 +183,15 @@ $.fn.position = function( options ) {
position.left += myOffset[ 0 ];
position.top += myOffset[ 1 ];
- // prevent fractions (see #5280)
- position.left = Math.round( position.left );
- position.top = Math.round( position.top );
+ // if the browser doesn't support fractions, then round for consistent results
+ if ( !$.support.offsetFractions ) {
+ position.left = Math.round( position.left );
+ position.top = Math.round( position.top );
+ }
collisionPosition = {
- left: position.left - marginLeft,
- top: position.top - marginTop
+ marginLeft: marginLeft,
+ marginTop: marginTop
};
$.each( [ "left", "top" ], function( i, dir ) {
@@ -225,18 +228,38 @@ $.ui.position = {
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 > outerWidth || overLeft > 0 ) {
+ collisionPosLeft = position.left - data.collisionPosition.marginLeft,
+ overLeft = withinOffset - collisionPosLeft,
+ overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
+ newOverRight,
+ newOverLeft;
+
+ // element is wider than within
+ if ( data.collisionWidth > outerWidth ) {
+ // element is initially over the left side of within
+ if ( overLeft > 0 && overRight <= 0 ) {
+ newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
+ position.left += overLeft - newOverRight;
+ // element is initially over right side of within
+ } else if ( overRight > 0 && overLeft <= 0 ) {
+ position.left = withinOffset;
+ // element is initially over both left and right sides of within
+ } else {
+ if ( overLeft > overRight ) {
+ position.left = withinOffset + outerWidth - data.collisionWidth;
+ } else {
+ position.left = withinOffset;
+ }
+ }
+ // too far left -> align with left edge
+ } else if ( overLeft > 0 ) {
position.left += overLeft;
// too far right -> align with right edge
} else if ( overRight > 0 ) {
position.left -= overRight;
// adjust based on position and margin
} else {
- position.left = Math.max( position.left - data.collisionPosition.left, position.left );
+ position.left = Math.max( position.left - collisionPosLeft, position.left );
}
},
top: function( position, data ) {
@@ -245,18 +268,38 @@ $.ui.position = {
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 > outerHeight || overTop > 0 ) {
+ collisionPosTop = position.top - data.collisionPosition.marginTop,
+ overTop = withinOffset - collisionPosTop,
+ overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
+ newOverTop,
+ newOverBottom;
+
+ // element is taller than within
+ if ( data.collisionHeight > outerHeight ) {
+ // element is initially over the top of within
+ if ( overTop > 0 && overBottom <= 0 ) {
+ newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
+ position.top += overTop - newOverBottom;
+ // element is initially over bottom of within
+ } else if ( overBottom > 0 && overTop <= 0 ) {
+ position.top = withinOffset;
+ // element is initially over both top and bottom of within
+ } else {
+ if ( overTop > overBottom ) {
+ position.top = withinOffset + outerHeight - data.collisionHeight;
+ } else {
+ position.top = withinOffset;
+ }
+ }
+ // too far up -> align with top
+ } else if ( overTop > 0 ) {
position.top += overTop;
// too far down -> align with bottom edge
} else if ( overBottom > 0 ) {
position.top -= overBottom;
// adjust based on position and margin
} else {
- position.top = Math.max( position.top - data.collisionPosition.top, position.top );
+ position.top = Math.max( position.top - collisionPosTop, position.top );
}
}
},
@@ -268,14 +311,15 @@ $.ui.position = {
data.elem
.removeClass( "ui-flipped-left ui-flipped-right" );
-
+
var within = data.within,
win = $( window ),
isWindow = $.isWindow( data.within[0] ),
- withinOffset = isWindow ? 0 : within.offset().left,
+ withinOffset = ( isWindow ? 0 : within.offset().left ) + within.scrollLeft(),
outerWidth = isWindow ? within.width() : within.outerWidth(),
- overLeft = data.collisionPosition.left - withinOffset,
- overRight = data.collisionPosition.left + data.collisionWidth - outerWidth - withinOffset,
+ collisionPosLeft = position.left - data.collisionPosition.marginLeft,
+ overLeft = collisionPosLeft - withinOffset,
+ overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
left = data.my[ 0 ] === "left",
myOffset = data.my[ 0 ] === "left" ?
-data.elemWidth :
@@ -285,30 +329,45 @@ $.ui.position = {
atOffset = data.at[ 0 ] === "left" ?
data.targetWidth :
-data.targetWidth,
- offset = -2 * data.offset[ 0 ];
- if ( overLeft < 0 || overRight > 0 ) {
-
- data.elem
- .addClass( "ui-flipped-" + ( overLeft < 0 ? "right" : "left" ) );
-
- position.left += myOffset + atOffset + offset;
+ offset = -2 * data.offset[ 0 ],
+ newOverRight,
+ newOverLeft;
+
+ if ( overLeft < 0 ) {
+ newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
+ if ( newOverRight < 0 || newOverRight < Math.abs( overLeft ) ) {
+ data.elem
+ .addClass( "ui-flipped-right" );
+
+ position.left += myOffset + atOffset + offset;
+ }
+ }
+ else if ( overRight > 0 ) {
+ newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - withinOffset;
+ if ( newOverLeft > 0 || Math.abs( newOverLeft ) < overRight ) {
+ data.elem
+ .addClass( "ui-flipped-left" );
+
+ position.left += myOffset + atOffset + offset;
+ }
}
},
top: function( position, data ) {
if ( data.at[ 1 ] === center ) {
return;
}
-
+
data.elem
.removeClass( "ui-flipped-top ui-flipped-bottom" );
-
+
var within = data.within,
win = $( window ),
isWindow = $.isWindow( data.within[0] ),
- withinOffset = isWindow ? 0 : within.offset().top,
+ withinOffset = ( isWindow ? 0 : within.offset().top ) + within.scrollTop(),
outerHeight = isWindow ? within.height() : within.outerHeight(),
- overTop = data.collisionPosition.top - withinOffset,
- overBottom = data.collisionPosition.top + data.collisionHeight - outerHeight - withinOffset,
+ collisionPosTop = position.top - data.collisionPosition.marginTop,
+ overTop = collisionPosTop - withinOffset,
+ overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
top = data.my[ 1 ] === "top",
myOffset = top ?
-data.elemHeight :
@@ -318,18 +377,80 @@ $.ui.position = {
atOffset = data.at[ 1 ] === "top" ?
data.targetHeight :
-data.targetHeight,
- offset = -2 * data.offset[ 1 ];
- if ( overTop < 0 || overBottom > 0 ) {
-
- data.elem
- .addClass( "ui-flipped-" + ( overTop < 0 ? "bottom" : "top" ) );
-
- position.top += myOffset + atOffset + offset;
+ offset = -2 * data.offset[ 1 ],
+ newOverTop,
+ newOverBottom;
+ if ( overTop < 0 ) {
+ newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
+ if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < Math.abs( overTop ) ) ) {
+ data.elem
+ .addClass( "ui-flipped-bottom" );
+
+ position.top += myOffset + atOffset + offset;
+ }
+ }
+ else if ( overBottom > 0 ) {
+ newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - withinOffset;
+ if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || Math.abs( newOverTop ) < overBottom ) ) {
+ data.elem
+ .addClass( "ui-flipped-top" );
+
+ position.top += myOffset + atOffset + offset;
+ }
}
}
+ },
+ flipfit: {
+ left: function() {
+ $.ui.position.flip.left.apply( this, arguments );
+ $.ui.position.fit.left.apply( this, arguments );
+ },
+ top: function() {
+ $.ui.position.flip.top.apply( this, arguments );
+ $.ui.position.fit.top.apply( this, arguments );
+ }
}
};
+// fraction support test
+(function () {
+ var testElement, testElementParent, testElementStyle, offsetLeft, i
+ body = document.getElementsByTagName( "body" )[ 0 ],
+ div = document.createElement( "div" );
+
+ //Create a "fake body" for testing based on method used in jQuery.support
+ testElement = document.createElement( body ? "div" : "body" );
+ testElementStyle = {
+ visibility: "hidden",
+ width: 0,
+ height: 0,
+ border: 0,
+ margin: 0,
+ background: "none"
+ };
+ if ( body ) {
+ jQuery.extend( testElementStyle, {
+ position: "absolute",
+ left: "-1000px",
+ top: "-1000px"
+ });
+ }
+ for ( i in testElementStyle ) {
+ testElement.style[ i ] = testElementStyle[ i ];
+ }
+ testElement.appendChild( div );
+ testElementParent = body || document.documentElement;
+ testElementParent.insertBefore( testElement, testElementParent.firstChild );
+
+ div.style.cssText = "position: absolute; left: 10.7432222px;";
+
+ offsetLeft = $( div ).offset().left;
+ $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
+
+ testElement.innerHTML = "";
+ testElementParent.removeChild( testElement );
+})();
+
// DEPRECATED
if ( $.uiBackCompat !== false ) {
// offset option
diff --git a/ui/jquery.ui.selectable.js b/ui/jquery.ui.selectable.js
index 75f1cee66..d7b24d4e3 100644
--- a/ui/jquery.ui.selectable.js
+++ b/ui/jquery.ui.selectable.js
@@ -34,6 +34,7 @@ $.widget("ui.selectable", $.ui.mouse, {
var selectees;
this.refresh = function() {
selectees = $(self.options.filter, self.element[0]);
+ selectees.addClass("ui-selectee");
selectees.each(function() {
var $this = $(this);
var pos = $this.offset();
diff --git a/ui/jquery.ui.sortable.js b/ui/jquery.ui.sortable.js
index 99798a915..62d227a3d 100644
--- a/ui/jquery.ui.sortable.js
+++ b/ui/jquery.ui.sortable.js
@@ -63,13 +63,11 @@ $.widget("ui.sortable", $.ui.mouse, {
destroy: function() {
this.element
- .removeClass("ui-sortable ui-sortable-disabled")
- .removeData("sortable")
- .unbind(".sortable");
+ .removeClass("ui-sortable ui-sortable-disabled");
this._mouseDestroy();
for ( var i = this.items.length - 1; i >= 0; i-- )
- this.items[i].item.removeData("sortable-item");
+ this.items[i].item.removeData(this.widgetName + "-item");
return this;
},
@@ -86,6 +84,7 @@ $.widget("ui.sortable", $.ui.mouse, {
},
_mouseCapture: function(event, overrideHandle) {
+ var that = this;
if (this.reverting) {
return false;
@@ -98,12 +97,12 @@ $.widget("ui.sortable", $.ui.mouse, {
//Find out if the clicked node (or one of its parents) is a actual item in this.items
var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
- if($.data(this, 'sortable-item') == self) {
+ if($.data(this, that.widgetName + '-item') == self) {
currentItem = $(this);
return false;
}
});
- if($.data(event.target, 'sortable-item') == self) currentItem = $(event.target);
+ if($.data(event.target, that.widgetName + '-item') == self) currentItem = $(event.target);
if(!currentItem) return false;
if(this.options.handle && !overrideHandle) {
@@ -528,7 +527,7 @@ $.widget("ui.sortable", $.ui.mouse, {
for (var i = connectWith.length - 1; i >= 0; i--){
var cur = $(connectWith[i]);
for (var j = cur.length - 1; j >= 0; j--){
- var inst = $.data(cur[j], 'sortable');
+ var inst = $.data(cur[j], this.widgetName);
if(inst && inst != this && !inst.options.disabled) {
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]);
}
@@ -550,7 +549,7 @@ $.widget("ui.sortable", $.ui.mouse, {
_removeCurrentsFromItems: function() {
- var list = this.currentItem.find(":data(sortable-item)");
+ var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
for (var i=0; i < this.items.length; i++) {
@@ -576,7 +575,7 @@ $.widget("ui.sortable", $.ui.mouse, {
for (var i = connectWith.length - 1; i >= 0; i--){
var cur = $(connectWith[i]);
for (var j = cur.length - 1; j >= 0; j--){
- var inst = $.data(cur[j], 'sortable');
+ var inst = $.data(cur[j], this.widgetName);
if(inst && inst != this && !inst.options.disabled) {
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
this.containers.push(inst);
@@ -592,7 +591,7 @@ $.widget("ui.sortable", $.ui.mouse, {
for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
var item = $(_queries[j]);
- item.data('sortable-item', targetData); // Data for target checking (mouse manager)
+ item.data(this.widgetName + '-item', targetData); // Data for target checking (mouse manager)
items.push({
item: item,
diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js
index 9f4a6b921..5623722db 100644
--- a/ui/jquery.ui.spinner.js
+++ b/ui/jquery.ui.spinner.js
@@ -10,15 +10,16 @@
* Depends:
* jquery.ui.core.js
* jquery.ui.widget.js
+ * jquery.ui.button.js
*/
(function( $ ) {
function modifier( fn ) {
return function() {
- var previous = this.options.value;
+ var previous = this.element.val();
fn.apply( this, arguments );
this._refresh();
- if ( previous !== this.options.value ) {
+ if ( previous !== this.element.val() ) {
this._trigger( "change" );
}
};
@@ -29,13 +30,13 @@ $.widget( "ui.spinner", {
defaultElement: "<input>",
widgetEventPrefix: "spin",
options: {
+ culture: null,
incremental: true,
- max: Number.MAX_VALUE,
- min: -Number.MAX_VALUE,
+ max: null,
+ min: null,
numberFormat: null,
page: 10,
step: 1,
- value: 0,
change: null,
spin: null,
@@ -44,17 +45,26 @@ $.widget( "ui.spinner", {
},
_create: function() {
- this._value( this.options.value );
+ this._value( this.element.val(), true );
this._draw();
- this._mousewheel();
+ this._bind( this._events );
this._refresh();
+
+ // turning off autocomplete prevents the browser from remembering the
+ // value when navigating through history, so we re-enable autocomplete
+ // if the page is unloaded before the widget is destroyed. #7790
+ this._bind( this.window, {
+ beforeunload: function() {
+ this.element.removeAttr( "autocomplete" );
+ }
+ });
},
_getCreateOptions: function() {
var options = {},
element = this.element;
- $.each( [ "min", "max", "step", "value" ], function( i, option ) {
+ $.each( [ "min", "max", "step" ], function( i, option ) {
var value = element.attr( option );
if ( value !== undefined && value.length ) {
options[ option ] = value;
@@ -64,6 +74,72 @@ $.widget( "ui.spinner", {
return options;
},
+ _events: {
+ keydown: function( event ) {
+ if ( this._start( event ) && this._keydown( event ) ) {
+ event.preventDefault();
+ }
+ },
+ keyup: "_stop",
+ focus: function() {
+ this.uiSpinner.addClass( "ui-state-active" );
+ this.previous = this.element.val();
+ },
+ blur: function( event ) {
+ this._refresh();
+ this.uiSpinner.removeClass( "ui-state-active" );
+ if ( this.previous !== this.element.val() ) {
+ this._trigger( "change", event );
+ }
+ },
+ mousewheel: function( event, delta ) {
+ if ( !delta ) {
+ return;
+ }
+ if ( !this.spinning && !this._start( event ) ) {
+ return false;
+ }
+
+ this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
+ clearTimeout( this.mousewheelTimer );
+ this.mousewheelTimer = this._delay(function() {
+ if ( this.spinning ) {
+ this._stop( event );
+ }
+ }, 100 );
+ event.preventDefault();
+ },
+ "mousedown .ui-spinner-button": function( event ) {
+ // ensure focus is on (or stays on) the text field
+ event.preventDefault();
+ if ( this.document[0].activeElement !== this.element[ 0 ] ) {
+ this.element.focus();
+ }
+
+ if ( this._start( event ) === false ) {
+ return;
+ }
+
+ this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
+ },
+ "mouseup .ui-spinner-button": "_stop",
+ "mouseenter .ui-spinner-button": function( event ) {
+ // button will add ui-state-active if mouse was down while mouseleave and kept down
+ if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
+ return;
+ }
+
+ if ( this._start( event ) === false ) {
+ return false;
+ }
+ this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
+ },
+ // TODO: do we really want to consider this a stop?
+ // shouldn't we just stop the repeater and wait until mouseup before
+ // we trigger the stop event?
+ "mouseleave .ui-spinner-button": "_stop"
+ },
+
_draw: function() {
var uiSpinner = this.uiSpinner = this.element
.addClass( "ui-spinner-input" )
@@ -75,69 +151,19 @@ $.widget( "ui.spinner", {
this._hoverable( uiSpinner );
this.element.attr( "role", "spinbutton" );
- this._bind({
- keydown: function( event ) {
- if ( this._start( event ) && this._keydown( event ) ) {
- event.preventDefault();
- }
- },
- keyup: "_stop",
- focus: function() {
- uiSpinner.addClass( "ui-state-active" );
- this.previous = this.element.val();
- },
- blur: function( event ) {
- // don't clear invalid values on blur
- var value = this.element.val();
- this._value( value );
- if ( this.element.val() === "" ) {
- this.element.val( value );
- }
- uiSpinner.removeClass( "ui-state-active" );
- // TODO: what should trigger change?
- // element.val() or options.value?
- if ( this.previous !== this.element.val() ) {
- this._trigger( "change", event );
- }
- }
- });
// button bindings
this.buttons = uiSpinner.find( ".ui-spinner-button" )
.attr( "tabIndex", -1 )
.button()
.removeClass( "ui-corner-all" );
- this._bind( this.buttons, {
- mousedown: function( event ) {
- // ensure focus is on (or stays on) the text field
- event.preventDefault();
- if ( document.activeElement !== this.element[ 0 ] ) {
- this.element.focus();
- }
-
- if ( this._start( event ) === false ) {
- return;
- }
- this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
- },
- mouseup: "_stop",
- mouseenter: function( event ) {
- // button will add ui-state-active if mouse was down while mouseleave and kept down
- if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
- return;
- }
-
- if ( this._start( event ) === false ) {
- return false;
- }
- this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
- },
- // TODO: do we really want to consider this a stop?
- // shouldn't we just stop the repeater and wait until mouseup before
- // we trigger the stop event?
- mouseleave: "_stop"
- });
+ // IE 6 doesn't understand height: 50% for the buttons
+ // unless the wrapper has an explicit height
+ if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
+ uiSpinner.height() > 0 ) {
+ uiSpinner.height( uiSpinner.height() );
+ }
// disable spinner if element was already disabled
if ( this.options.disabled ) {
@@ -162,39 +188,11 @@ $.widget( "ui.spinner", {
case keyCode.PAGE_DOWN:
this._repeat( null, -options.page, event );
return true;
- case keyCode.ENTER:
- this._value( this.element.val() );
}
return false;
},
- _mousewheel: function() {
- // need the delta normalization that mousewheel plugin provides
- if ( !$.fn.mousewheel ) {
- return;
- }
- this._bind({
- mousewheel: function( event, delta ) {
- if ( !delta ) {
- return;
- }
- if ( !this.spinning && !this._start( event ) ) {
- return false;
- }
-
- this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
- clearTimeout( this.mousewheelTimer );
- this.mousewheelTimer = setTimeout(function() {
- if ( this.spinning ) {
- this._stop( event );
- }
- }, 100 );
- event.preventDefault();
- }
- });
- },
-
_uiSpinnerHtml: function() {
return "<span class='ui-spinner ui-state-default ui-widget ui-widget-content ui-corner-all'></span>";
},
@@ -222,47 +220,78 @@ $.widget( "ui.spinner", {
},
_repeat: function( i, steps, event ) {
- var that = this;
i = i || 500;
clearTimeout( this.timer );
- this.timer = setTimeout(function() {
- that._repeat( 40, steps, event );
+ this.timer = this._delay(function() {
+ this._repeat( 40, steps, event );
}, i );
this._spin( steps * this.options.step, event );
},
_spin: function( step, event ) {
+ var value = this.value() || 0;
+
if ( !this.counter ) {
this.counter = 1;
}
- var newVal = this.value() + step * this._increment( this.counter );
+ value = this._adjustValue( value + step * this._increment( this.counter ) );
- // clamp the new value
- newVal = this._trimValue( newVal );
-
- if ( !this.spinning || this._trigger( "spin", event, { value: newVal } ) !== false) {
- this._value( newVal );
+ if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
+ this._value( value );
this.counter++;
}
},
_increment: function( i ) {
- return this.options.incremental ?
- Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 ) :
- 1;
+ var incremental = this.options.incremental;
+
+ if ( incremental ) {
+ return $.isFunction( incremental ) ?
+ incremental( i ) :
+ Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
+ }
+
+ return 1;
},
- _trimValue: function( value ) {
- var options = this.options;
+ _precision: function() {
+ var precision = this._precisionOf( this.options.step );
+ if ( this.options.min !== null ) {
+ precision = Math.max( precision, this._precisionOf( this.options.min ) );
+ }
+ return precision;
+ },
+
+ _precisionOf: function( num ) {
+ var str = num.toString(),
+ decimal = str.indexOf( "." );
+ return decimal === -1 ? 0 : str.length - decimal - 1;
+ },
- if ( value > options.max) {
+ _adjustValue: function( value ) {
+ var base, aboveMin,
+ options = this.options;
+
+ // make sure we're at a valid step
+ // - find out where we are relative to the base (min or 0)
+ base = options.min !== null ? options.min : 0;
+ aboveMin = value - base;
+ // - round to the nearest step
+ aboveMin = Math.round(aboveMin / options.step) * options.step;
+ // - rounding is based on 0, so adjust back to our base
+ value = base + aboveMin;
+
+ // fix precision from bad JS floating point math
+ value = parseFloat( value.toFixed( this._precision() ) );
+
+ // clamp the value
+ if ( options.max !== null && value > options.max) {
return options.max;
}
-
- if ( value < options.min ) {
+ if ( options.min !== null && value < options.min ) {
return options.min;
}
@@ -282,8 +311,11 @@ $.widget( "ui.spinner", {
},
_setOption: function( key, value ) {
- if ( key === "value" ) {
- return this._value( value );
+ if ( key === "culture" || key === "numberFormat" ) {
+ var prevValue = this._parse( this.element.val() );
+ this.options[ key ] = value;
+ this.element.val( this._format( prevValue ) );
+ return;
}
this._super( "_setOption", key, value );
@@ -301,36 +333,48 @@ $.widget( "ui.spinner", {
_setOptions: modifier(function( options ) {
this._super( "_setOptions", options );
-
- // handle any options that might cause value to change, e.g., min
- this._value( this._trimValue( this.options.value ) );
+ this._value( this.element.val() );
}),
_parse: function( val ) {
- if ( typeof val === "string" ) {
- val = window.Globalize && this.options.numberFormat ? Globalize.parseFloat( val ) : +val;
+ if ( typeof val === "string" && val !== "" ) {
+ val = window.Globalize && this.options.numberFormat ?
+ Globalize.parseFloat( val, 10, this.options.culture ) : +val;
}
- return isNaN( val ) ? null : val;
+ return val === "" || isNaN( val ) ? null : val;
},
- _format: function() {
- var num = this.options.value;
- return window.Globalize && this.options.numberFormat ? Globalize.format( num, this.options.numberFormat ) : num;
+ _format: function( value ) {
+ if ( value === "" ) {
+ return "";
+ }
+ return window.Globalize && this.options.numberFormat ?
+ Globalize.format( value, this.options.numberFormat, this.options.culture ) :
+ value;
},
_refresh: function() {
- this.element
- .val( this._format() )
- .attr({
- "aria-valuemin": this.options.min,
- "aria-valuemax": this.options.max,
- "aria-valuenow": this.options.value
- });
+ this.element.attr({
+ "aria-valuemin": this.options.min,
+ "aria-valuemax": this.options.max,
+ // TODO: what should we do with values that can't be parsed?
+ "aria-valuenow": this._parse( this.element.val() )
+ });
},
// update the value without triggering change
- _value: function( value ) {
- this.options.value = this._trimValue( this._parse(value) );
+ _value: function( value, allowAny ) {
+ var parsed;
+ if ( value !== "" ) {
+ parsed = this._parse( value );
+ if ( parsed !== null ) {
+ if ( !allowAny ) {
+ parsed = this._adjustValue( parsed );
+ }
+ value = this._format( parsed );
+ }
+ }
+ this.element.val( value );
this._refresh();
},
@@ -371,9 +415,9 @@ $.widget( "ui.spinner", {
value: function( newVal ) {
if ( !arguments.length ) {
- return this.options.value;
+ return this._parse( this.element.val() );
}
- this.option( "value", newVal );
+ modifier( this._value ).call( this, newVal );
},
widget: function() {
diff --git a/ui/jquery.ui.tabs.js b/ui/jquery.ui.tabs.js
index 62bab2d9f..5eaab1aae 100644
--- a/ui/jquery.ui.tabs.js
+++ b/ui/jquery.ui.tabs.js
@@ -18,6 +18,19 @@ function getNextTabId() {
return ++tabId;
}
+var isLocal = (function() {
+ var rhash = /#.*$/,
+ currentPage = location.href.replace( rhash, "" );
+
+ return function( anchor ) {
+ // clone the node to work around IE 6 not normalizing the href property
+ // if it's manually set, i.e., a.href = "#foo" kills the normalization
+ anchor = anchor.cloneNode( false );
+ return anchor.hash.length > 1 &&
+ anchor.href.replace( rhash, "" ) === currentPage;
+ };
+})();
+
$.widget( "ui.tabs", {
version: "@VERSION",
options: {
@@ -197,8 +210,7 @@ $.widget( "ui.tabs", {
},
_processTabs: function() {
- var self = this,
- fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
+ var self = this;
this.list = this.element.find( "ol,ul" ).eq( 0 );
this.lis = $( " > li:has(a[href])", this.list );
@@ -208,30 +220,14 @@ $.widget( "ui.tabs", {
this.panels = $( [] );
this.anchors.each(function( i, a ) {
- var href = $( a ).attr( "href" ),
- hrefBase = href.split( "#" )[ 0 ],
- selector,
- panel,
- baseEl;
-
- // For dynamically created HTML that contains a hash as href IE < 8 expands
- // such href to the full page url with hash and then misinterprets tab as ajax.
- // Same consideration applies for an added tab with a fragment identifier
- // since a[href=#fragment-identifier] does unexpectedly not match.
- // Thus normalize href attribute...
- if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] ||
- ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) {
- href = a.hash;
- a.href = href;
- }
+ var selector, panel;
// inline tab
- if ( fragmentId.test( href ) ) {
- selector = href;
+ if ( isLocal( a ) ) {
+ selector = a.hash;
panel = self.element.find( self._sanitizeSelector( selector ) );
// remote tab
- // prevent loading the page itself if href is just "#"
- } else if ( href && href !== "#" ) {
+ } else {
var id = self._tabId( a );
selector = "#" + id;
panel = self.element.find( selector );
@@ -239,9 +235,6 @@ $.widget( "ui.tabs", {
panel = self._createPanel( id );
panel.insertAfter( self.panels[ i - 1 ] || self.list );
}
- // invalid tab href
- } else {
- self.options.disabled.push( i );
}
if ( panel.length) {
@@ -525,21 +518,18 @@ $.widget( "ui.tabs", {
options = this.options,
anchor = this.anchors.eq( index ),
panel = self._getPanelForTab( anchor ),
- // TODO until #3808 is fixed strip fragment identifier from url
- // (IE fails to load from such url)
- url = anchor.attr( "href" ).replace( /#.*$/, "" ),
eventData = {
tab: anchor,
panel: panel
};
// not remote
- if ( !url ) {
+ if ( isLocal( anchor[ 0 ] ) ) {
return;
}
this.xhr = $.ajax({
- url: url,
+ url: anchor.attr( "href" ),
beforeSend: function( jqXHR, settings ) {
return self._trigger( "beforeLoad", event,
$.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
@@ -551,19 +541,27 @@ $.widget( "ui.tabs", {
this.xhr
.success(function( response ) {
- panel.html( response );
- self._trigger( "load", event, eventData );
+ // TODO: IE resolves cached XHRs immediately
+ // remove when core #10467 is fixed
+ setTimeout(function() {
+ panel.html( response );
+ self._trigger( "load", event, eventData );
+ }, 1 );
})
.complete(function( jqXHR, status ) {
- if ( status === "abort" ) {
- self.panels.stop( false, true );
- }
-
- self.lis.eq( index ).removeClass( "ui-tabs-loading" );
-
- if ( jqXHR === self.xhr ) {
- delete self.xhr;
- }
+ // TODO: IE resolves cached XHRs immediately
+ // remove when core #10467 is fixed
+ setTimeout(function() {
+ if ( status === "abort" ) {
+ self.panels.stop( false, true );
+ }
+
+ self.lis.eq( index ).removeClass( "ui-tabs-loading" );
+
+ if ( jqXHR === self.xhr ) {
+ delete self.xhr;
+ }
+ }, 1 );
});
}
@@ -802,11 +800,14 @@ if ( $.uiBackCompat !== false ) {
index = this._getIndex( index );
var options = this.options,
tab = this.lis.eq( index ).remove(),
- panel = this.panels.eq( index ).remove();
+ panel = this._getPanelForTab( tab.find( "a[aria-controls]" ) ).remove();
// If selected tab was removed focus tab to the right or
// in case the last tab was removed the tab to the left.
- if ( tab.hasClass( "ui-tabs-active" ) && this.anchors.length > 1) {
+ // We check for more than 2 tabs, because if there are only 2,
+ // then when we remove this tab, there will only be one tab left
+ // so we don't need to detect which tab to activate.
+ if ( tab.hasClass( "ui-tabs-active" ) && this.anchors.length > 2 ) {
this._activate( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
}
diff --git a/ui/jquery.ui.tooltip.js b/ui/jquery.ui.tooltip.js
index 5e32459fc..35b6f9b50 100644
--- a/ui/jquery.ui.tooltip.js
+++ b/ui/jquery.ui.tooltip.js
@@ -27,7 +27,7 @@ $.widget( "ui.tooltip", {
position: {
my: "left+15 center",
at: "right center",
- collision: "flip fit"
+ collision: "flipfit flipfit"
},
show: true,
tooltipClass: null,
@@ -166,7 +166,7 @@ $.widget( "ui.tooltip", {
// don't close if the element has focus
// this prevents the tooltip from closing if you hover while focused
- if ( !force && document.activeElement === target[0] ) {
+ if ( !force && this.document[0].activeElement === target[0] ) {
return;
}
@@ -200,7 +200,7 @@ $.widget( "ui.tooltip", {
$( "<div>" )
.addClass( "ui-tooltip-content" )
.appendTo( tooltip );
- tooltip.appendTo( document.body );
+ tooltip.appendTo( this.document[0].body );
if ( $.fn.bgiframe ) {
tooltip.bgiframe();
}
diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js
index 4c538d020..294e321a9 100644
--- a/ui/jquery.ui.widget.js
+++ b/ui/jquery.ui.widget.js
@@ -195,6 +195,12 @@ $.Widget.prototype = {
if ( element !== this ) {
$.data( element, this.widgetName, this );
this._bind({ remove: "destroy" });
+ this.document = $( element.style ?
+ // element within the document
+ element.ownerDocument :
+ // element is window or document
+ element.document || element );
+ this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
}
this._create();
@@ -270,10 +276,11 @@ $.Widget.prototype = {
return this;
},
_setOptions: function( options ) {
- var self = this;
- $.each( options, function( key, value ) {
- self._setOption( key, value );
- });
+ var key;
+
+ for ( key in options ) {
+ this._setOption( key, options[ key ] );
+ }
return this;
},
@@ -326,13 +333,22 @@ $.Widget.prototype = {
eventName = match[1] + "." + instance.widgetName,
selector = match[2];
if ( selector ) {
- element.delegate( selector, eventName, handlerProxy );
+ instance.widget().delegate( selector, eventName, handlerProxy );
} else {
element.bind( eventName, handlerProxy );
}
});
},
+ _delay: function( handler, delay ) {
+ function handlerProxy() {
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
+ .apply( instance, arguments );
+ }
+ var instance = this;
+ return setTimeout( handlerProxy, delay || 0 );
+ },
+
_hoverable: function( element ) {
this.hoverable = this.hoverable.add( element );
this._bind( element, {
@@ -377,6 +393,10 @@ $.Widget.prototype = {
}
}
+ // the original event may come from any element
+ // so we need to reset the target on the new event
+ event.target = this.element[0];
+
this.element.trigger( event, data );
args = $.isArray( data ) ?