/* * jQuery UI Resizable @VERSION * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Resizables * * Depends: * jquery.ui.core.js * jquery.ui.mouse.js * jquery.ui.widget.js */ (function( $, undefined ) { $.widget("ui.resizable", $.ui.mouse, { widgetEventPrefix: "resize", options: { alsoResize: false, animate: false, animateDuration: "slow", animateEasing: "swing", aspectRatio: false, autoHide: false, containment: false, ghost: false, grid: false, handles: "e,s,se", helper: false, maxHeight: null, maxWidth: null, minHeight: 10, minWidth: 10, zIndex: 1000 }, _create: function() { var self = this, o = this.options; this.element.addClass("ui-resizable"); $.extend(this, { _aspectRatio: !!(o.aspectRatio), aspectRatio: o.aspectRatio, originalElement: this.element, _proportionallyResizeElements: [], _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null }); //Wrap the element if it cannot hold child nodes if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) { //Opera fix for relative positioning if (/relative/.test(this.element.css('position')) && $.browser.opera) this.element.css({ position: 'relative', top: 'auto', left: 'auto' }); //Create a wrapper element and set the wrapper to the new current internal element this.element.wrap( $('
').css({ position: this.element.css('position'), width: this.element.outerWidth(), height: this.element.outerHeight(), top: this.element.css('top'), left: this.element.css('left') }) ); //Overwrite the original this.element this.element = this.element.parent().data( "resizable", this.element.data('resizable') ); this.elementIsWrapper = true; //Move margins to the wrapper this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") }); this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0}); //Prevent Safari textarea resize this.originalResizeStyle = this.originalElement.css('resize'); this.originalElement.css('resize', 'none'); //Push the actual element to our proportionallyResize internal array this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' })); // avoid IE jump (hard set the margin) this.originalElement.css({ margin: this.originalElement.css('margin') }); // fix handlers offset this._proportionallyResize(); } this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' }); if(this.handles.constructor == String) { if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw'; var n = this.handles.split(","); this.handles = {}; for(var i = 0; i < n.length; i++) { var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle; var axis = $(''); // increase zIndex of sw, se, ne, nw axis //TODO : this modifies original option if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex }); //TODO : What's going on here? if ('se' == handle) { axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se'); }; //Insert into internal handles object and append to element this.handles[handle] = '.ui-resizable-'+handle; this.element.append(axis); } } this._renderAxis = function(target) { target = target || this.element; for(var i in this.handles) { if(this.handles[i].constructor == String) this.handles[i] = $(this.handles[i], this.element).show(); //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls) if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) { var axis = $(this.handles[i], this.element), padWrapper = 0; //Checking the correct pad and border padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth(); //The padding type i have to apply... var padPos = [ 'padding', /ne|nw|n/.test(i) ? 'Top' : /se|sw|s/.test(i) ? 'Bottom' : /^e$/.test(i) ? 'Right' : 'Left' ].join(""); target.css(padPos, padWrapper); this._proportionallyResize(); } //TODO: What's that good for? There's not anything to be executed left if(!$(this.handles[i]).length) continue; } }; //TODO: make renderAxis a prototype function this._renderAxis(this.element); this._handles = $('.ui-resizable-handle', this.element) .disableSelection(); //Matching axis name this._handles.mouseover(function() { if (!self.resizing) { if (this.className) var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i); //Axis, default = se self.axis = axis && axis[1] ? axis[1] : 'se'; } }); //If we want to auto hide the elements if (o.autoHide) { this._handles.hide(); $(this.element) .addClass("ui-resizable-autohide") .hover(function() { $(this).removeClass("ui-resizable-autohide"); self._handles.show(); }, function(){ if (!self.resizing) { $(this).addClass("ui-resizable-autohide"); self._handles.hide(); } }); } //Initialize the mouse interaction this._mouseInit(); }, destroy: function() { this._mouseDestroy(); var _destroy = function(exp) { $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing") .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove(); }; //TODO: Unwrap at same DOM position if (this.elementIsWrapper) { _destroy(this.element); var wrapper = this.element; wrapper.after( this.originalElement.css({ position: wrapper.css('position'), width: wrapper.outerWidth(), height: wrapper.outerHeight(), top: wrapper.css('top'), left: wrapper.css('left') }) ).remove(); } this.originalElement.css('resize', this.originalResizeStyle); _destroy(this.originalElement); return this; }, _mouseCapture: function(event) { var handle = false; for (var i in this.handles) { if ($(this.handles[i])[0] == event.target) { handle = true; } } return !this.options.disabled && handle; }, _mouseStart: function(event) { var o = this.options, iniPos = this.element.position(), el = this.element; this.resizing = true; this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() }; // bugfix for http://dev.jquery.com/ticket/1749 if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) { el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left }); } //Opera fixing relative position if ($.browser.opera && (/relative/).test(el.css('position'))) el.css({ position: 'relative', top: 'auto', left: 'auto' }); this._renderProxy(); var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top')); if (o.containment) { curleft += $(o.containment).scrollLeft() || 0; curtop += $(o.containment).scrollTop() || 0; } //Store needed variables this.offset = this.helper.offset(); this.position = { left: curleft, top: curtop }; this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; this.originalPosition = { left: curleft, top: curtop }; this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() }; this.originalMousePosition = { left: event.pageX, top: event.pageY }; //Aspect Ratio this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1); var cursor = $('.ui-resizable-' + this.axis).css('cursor'); $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor); el.addClass("ui-resizable-resizing"); this._propagate("start", event); return true; }, _mouseDrag: function(event) { //Increase performance, avoid regex var el = this.helper, o = this.options, props = {}, self = this, smp = this.originalMousePosition, a = this.axis; var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0; var trigger = this._change[a]; if (!trigger) return false; // Calculate the attrs that will be change var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff; if (this._aspectRatio || event.shiftKey) data = this._updateRatio(data, event); data = this._respectSize(data, event); // plugins callbacks need to be called first this._propagate("resize", event); el.css({ top: this.position.top + "px", left: this.position.left + "px", width: this.size.width + "px", height: this.size.height + "px" }); if (!this._helper && this._proportionallyResizeElements.length) this._proportionallyResize(); this._updateCache(data); // calling the user callback at the end this._trigger('resize', event, this.ui()); return false; }, _mouseStop: function(event) { this.resizing = false; var o = this.options, self = this; if(this._helper) { var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName), soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height, soffsetw = ista ? 0 : self.sizeDiff.width; var s = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) }, left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null; if (!o.animate) this.element.css($.extend(s, { top: top, left: left })); self.helper.height(self.size.height); self.helper.width(self.size.width); if (this._helper && !o.animate) this._proportionallyResize(); } $('body').css('cursor', 'auto'); this.element.removeClass("ui-resizable-resizing"); this._propagate("stop", event); if (this._helper) this.helper.remove(); return false; }, _updateCache: function(data) { var o = this.options; this.offset = this.helper.offset(); if (isNumber(data.left)) this.position.left = data.left; if (isNumber(data.top)) this.position.top = data.top; if (isNumber(data.height)) this.size.height = data.height; if (isNumber(data.width)) this.size.width = data.width; }, _updateRatio: function(data, event) { var o = this.options, cpos = this.position, csize = this.size, a = this.axis; if (data.height) data.width = (csize.height * this.aspectRatio); else if (data.width) data.height = (csize.width / this.aspectRatio); if (a == 'sw') { data.left = cpos.left + (csize.width - data.width); data.top = null; } if (a == 'nw') { data.top = cpos.top + (csize.height - data.height); data.left = cpos.left + (csize.width - data.width); } return data; }, _respectSize: function(data, event) { var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis, ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height), isminw = isNumber(data.width) && o.minWidth && (o.minWiddefine( [
"qunit",
"jquery",
"lib/helper",
"ui/widgets/selectmenu"
], function( QUnit, $, helper ) {
QUnit.module( "selectmenu: methods", { afterEach: helper.moduleAfterEach } );
QUnit.test( "destroy", function( assert ) {
assert.expect( 1 );
assert.domEqual( "#speed", function() {
$( "#speed" ).selectmenu().selectmenu( "destroy" );
} );
} );
QUnit.test( "open / close", function( assert ) {
assert.expect( 5 );
var element = $( "#speed" ).selectmenu(),
menu = element.selectmenu( "menuWidget" );
assert.ok( menu.is( ":hidden" ), "menu hidden on init" );
element.selectmenu( "open" );
assert.ok( menu.is( ":visible" ), "open: menu visible" );
assert.equal( menu.attr( "aria-hidden" ), "false", "open: menu aria-disabled" );
element.selectmenu( "close" );
assert.ok( menu.is( ":hidden" ), "close: menu hidden" );
assert.equal( menu.attr( "aria-hidden" ), "true", "close: menu aria-disabled" );
} );
QUnit.test( "enable / disable", function( assert ) {
assert.expect( 10 );
var element = $( "#speed" ).selectmenu(),
button = element.selectmenu( "widget" ),
menu = element.selectmenu( "menuWidget" );
element.selectmenu( "disable" );
assert.ok( element.selectmenu( "option", "disabled" ), "disable: widget option" );
assert.equal( element.attr( "disabled" ), "disabled", "disable: native select disabled" );
assert.equal( button.attr( "aria-disabled" ), "true", "disable: button ARIA" );
assert.equal( button.attr( "tabindex" ), -1, "disable: button tabindex" );
assert.equal( menu.attr( "aria-disabled" ), "true", "disable: menu ARIA" );
element.selectmenu( "enable" );
assert.ok( !element.selectmenu( "option", "disabled" ), "enable: widget option" );
assert.equal( element.attr( "disabled" ), undefined, "enable: native select disabled" );
assert.equal( button.attr( "aria-disabled" ), "false", "enable: button ARIA" );
assert.equal( button.attr( "tabindex" ), 0, "enable: button tabindex" );
assert.equal( menu.attr( "aria-disabled" ), "false", "enable: menu ARIA" );
} );
QUnit.test( "refresh - structure", function( assert ) {
assert.expect( 3 );
var menuItems,
element = $( "#speed" ).selectmenu(),
menu = element.selectmenu( "menuWidget" ),
options = element.find( "option" );
options.eq( 0 )
.attr( "value", "changed_value" )
.text( "Changed value" );
options.eq( 2 ).remove();
options.eq( 3 ).remove();
element.append( "<option value=\"added_option\">Added option</option>" );
element.selectmenu( "refresh" );
options = element.find( "option" );
menuItems = menu.find( "li" ).not( ".ui-selectmenu-optgroup" );
assert.equal( options.length, menuItems.length, "menu item length" );
assert.equal( "Added option", menuItems.last().text(), "added item" );
assert.equal( "Changed value", menuItems.eq( 0 ).text(), "changed item" );
} );
QUnit.test( "refresh - change selected option", function( assert ) {
var ready = assert.async();
assert.expect( 4 );
var element = $( "#speed" ).selectmenu(),
button = element.selectmenu( "widget" );
assert.equal( String.prototype.trim.call( button.text() ), "Medium", "button text after init" );
button.simulate( "focus" );
setTimeout( function() {
assert.equal( String.prototype.trim.call( button.text() ), "Medium", "button text after focus" );
element[ 0 ].selectedIndex = 0;
element.selectmenu( "refresh" );
assert.equal( String.prototype.trim.call( button.text() ), "Slower", "button text after changing selected option" );
element.find( "option" ).prop( "selected", false );
element.append( "<option selected value=\"selected_option\">Selected option</option>" );
element.selectmenu( "refresh" );
assert.equal( String.prototype.trim.call( button.text() ), "Selected option", "button text after adding selected option" );
ready();
} );
} );
QUnit.test( "refresh - disabled select", function( assert ) {
assert.expect( 4 );
var element = $( "#speed" ).selectmenu(),
button = element.selectmenu( "widget" ),
menu = element.selectmenu( "menuWidget" );
element.prop( "disabled", true );
element.selectmenu( "refresh" );
assert.ok( element.selectmenu( "option", "disabled" ), "widget option" );
assert.equal( button.attr( "aria-disabled" ), "true", "button ARIA" );
assert.equal( button.attr( "tabindex" ), -1, "button tabindex" );
assert.equal( menu.attr( "aria-disabled" ), "true", "menu ARIA" );
} );
QUnit.test( "refresh - disabled option", function( assert ) {
assert.expect( 1 );
var disabledItem,
element = $( "#speed" ).selectmenu(),
menu = element.selectmenu( "menuWidget" ).parent();
element.find( "option" ).eq( 2 ).prop( "disabled", true );
element.selectmenu( "refresh" );
disabledItem = menu.find( "li" ).not( ".ui-selectmenu-optgroup" ).eq( 2 );
assert.hasClasses( disabledItem, "ui-state-disabled" );
} );
QUnit.test( "refresh - disabled optgroup", function( assert ) {
var i, item,
element = $( "#files" ).selectmenu(),
menu = element.selectmenu( "menuWidget" ).parent(),
originalDisabledOptgroup = element.find( "optgroup" ).first(),
originalDisabledOptions = originalDisabledOptgroup.find( "option" );
assert.expect( 2 + originalDisabledOptions.length );
originalDisabledOptgroup.prop( "disabled", true );
element.selectmenu( "refresh" );
item = menu.find( "li.ui-selectmenu-optgroup" ).first();
assert.hasClasses( item, "ui-state-disabled" );
assert.equal(
menu.find( "li" ).not( ".ui-selectmenu-optgroup" ).filter( ".ui-state-disabled" ).length,
originalDisabledOptions.length,
"disabled options"
);
for ( i = 0; i < originalDisabledOptions.length; i++ ) {
item = item.next( "li" );
assert.hasClasses( item, "ui-state-disabled" );
}
} );
QUnit.test( "refresh - remove all options", function( assert ) {
assert.expect( 2 );
var element = $( "#speed" ).selectmenu(),
button = element.selectmenu( "widget" ),
menu = element.selectmenu( "menuWidget" );
element.children().remove();
element.selectmenu( "refresh" );
assert.equal( button.find( ".ui-selectmenu-text" ).html(), $( "<span> </span>" ).html(),
"Empty button text" );
assert.equal( menu.children().length, 0, "Empty menu" );
} );
QUnit.test( "widget and menuWidget", function( assert ) {
assert.expect( 4 );
var element = $( "#speed" ).selectmenu(),
button = element.selectmenu( "widget" ),
menu = element.selectmenu( "menuWidget" );
assert.equal( button.length, 1, "button: one element" );
assert.hasClasses( button, "ui-button" );
assert.equal( menu.length, 1, "Menu Widget: one element" );
assert.ok( menu.is( "ul.ui-menu" ), "Menu Widget: element and class" );
} );
} );