4 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT (MIT-LICENSE.txt)
6 * and GPL (GPL-LICENSE.txt) licenses.
8 * http://docs.jquery.com/UI
10 ;jQuery.ui || (function($) {
12 var _remove = $.fn.remove,
13 isFF2 = $.browser.mozilla && (parseFloat($.browser.version) < 1.9);
15 //Helper functions and ui object
19 // $.ui.plugin is deprecated. Use the proxy pattern instead.
21 add: function(module, option, set) {
22 var proto = $.ui[module].prototype;
24 proto.plugins[i] = proto.plugins[i] || [];
25 proto.plugins[i].push([option, set[i]]);
28 call: function(instance, name, args) {
29 var set = instance.plugins[name];
30 if(!set || !instance.element[0].parentNode) { return; }
32 for (var i = 0; i < set.length; i++) {
33 if (instance.options[set[i][0]]) {
34 set[i][1].apply(instance.element, args);
40 contains: function(a, b) {
41 return document.compareDocumentPosition
42 ? a.compareDocumentPosition(b) & 16
43 : a !== b && a.contains(b);
46 hasScroll: function(el, a) {
48 //If overflow is hidden, the element might have extra content, but the user wants to hide it
49 if ($(el).css('overflow') == 'hidden') { return false; }
51 var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
54 if (el[scroll] > 0) { return true; }
56 // TODO: determine which cases actually cause this to happen
57 // if the element doesn't have the scroll set, see if it's possible to
60 has = (el[scroll] > 0);
65 isOverAxis: function(x, reference, size) {
66 //Determines when x coordinate is over "b" element axis
67 return (x > reference) && (x < (reference + size));
70 isOver: function(y, x, top, left, height, width) {
71 //Determines when x, y coordinates is over "b" element
72 return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width);
105 // WAI-ARIA normalization
108 removeAttr = $.fn.removeAttr,
109 ariaNS = "http://www.w3.org/2005/07/aaa",
110 ariaState = /^aria-/,
111 ariaRole = /^wairole:/;
113 $.attr = function(elem, name, value) {
114 var set = value !== undefined;
116 return (name == 'role'
118 ? attr.call(this, elem, name, "wairole:" + value)
119 : (attr.apply(this, arguments) || "").replace(ariaRole, ""))
120 : (ariaState.test(name)
122 ? elem.setAttributeNS(ariaNS,
123 name.replace(ariaState, "aaa:"), value)
124 : attr.call(this, elem, name.replace(ariaState, "aaa:")))
125 : attr.apply(this, arguments)));
128 $.fn.removeAttr = function(name) {
129 return (ariaState.test(name)
130 ? this.each(function() {
131 this.removeAttributeNS(ariaNS, name.replace(ariaState, ""));
132 }) : removeAttr.call(this, name));
139 // Safari has a native remove event which actually removes DOM elements,
140 // so we have to use triggerHandler instead of trigger (#3037).
141 $("*", this).add(this).each(function() {
142 $(this).triggerHandler("remove");
144 return _remove.apply(this, arguments );
147 enableSelection: function() {
149 .attr('unselectable', 'off')
150 .css('MozUserSelect', '')
151 .unbind('selectstart.ui');
154 disableSelection: function() {
156 .attr('unselectable', 'on')
157 .css('MozUserSelect', 'none')
158 .bind('selectstart.ui', function() { return false; });
161 scrollParent: function() {
163 if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
164 scrollParent = this.parents().filter(function() {
165 return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
168 scrollParent = this.parents().filter(function() {
169 return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
173 return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
178 //Additional selectors
179 $.extend($.expr[':'], {
180 data: function(elem, i, match) {
181 return !!$.data(elem, match[3]);
184 focusable: function(element) {
185 var nodeName = element.nodeName.toLowerCase(),
186 tabIndex = $.attr(element, 'tabindex');
187 return (/input|select|textarea|button|object/.test(nodeName)
189 : 'a' == nodeName || 'area' == nodeName
190 ? element.href || !isNaN(tabIndex)
192 // the element and all of its ancestors must be visible
193 // the browser may report that the area is hidden
194 && !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
197 tabbable: function(element) {
198 var tabIndex = $.attr(element, 'tabindex');
199 return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable');
204 // $.widget is a factory to create jQuery plugins
205 // taking some boilerplate code out of the plugin code
206 function getter(namespace, plugin, method, args) {
207 function getMethods(type) {
208 var methods = $[namespace][plugin][type] || [];
209 return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods);
212 var methods = getMethods('getter');
213 if (args.length == 1 && typeof args[0] == 'string') {
214 methods = methods.concat(getMethods('getterSetter'));
216 return ($.inArray(method, methods) != -1);
219 $.widget = function(name, prototype) {
220 var namespace = name.split(".")[0];
221 name = name.split(".")[1];
223 // create plugin method
224 $.fn[name] = function(options) {
225 var isMethodCall = (typeof options == 'string'),
226 args = Array.prototype.slice.call(arguments, 1);
228 // prevent calls to internal methods
229 if (isMethodCall && options.substring(0, 1) == '_') {
233 // handle getter methods
234 if (isMethodCall && getter(namespace, name, options, args)) {
235 var instance = $.data(this[0], name);
236 return (instance ? instance[options].apply(instance, args)
240 // handle initialization and non-getter methods
241 return this.each(function() {
242 var instance = $.data(this, name);
245 (!instance && !isMethodCall &&
246 $.data(this, name, new $[namespace][name](this, options))._init());
249 (instance && isMethodCall && $.isFunction(instance[options]) &&
250 instance[options].apply(instance, args));
254 // create widget constructor
255 $[namespace] = $[namespace] || {};
256 $[namespace][name] = function(element, options) {
259 this.namespace = namespace;
260 this.widgetName = name;
261 this.widgetEventPrefix = $[namespace][name].eventPrefix || name;
262 this.widgetBaseClass = namespace + '-' + name;
264 this.options = $.extend({},
266 $[namespace][name].defaults,
267 $.metadata && $.metadata.get(element)[name],
270 this.element = $(element)
271 .bind('setData.' + name, function(event, key, value) {
272 if (event.target == element) {
273 return self._setData(key, value);
276 .bind('getData.' + name, function(event, key) {
277 if (event.target == element) {
278 return self._getData(key);
281 .bind('remove', function() {
282 return self.destroy();
286 // add widget prototype
287 $[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype);
289 // TODO: merge getter and getterSetter properties from widget prototype
290 // and plugin prototype
291 $[namespace][name].getterSetter = 'option';
294 $.widget.prototype = {
295 _init: function() {},
296 destroy: function() {
297 this.element.removeData(this.widgetName)
298 .removeClass(this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled')
299 .removeAttr('aria-disabled');
302 option: function(key, value) {
306 if (typeof key == "string") {
307 if (value === undefined) {
308 return this._getData(key);
311 options[key] = value;
314 $.each(options, function(key, value) {
315 self._setData(key, value);
318 _getData: function(key) {
319 return this.options[key];
321 _setData: function(key, value) {
322 this.options[key] = value;
324 if (key == 'disabled') {
326 [value ? 'addClass' : 'removeClass'](
327 this.widgetBaseClass + '-disabled' + ' ' +
328 this.namespace + '-state-disabled')
329 .attr("aria-disabled", value);
334 this._setData('disabled', false);
336 disable: function() {
337 this._setData('disabled', true);
340 _trigger: function(type, event, data) {
341 var callback = this.options[type],
342 eventName = (type == this.widgetEventPrefix
343 ? type : this.widgetEventPrefix + type);
345 event = $.Event(event);
346 event.type = eventName;
348 // copy original event properties over to the new event
349 // this would happen if we could call $.event.fix instead of $.Event
350 // but we don't have a way to force an event to be fixed multiple times
351 if (event.originalEvent) {
352 for (var i = $.event.props.length, prop; i;) {
353 prop = $.event.props[--i];
354 event[prop] = event.originalEvent[prop];
358 this.element.trigger(event, data);
360 return !($.isFunction(callback) && callback.call(this.element[0], event, data) === false
361 || event.isDefaultPrevented());
365 $.widget.defaults = {
370 /** Mouse Interaction Plugin **/
373 _mouseInit: function() {
377 .bind('mousedown.'+this.widgetName, function(event) {
378 return self._mouseDown(event);
380 .bind('click.'+this.widgetName, function(event) {
381 if(self._preventClickEvent) {
382 self._preventClickEvent = false;
383 event.stopImmediatePropagation();
388 // Prevent text selection in IE
389 if ($.browser.msie) {
390 this._mouseUnselectable = this.element.attr('unselectable');
391 this.element.attr('unselectable', 'on');
394 this.started = false;
397 // TODO: make sure destroying one instance of mouse doesn't mess with
398 // other instances of mouse
399 _mouseDestroy: function() {
400 this.element.unbind('.'+this.widgetName);
402 // Restore text selection in IE
404 && this.element.attr('unselectable', this._mouseUnselectable));
407 _mouseDown: function(event) {
408 // don't let more than one widget handle mouseStart
409 // TODO: figure out why we have to use originalEvent
410 event.originalEvent = event.originalEvent || {};
411 if (event.originalEvent.mouseHandled) { return; }
413 // we may have missed mouseup (out of window)
414 (this._mouseStarted && this._mouseUp(event));
416 this._mouseDownEvent = event;
419 btnIsLeft = (event.which == 1),
420 elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
421 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
425 this.mouseDelayMet = !this.options.delay;
426 if (!this.mouseDelayMet) {
427 this._mouseDelayTimer = setTimeout(function() {
428 self.mouseDelayMet = true;
429 }, this.options.delay);
432 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
433 this._mouseStarted = (this._mouseStart(event) !== false);
434 if (!this._mouseStarted) {
435 event.preventDefault();
440 // these delegates are required to keep context
441 this._mouseMoveDelegate = function(event) {
442 return self._mouseMove(event);
444 this._mouseUpDelegate = function(event) {
445 return self._mouseUp(event);
448 .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
449 .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
451 // preventDefault() is used to prevent the selection of text here -
452 // however, in Safari, this causes select boxes not to be selectable
453 // anymore, so this fix is needed
454 ($.browser.safari || event.preventDefault());
456 event.originalEvent.mouseHandled = true;
460 _mouseMove: function(event) {
461 // IE mouseup check - mouseup happened when mouse was out of window
462 if ($.browser.msie && !event.button) {
463 return this._mouseUp(event);
466 if (this._mouseStarted) {
467 this._mouseDrag(event);
468 return event.preventDefault();
471 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
473 (this._mouseStart(this._mouseDownEvent, event) !== false);
474 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
477 return !this._mouseStarted;
480 _mouseUp: function(event) {
482 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
483 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
485 if (this._mouseStarted) {
486 this._mouseStarted = false;
487 this._preventClickEvent = (event.target == this._mouseDownEvent.target);
488 this._mouseStop(event);
494 _mouseDistanceMet: function(event) {
496 Math.abs(this._mouseDownEvent.pageX - event.pageX),
497 Math.abs(this._mouseDownEvent.pageY - event.pageY)
498 ) >= this.options.distance
502 _mouseDelayMet: function(event) {
503 return this.mouseDelayMet;
506 // These are placeholder methods, to be overriden by extending plugin
507 _mouseStart: function(event) {},
508 _mouseDrag: function(event) {},
509 _mouseStop: function(event) {},
510 _mouseCapture: function(event) { return true; }
513 $.ui.mouse.defaults = {
521 * jQuery UI Draggable 1.7.2
523 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
524 * Dual licensed under the MIT (MIT-LICENSE.txt)
525 * and GPL (GPL-LICENSE.txt) licenses.
527 * http://docs.jquery.com/UI/Draggables
534 $.widget("ui.draggable", $.extend({}, $.ui.mouse, {
538 if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
539 this.element[0].style.position = 'relative';
541 (this.options.addClasses && this.element.addClass("ui-draggable"));
542 (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
548 destroy: function() {
549 if(!this.element.data('draggable')) return;
551 .removeData("draggable")
552 .unbind(".draggable")
553 .removeClass("ui-draggable"
554 + " ui-draggable-dragging"
555 + " ui-draggable-disabled");
556 this._mouseDestroy();
559 _mouseCapture: function(event) {
561 var o = this.options;
563 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
566 //Quit if we're not on a valid handle
567 this.handle = this._getHandle(event);
575 _mouseStart: function(event) {
577 var o = this.options;
579 //Create and append the visible helper
580 this.helper = this._createHelper(event);
582 //Cache the helper size
583 this._cacheHelperProportions();
585 //If ddmanager is used for droppables, set the global draggable
587 $.ui.ddmanager.current = this;
590 * - Position generation -
591 * This block generates everything position related - it's the core of draggables.
594 //Cache the margins of the original element
595 this._cacheMargins();
597 //Store the helper's css position
598 this.cssPosition = this.helper.css("position");
599 this.scrollParent = this.helper.scrollParent();
601 //The element's absolute position on the page minus margins
602 this.offset = this.element.offset();
604 top: this.offset.top - this.margins.top,
605 left: this.offset.left - this.margins.left
608 $.extend(this.offset, {
609 click: { //Where the click happened, relative to the element
610 left: event.pageX - this.offset.left,
611 top: event.pageY - this.offset.top
613 parent: this._getParentOffset(),
614 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
617 //Generate the original position
618 this.originalPosition = this._generatePosition(event);
619 this.originalPageX = event.pageX;
620 this.originalPageY = event.pageY;
622 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
624 this._adjustOffsetFromHelper(o.cursorAt);
626 //Set a containment if given in the options
628 this._setContainment();
630 //Call plugins and callbacks
631 this._trigger("start", event);
633 //Recache the helper size
634 this._cacheHelperProportions();
636 //Prepare the droppable offsets
637 if ($.ui.ddmanager && !o.dropBehaviour)
638 $.ui.ddmanager.prepareOffsets(this, event);
640 this.helper.addClass("ui-draggable-dragging");
641 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
645 _mouseDrag: function(event, noPropagation) {
647 //Compute the helpers position
648 this.position = this._generatePosition(event);
649 this.positionAbs = this._convertPositionTo("absolute");
651 //Call plugins and callbacks and use the resulting position if something is returned
652 if (!noPropagation) {
653 var ui = this._uiHash();
654 this._trigger('drag', event, ui);
655 this.position = ui.position;
658 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
659 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
660 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
665 _mouseStop: function(event) {
667 //If we are using droppables, inform the manager about the drop
669 if ($.ui.ddmanager && !this.options.dropBehaviour)
670 dropped = $.ui.ddmanager.drop(this, event);
672 //if a drop comes from outside (a sortable)
674 dropped = this.dropped;
675 this.dropped = false;
678 if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
680 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
681 self._trigger("stop", event);
685 this._trigger("stop", event);
692 _getHandle: function(event) {
694 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
695 $(this.options.handle, this.element)
699 if(this == event.target) handle = true;
706 _createHelper: function(event) {
708 var o = this.options;
709 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
711 if(!helper.parents('body').length)
712 helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
714 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
715 helper.css("position", "absolute");
721 _adjustOffsetFromHelper: function(obj) {
722 if(obj.left != undefined) this.offset.click.left = obj.left + this.margins.left;
723 if(obj.right != undefined) this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
724 if(obj.top != undefined) this.offset.click.top = obj.top + this.margins.top;
725 if(obj.bottom != undefined) this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
728 _getParentOffset: function() {
730 //Get the offsetParent and cache its position
731 this.offsetParent = this.helper.offsetParent();
732 var po = this.offsetParent.offset();
734 // This is a special case where we need to modify a offset calculated on start, since the following happened:
735 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
736 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
737 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
738 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
739 po.left += this.scrollParent.scrollLeft();
740 po.top += this.scrollParent.scrollTop();
743 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
744 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
745 po = { top: 0, left: 0 };
748 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
749 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
754 _getRelativeOffset: function() {
756 if(this.cssPosition == "relative") {
757 var p = this.element.position();
759 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
760 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
763 return { top: 0, left: 0 };
768 _cacheMargins: function() {
770 left: (parseInt(this.element.css("marginLeft"),10) || 0),
771 top: (parseInt(this.element.css("marginTop"),10) || 0)
775 _cacheHelperProportions: function() {
776 this.helperProportions = {
777 width: this.helper.outerWidth(),
778 height: this.helper.outerHeight()
782 _setContainment: function() {
784 var o = this.options;
785 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
786 if(o.containment == 'document' || o.containment == 'window') this.containment = [
787 0 - this.offset.relative.left - this.offset.parent.left,
788 0 - this.offset.relative.top - this.offset.parent.top,
789 $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
790 ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
793 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
794 var ce = $(o.containment)[0]; if(!ce) return;
795 var co = $(o.containment).offset();
796 var over = ($(ce).css("overflow") != 'hidden');
799 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
800 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
801 co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
802 co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
804 } else if(o.containment.constructor == Array) {
805 this.containment = o.containment;
810 _convertPositionTo: function(d, pos) {
812 if(!pos) pos = this.position;
813 var mod = d == "absolute" ? 1 : -1;
814 var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
818 pos.top // The absolute mouse position
819 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
820 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
821 - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
824 pos.left // The absolute mouse position
825 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
826 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
827 - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
833 _generatePosition: function(event) {
835 var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
837 // This is another very weird special case that only happens for relative elements:
838 // 1. If the css position is relative
839 // 2. and the scroll parent is the document or similar to the offset parent
840 // we have to refresh the relative offset during the scroll so there are no jumps
841 if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
842 this.offset.relative = this._getRelativeOffset();
845 var pageX = event.pageX;
846 var pageY = event.pageY;
849 * - Position constraining -
850 * Constrain the position to a mix of grid, containment.
853 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
855 if(this.containment) {
856 if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
857 if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
858 if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
859 if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
863 var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
864 pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
866 var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
867 pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
874 pageY // The absolute mouse position
875 - this.offset.click.top // Click offset (relative to the element)
876 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
877 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
878 + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
881 pageX // The absolute mouse position
882 - this.offset.click.left // Click offset (relative to the element)
883 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
884 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
885 + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
892 this.helper.removeClass("ui-draggable-dragging");
893 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
894 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
896 this.cancelHelperRemoval = false;
899 // From now on bulk stuff - mainly helpers
901 _trigger: function(type, event, ui) {
902 ui = ui || this._uiHash();
903 $.ui.plugin.call(this, type, [event, ui]);
904 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
905 return $.widget.prototype._trigger.call(this, type, event, ui);
910 _uiHash: function(event) {
913 position: this.position,
914 absolutePosition: this.positionAbs, //deprecated
915 offset: this.positionAbs
921 $.extend($.ui.draggable, {
928 cancel: ":input,option",
929 connectToSortable: false,
940 refreshPositions: false,
945 scrollSensitivity: 20,
955 $.ui.plugin.add("draggable", "connectToSortable", {
956 start: function(event, ui) {
958 var inst = $(this).data("draggable"), o = inst.options,
959 uiSortable = $.extend({}, ui, { item: inst.element });
961 $(o.connectToSortable).each(function() {
962 var sortable = $.data(this, 'sortable');
963 if (sortable && !sortable.options.disabled) {
964 inst.sortables.push({
966 shouldRevert: sortable.options.revert
968 sortable._refreshItems(); //Do a one-time refresh at start to refresh the containerCache
969 sortable._trigger("activate", event, uiSortable);
974 stop: function(event, ui) {
976 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
977 var inst = $(this).data("draggable"),
978 uiSortable = $.extend({}, ui, { item: inst.element });
980 $.each(inst.sortables, function() {
981 if(this.instance.isOver) {
983 this.instance.isOver = 0;
985 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
986 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
988 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
989 if(this.shouldRevert) this.instance.options.revert = true;
991 //Trigger the stop of the sortable
992 this.instance._mouseStop(event);
994 this.instance.options.helper = this.instance.options._helper;
996 //If the helper has been the original item, restore properties in the sortable
997 if(inst.options.helper == 'original')
998 this.instance.currentItem.css({ top: 'auto', left: 'auto' });
1001 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1002 this.instance._trigger("deactivate", event, uiSortable);
1008 drag: function(event, ui) {
1010 var inst = $(this).data("draggable"), self = this;
1012 var checkPos = function(o) {
1013 var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
1014 var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
1015 var itemHeight = o.height, itemWidth = o.width;
1016 var itemTop = o.top, itemLeft = o.left;
1018 return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
1021 $.each(inst.sortables, function(i) {
1023 //Copy over some variables to allow calling the sortable's native _intersectsWith
1024 this.instance.positionAbs = inst.positionAbs;
1025 this.instance.helperProportions = inst.helperProportions;
1026 this.instance.offset.click = inst.offset.click;
1028 if(this.instance._intersectsWith(this.instance.containerCache)) {
1030 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1031 if(!this.instance.isOver) {
1033 this.instance.isOver = 1;
1034 //Now we fake the start of dragging for the sortable instance,
1035 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1036 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
1037 this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
1038 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1039 this.instance.options.helper = function() { return ui.helper[0]; };
1041 event.target = this.instance.currentItem[0];
1042 this.instance._mouseCapture(event, true);
1043 this.instance._mouseStart(event, true, true);
1045 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1046 this.instance.offset.click.top = inst.offset.click.top;
1047 this.instance.offset.click.left = inst.offset.click.left;
1048 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
1049 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
1051 inst._trigger("toSortable", event);
1052 inst.dropped = this.instance.element; //draggable revert needs that
1053 //hack so receive/update callbacks work (mostly)
1054 inst.currentItem = inst.element;
1055 this.instance.fromOutside = inst;
1059 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
1060 if(this.instance.currentItem) this.instance._mouseDrag(event);
1064 //If it doesn't intersect with the sortable, and it intersected before,
1065 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1066 if(this.instance.isOver) {
1068 this.instance.isOver = 0;
1069 this.instance.cancelHelperRemoval = true;
1071 //Prevent reverting on this forced stop
1072 this.instance.options.revert = false;
1074 // The out event needs to be triggered independently
1075 this.instance._trigger('out', event, this.instance._uiHash(this.instance));
1077 this.instance._mouseStop(event, true);
1078 this.instance.options.helper = this.instance.options._helper;
1080 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1081 this.instance.currentItem.remove();
1082 if(this.instance.placeholder) this.instance.placeholder.remove();
1084 inst._trigger("fromSortable", event);
1085 inst.dropped = false; //draggable revert needs that
1095 $.ui.plugin.add("draggable", "cursor", {
1096 start: function(event, ui) {
1097 var t = $('body'), o = $(this).data('draggable').options;
1098 if (t.css("cursor")) o._cursor = t.css("cursor");
1099 t.css("cursor", o.cursor);
1101 stop: function(event, ui) {
1102 var o = $(this).data('draggable').options;
1103 if (o._cursor) $('body').css("cursor", o._cursor);
1107 $.ui.plugin.add("draggable", "iframeFix", {
1108 start: function(event, ui) {
1109 var o = $(this).data('draggable').options;
1110 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1111 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
1113 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1114 position: "absolute", opacity: "0.001", zIndex: 1000
1116 .css($(this).offset())
1120 stop: function(event, ui) {
1121 $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
1125 $.ui.plugin.add("draggable", "opacity", {
1126 start: function(event, ui) {
1127 var t = $(ui.helper), o = $(this).data('draggable').options;
1128 if(t.css("opacity")) o._opacity = t.css("opacity");
1129 t.css('opacity', o.opacity);
1131 stop: function(event, ui) {
1132 var o = $(this).data('draggable').options;
1133 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
1137 $.ui.plugin.add("draggable", "scroll", {
1138 start: function(event, ui) {
1139 var i = $(this).data("draggable");
1140 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
1142 drag: function(event, ui) {
1144 var i = $(this).data("draggable"), o = i.options, scrolled = false;
1146 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
1148 if(!o.axis || o.axis != 'x') {
1149 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
1150 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1151 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
1152 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1155 if(!o.axis || o.axis != 'y') {
1156 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
1157 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1158 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
1159 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1164 if(!o.axis || o.axis != 'x') {
1165 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
1166 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1167 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
1168 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1171 if(!o.axis || o.axis != 'y') {
1172 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
1173 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
1174 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
1175 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
1180 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
1181 $.ui.ddmanager.prepareOffsets(i, event);
1186 $.ui.plugin.add("draggable", "snap", {
1187 start: function(event, ui) {
1189 var i = $(this).data("draggable"), o = i.options;
1190 i.snapElements = [];
1192 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
1193 var $t = $(this); var $o = $t.offset();
1194 if(this != i.element[0]) i.snapElements.push({
1196 width: $t.outerWidth(), height: $t.outerHeight(),
1197 top: $o.top, left: $o.left
1202 drag: function(event, ui) {
1204 var inst = $(this).data("draggable"), o = inst.options;
1205 var d = o.snapTolerance;
1207 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1208 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1210 for (var i = inst.snapElements.length - 1; i >= 0; i--){
1212 var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
1213 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
1215 //Yes, I know, this is insane ;)
1216 if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
1217 if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1218 inst.snapElements[i].snapping = false;
1222 if(o.snapMode != 'inner') {
1223 var ts = Math.abs(t - y2) <= d;
1224 var bs = Math.abs(b - y1) <= d;
1225 var ls = Math.abs(l - x2) <= d;
1226 var rs = Math.abs(r - x1) <= d;
1227 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1228 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1229 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1230 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1233 var first = (ts || bs || ls || rs);
1235 if(o.snapMode != 'outer') {
1236 var ts = Math.abs(t - y1) <= d;
1237 var bs = Math.abs(b - y2) <= d;
1238 var ls = Math.abs(l - x1) <= d;
1239 var rs = Math.abs(r - x2) <= d;
1240 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1241 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1242 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1243 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1246 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
1247 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1248 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1255 $.ui.plugin.add("draggable", "stack", {
1256 start: function(event, ui) {
1258 var o = $(this).data("draggable").options;
1260 var group = $.makeArray($(o.stack.group)).sort(function(a,b) {
1261 return (parseInt($(a).css("zIndex"),10) || o.stack.min) - (parseInt($(b).css("zIndex"),10) || o.stack.min);
1264 $(group).each(function(i) {
1265 this.style.zIndex = o.stack.min + i;
1268 this[0].style.zIndex = o.stack.min + group.length;
1273 $.ui.plugin.add("draggable", "zIndex", {
1274 start: function(event, ui) {
1275 var t = $(ui.helper), o = $(this).data("draggable").options;
1276 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
1277 t.css('zIndex', o.zIndex);
1279 stop: function(event, ui) {
1280 var o = $(this).data("draggable").options;
1281 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
1287 * jQuery UI Droppable 1.7.2
1289 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
1290 * Dual licensed under the MIT (MIT-LICENSE.txt)
1291 * and GPL (GPL-LICENSE.txt) licenses.
1293 * http://docs.jquery.com/UI/Droppables
1301 $.widget("ui.droppable", {
1305 var o = this.options, accept = o.accept;
1306 this.isover = 0; this.isout = 1;
1308 this.options.accept = this.options.accept && $.isFunction(this.options.accept) ? this.options.accept : function(d) {
1309 return d.is(accept);
1312 //Store the droppable's proportions
1313 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
1315 // Add the reference and positions to the manager
1316 $.ui.ddmanager.droppables[this.options.scope] = $.ui.ddmanager.droppables[this.options.scope] || [];
1317 $.ui.ddmanager.droppables[this.options.scope].push(this);
1319 (this.options.addClasses && this.element.addClass("ui-droppable"));
1323 destroy: function() {
1324 var drop = $.ui.ddmanager.droppables[this.options.scope];
1325 for ( var i = 0; i < drop.length; i++ )
1326 if ( drop[i] == this )
1330 .removeClass("ui-droppable ui-droppable-disabled")
1331 .removeData("droppable")
1332 .unbind(".droppable");
1335 _setData: function(key, value) {
1337 if(key == 'accept') {
1338 this.options.accept = value && $.isFunction(value) ? value : function(d) {
1342 $.widget.prototype._setData.apply(this, arguments);
1347 _activate: function(event) {
1348 var draggable = $.ui.ddmanager.current;
1349 if(this.options.activeClass) this.element.addClass(this.options.activeClass);
1350 (draggable && this._trigger('activate', event, this.ui(draggable)));
1353 _deactivate: function(event) {
1354 var draggable = $.ui.ddmanager.current;
1355 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
1356 (draggable && this._trigger('deactivate', event, this.ui(draggable)));
1359 _over: function(event) {
1361 var draggable = $.ui.ddmanager.current;
1362 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
1364 if (this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1365 if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
1366 this._trigger('over', event, this.ui(draggable));
1371 _out: function(event) {
1373 var draggable = $.ui.ddmanager.current;
1374 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
1376 if (this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1377 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
1378 this._trigger('out', event, this.ui(draggable));
1383 _drop: function(event,custom) {
1385 var draggable = custom || $.ui.ddmanager.current;
1386 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
1388 var childrenIntersection = false;
1389 this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
1390 var inst = $.data(this, 'droppable');
1391 if(inst.options.greedy && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)) {
1392 childrenIntersection = true; return false;
1395 if(childrenIntersection) return false;
1397 if(this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1398 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
1399 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
1400 this._trigger('drop', event, this.ui(draggable));
1401 return this.element;
1410 draggable: (c.currentItem || c.element),
1412 position: c.position,
1413 absolutePosition: c.positionAbs, //deprecated
1414 offset: c.positionAbs
1420 $.extend($.ui.droppable, {
1422 eventPrefix: 'drop',
1430 tolerance: 'intersect'
1434 $.ui.intersect = function(draggable, droppable, toleranceMode) {
1436 if (!droppable.offset) return false;
1438 var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
1439 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
1440 var l = droppable.offset.left, r = l + droppable.proportions.width,
1441 t = droppable.offset.top, b = t + droppable.proportions.height;
1443 switch (toleranceMode) {
1445 return (l < x1 && x2 < r
1446 && t < y1 && y2 < b);
1449 return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
1450 && x2 - (draggable.helperProportions.width / 2) < r // Left Half
1451 && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
1452 && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
1455 var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
1456 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
1457 isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
1462 (y1 >= t && y1 <= b) || // Top edge touching
1463 (y2 >= t && y2 <= b) || // Bottom edge touching
1464 (y1 < t && y2 > b) // Surrounded vertically
1466 (x1 >= l && x1 <= r) || // Left edge touching
1467 (x2 >= l && x2 <= r) || // Right edge touching
1468 (x1 < l && x2 > r) // Surrounded horizontally
1479 This manager tracks offsets of draggables and droppables
1483 droppables: { 'default': [] },
1484 prepareOffsets: function(t, event) {
1486 var m = $.ui.ddmanager.droppables[t.options.scope];
1487 var type = event ? event.type : null; // workaround for #2317
1488 var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
1490 droppablesLoop: for (var i = 0; i < m.length; i++) {
1492 if(m[i].options.disabled || (t && !m[i].options.accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
1493 for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
1494 m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
1496 m[i].offset = m[i].element.offset();
1497 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
1499 if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
1504 drop: function(draggable, event) {
1506 var dropped = false;
1507 $.each($.ui.ddmanager.droppables[draggable.options.scope], function() {
1509 if(!this.options) return;
1510 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
1511 dropped = this._drop.call(this, event);
1513 if (!this.options.disabled && this.visible && this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1514 this.isout = 1; this.isover = 0;
1515 this._deactivate.call(this, event);
1522 drag: function(draggable, event) {
1524 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
1525 if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
1527 //Run through all droppables and check their positions based on specific tolerance options
1529 $.each($.ui.ddmanager.droppables[draggable.options.scope], function() {
1531 if(this.options.disabled || this.greedyChild || !this.visible) return;
1532 var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
1534 var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
1538 if (this.options.greedy) {
1539 var parent = this.element.parents(':data(droppable):eq(0)');
1540 if (parent.length) {
1541 parentInstance = $.data(parent[0], 'droppable');
1542 parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
1546 // we just moved into a greedy child
1547 if (parentInstance && c == 'isover') {
1548 parentInstance['isover'] = 0;
1549 parentInstance['isout'] = 1;
1550 parentInstance._out.call(parentInstance, event);
1553 this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
1554 this[c == "isover" ? "_over" : "_out"].call(this, event);
1556 // we just moved out of a greedy child
1557 if (parentInstance && c == 'isout') {
1558 parentInstance['isout'] = 0;
1559 parentInstance['isover'] = 1;
1560 parentInstance._over.call(parentInstance, event);
1569 * jQuery UI Resizable 1.7.2
1571 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
1572 * Dual licensed under the MIT (MIT-LICENSE.txt)
1573 * and GPL (GPL-LICENSE.txt) licenses.
1575 * http://docs.jquery.com/UI/Resizables
1582 $.widget("ui.resizable", $.extend({}, $.ui.mouse, {
1586 var self = this, o = this.options;
1587 this.element.addClass("ui-resizable");
1590 _aspectRatio: !!(o.aspectRatio),
1591 aspectRatio: o.aspectRatio,
1592 originalElement: this.element,
1593 _proportionallyResizeElements: [],
1594 _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
1597 //Wrap the element if it cannot hold child nodes
1598 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
1600 //Opera fix for relative positioning
1601 if (/relative/.test(this.element.css('position')) && $.browser.opera)
1602 this.element.css({ position: 'relative', top: 'auto', left: 'auto' });
1604 //Create a wrapper element and set the wrapper to the new current internal element
1606 $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
1607 position: this.element.css('position'),
1608 width: this.element.outerWidth(),
1609 height: this.element.outerHeight(),
1610 top: this.element.css('top'),
1611 left: this.element.css('left')
1615 //Overwrite the original this.element
1616 this.element = this.element.parent().data(
1617 "resizable", this.element.data('resizable')
1620 this.elementIsWrapper = true;
1622 //Move margins to the wrapper
1623 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
1624 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
1626 //Prevent Safari textarea resize
1627 this.originalResizeStyle = this.originalElement.css('resize');
1628 this.originalElement.css('resize', 'none');
1630 //Push the actual element to our proportionallyResize internal array
1631 this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
1633 // avoid IE jump (hard set the margin)
1634 this.originalElement.css({ margin: this.originalElement.css('margin') });
1636 // fix handlers offset
1637 this._proportionallyResize();
1641 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' });
1642 if(this.handles.constructor == String) {
1644 if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
1645 var n = this.handles.split(","); this.handles = {};
1647 for(var i = 0; i < n.length; i++) {
1649 var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
1650 var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
1652 // increase zIndex of sw, se, ne, nw axis
1653 //TODO : this modifies original option
1654 if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
1656 //TODO : What's going on here?
1657 if ('se' == handle) {
1658 axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
1661 //Insert into internal handles object and append to element
1662 this.handles[handle] = '.ui-resizable-'+handle;
1663 this.element.append(axis);
1668 this._renderAxis = function(target) {
1670 target = target || this.element;
1672 for(var i in this.handles) {
1674 if(this.handles[i].constructor == String)
1675 this.handles[i] = $(this.handles[i], this.element).show();
1677 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
1678 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
1680 var axis = $(this.handles[i], this.element), padWrapper = 0;
1682 //Checking the correct pad and border
1683 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
1685 //The padding type i have to apply...
1686 var padPos = [ 'padding',
1687 /ne|nw|n/.test(i) ? 'Top' :
1688 /se|sw|s/.test(i) ? 'Bottom' :
1689 /^e$/.test(i) ? 'Right' : 'Left' ].join("");
1691 target.css(padPos, padWrapper);
1693 this._proportionallyResize();
1697 //TODO: What's that good for? There's not anything to be executed left
1698 if(!$(this.handles[i]).length)
1704 //TODO: make renderAxis a prototype function
1705 this._renderAxis(this.element);
1707 this._handles = $('.ui-resizable-handle', this.element)
1708 .disableSelection();
1710 //Matching axis name
1711 this._handles.mouseover(function() {
1712 if (!self.resizing) {
1714 var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
1715 //Axis, default = se
1716 self.axis = axis && axis[1] ? axis[1] : 'se';
1720 //If we want to auto hide the elements
1722 this._handles.hide();
1724 .addClass("ui-resizable-autohide")
1726 $(this).removeClass("ui-resizable-autohide");
1727 self._handles.show();
1730 if (!self.resizing) {
1731 $(this).addClass("ui-resizable-autohide");
1732 self._handles.hide();
1737 //Initialize the mouse interaction
1742 destroy: function() {
1744 this._mouseDestroy();
1746 var _destroy = function(exp) {
1747 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
1748 .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
1751 //TODO: Unwrap at same DOM position
1752 if (this.elementIsWrapper) {
1753 _destroy(this.element);
1754 var wrapper = this.element;
1755 wrapper.parent().append(
1756 this.originalElement.css({
1757 position: wrapper.css('position'),
1758 width: wrapper.outerWidth(),
1759 height: wrapper.outerHeight(),
1760 top: wrapper.css('top'),
1761 left: wrapper.css('left')
1766 this.originalElement.css('resize', this.originalResizeStyle);
1767 _destroy(this.originalElement);
1771 _mouseCapture: function(event) {
1774 for(var i in this.handles) {
1775 if($(this.handles[i])[0] == event.target) handle = true;
1778 return this.options.disabled || !!handle;
1782 _mouseStart: function(event) {
1784 var o = this.options, iniPos = this.element.position(), el = this.element;
1786 this.resizing = true;
1787 this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
1789 // bugfix for http://dev.jquery.com/ticket/1749
1790 if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
1791 el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
1794 //Opera fixing relative position
1795 if ($.browser.opera && (/relative/).test(el.css('position')))
1796 el.css({ position: 'relative', top: 'auto', left: 'auto' });
1798 this._renderProxy();
1800 var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
1802 if (o.containment) {
1803 curleft += $(o.containment).scrollLeft() || 0;
1804 curtop += $(o.containment).scrollTop() || 0;
1807 //Store needed variables
1808 this.offset = this.helper.offset();
1809 this.position = { left: curleft, top: curtop };
1810 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
1811 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
1812 this.originalPosition = { left: curleft, top: curtop };
1813 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
1814 this.originalMousePosition = { left: event.pageX, top: event.pageY };
1817 this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
1819 var cursor = $('.ui-resizable-' + this.axis).css('cursor');
1820 $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
1822 el.addClass("ui-resizable-resizing");
1823 this._propagate("start", event);
1827 _mouseDrag: function(event) {
1829 //Increase performance, avoid regex
1830 var el = this.helper, o = this.options, props = {},
1831 self = this, smp = this.originalMousePosition, a = this.axis;
1833 var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
1834 var trigger = this._change[a];
1835 if (!trigger) return false;
1837 // Calculate the attrs that will be change
1838 var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
1840 if (this._aspectRatio || event.shiftKey)
1841 data = this._updateRatio(data, event);
1843 data = this._respectSize(data, event);
1845 // plugins callbacks need to be called first
1846 this._propagate("resize", event);
1849 top: this.position.top + "px", left: this.position.left + "px",
1850 width: this.size.width + "px", height: this.size.height + "px"
1853 if (!this._helper && this._proportionallyResizeElements.length)
1854 this._proportionallyResize();
1856 this._updateCache(data);
1858 // calling the user callback at the end
1859 this._trigger('resize', event, this.ui());
1864 _mouseStop: function(event) {
1866 this.resizing = false;
1867 var o = this.options, self = this;
1870 var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
1871 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
1872 soffsetw = ista ? 0 : self.sizeDiff.width;
1874 var s = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
1875 left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
1876 top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
1879 this.element.css($.extend(s, { top: top, left: left }));
1881 self.helper.height(self.size.height);
1882 self.helper.width(self.size.width);
1884 if (this._helper && !o.animate) this._proportionallyResize();
1887 $('body').css('cursor', 'auto');
1889 this.element.removeClass("ui-resizable-resizing");
1891 this._propagate("stop", event);
1893 if (this._helper) this.helper.remove();
1898 _updateCache: function(data) {
1899 var o = this.options;
1900 this.offset = this.helper.offset();
1901 if (isNumber(data.left)) this.position.left = data.left;
1902 if (isNumber(data.top)) this.position.top = data.top;
1903 if (isNumber(data.height)) this.size.height = data.height;
1904 if (isNumber(data.width)) this.size.width = data.width;
1907 _updateRatio: function(data, event) {
1909 var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
1911 if (data.height) data.width = (csize.height * this.aspectRatio);
1912 else if (data.width) data.height = (csize.width / this.aspectRatio);
1915 data.left = cpos.left + (csize.width - data.width);
1919 data.top = cpos.top + (csize.height - data.height);
1920 data.left = cpos.left + (csize.width - data.width);
1926 _respectSize: function(data, event) {
1928 var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
1929 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
1930 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
1932 if (isminw) data.width = o.minWidth;
1933 if (isminh) data.height = o.minHeight;
1934 if (ismaxw) data.width = o.maxWidth;
1935 if (ismaxh) data.height = o.maxHeight;
1937 var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
1938 var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
1940 if (isminw && cw) data.left = dw - o.minWidth;
1941 if (ismaxw && cw) data.left = dw - o.maxWidth;
1942 if (isminh && ch) data.top = dh - o.minHeight;
1943 if (ismaxh && ch) data.top = dh - o.maxHeight;
1945 // fixing jump error on top/left - bug #2330
1946 var isNotwh = !data.width && !data.height;
1947 if (isNotwh && !data.left && data.top) data.top = null;
1948 else if (isNotwh && !data.top && data.left) data.left = null;
1953 _proportionallyResize: function() {
1955 var o = this.options;
1956 if (!this._proportionallyResizeElements.length) return;
1957 var element = this.helper || this.element;
1959 for (var i=0; i < this._proportionallyResizeElements.length; i++) {
1961 var prel = this._proportionallyResizeElements[i];
1963 if (!this.borderDif) {
1964 var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
1965 p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
1967 this.borderDif = $.map(b, function(v, i) {
1968 var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
1969 return border + padding;
1973 if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length)))
1977 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
1978 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
1985 _renderProxy: function() {
1987 var el = this.element, o = this.options;
1988 this.elementOffset = el.offset();
1992 this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
1994 // fix ie6 offset TODO: This seems broken
1995 var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
1996 pxyoffset = ( ie6 ? 2 : -1 );
1998 this.helper.addClass(this._helper).css({
1999 width: this.element.outerWidth() + pxyoffset,
2000 height: this.element.outerHeight() + pxyoffset,
2001 position: 'absolute',
2002 left: this.elementOffset.left - ie6offset +'px',
2003 top: this.elementOffset.top - ie6offset +'px',
2004 zIndex: ++o.zIndex //TODO: Don't modify option
2009 .disableSelection();
2012 this.helper = this.element;
2018 e: function(event, dx, dy) {
2019 return { width: this.originalSize.width + dx };
2021 w: function(event, dx, dy) {
2022 var o = this.options, cs = this.originalSize, sp = this.originalPosition;
2023 return { left: sp.left + dx, width: cs.width - dx };
2025 n: function(event, dx, dy) {
2026 var o = this.options, cs = this.originalSize, sp = this.originalPosition;
2027 return { top: sp.top + dy, height: cs.height - dy };
2029 s: function(event, dx, dy) {
2030 return { height: this.originalSize.height + dy };
2032 se: function(event, dx, dy) {
2033 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2035 sw: function(event, dx, dy) {
2036 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2038 ne: function(event, dx, dy) {
2039 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2041 nw: function(event, dx, dy) {
2042 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2046 _propagate: function(n, event) {
2047 $.ui.plugin.call(this, n, [event, this.ui()]);
2048 (n != "resize" && this._trigger(n, event, this.ui()));
2055 originalElement: this.originalElement,
2056 element: this.element,
2057 helper: this.helper,
2058 position: this.position,
2060 originalSize: this.originalSize,
2061 originalPosition: this.originalPosition
2067 $.extend($.ui.resizable, {
2069 eventPrefix: "resize",
2073 animateDuration: "slow",
2074 animateEasing: "swing",
2077 cancel: ":input,option",
2094 * Resizable Extensions
2097 $.ui.plugin.add("resizable", "alsoResize", {
2099 start: function(event, ui) {
2101 var self = $(this).data("resizable"), o = self.options;
2103 _store = function(exp) {
2104 $(exp).each(function() {
2105 $(this).data("resizable-alsoresize", {
2106 width: parseInt($(this).width(), 10), height: parseInt($(this).height(), 10),
2107 left: parseInt($(this).css('left'), 10), top: parseInt($(this).css('top'), 10)
2112 if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
2113 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
2114 else { $.each(o.alsoResize, function(exp, c) { _store(exp); }); }
2116 _store(o.alsoResize);
2120 resize: function(event, ui){
2121 var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
2124 height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
2125 top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
2128 _alsoResize = function(exp, c) {
2129 $(exp).each(function() {
2130 var el = $(this), start = $(this).data("resizable-alsoresize"), style = {}, css = c && c.length ? c : ['width', 'height', 'top', 'left'];
2132 $.each(css || ['width', 'height', 'top', 'left'], function(i, prop) {
2133 var sum = (start[prop]||0) + (delta[prop]||0);
2134 if (sum && sum >= 0)
2135 style[prop] = sum || null;
2138 //Opera fixing relative position
2139 if (/relative/.test(el.css('position')) && $.browser.opera) {
2140 self._revertToRelativePosition = true;
2141 el.css({ position: 'absolute', top: 'auto', left: 'auto' });
2148 if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
2149 $.each(o.alsoResize, function(exp, c) { _alsoResize(exp, c); });
2151 _alsoResize(o.alsoResize);
2155 stop: function(event, ui){
2156 var self = $(this).data("resizable");
2158 //Opera fixing relative position
2159 if (self._revertToRelativePosition && $.browser.opera) {
2160 self._revertToRelativePosition = false;
2161 el.css({ position: 'relative' });
2164 $(this).removeData("resizable-alsoresize-start");
2168 $.ui.plugin.add("resizable", "animate", {
2170 stop: function(event, ui) {
2171 var self = $(this).data("resizable"), o = self.options;
2173 var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2174 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
2175 soffsetw = ista ? 0 : self.sizeDiff.width;
2177 var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
2178 left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
2179 top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
2181 self.element.animate(
2182 $.extend(style, top && left ? { top: top, left: left } : {}), {
2183 duration: o.animateDuration,
2184 easing: o.animateEasing,
2188 width: parseInt(self.element.css('width'), 10),
2189 height: parseInt(self.element.css('height'), 10),
2190 top: parseInt(self.element.css('top'), 10),
2191 left: parseInt(self.element.css('left'), 10)
2194 if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
2196 // propagating resize, and updating values for each animation step
2197 self._updateCache(data);
2198 self._propagate("resize", event);
2207 $.ui.plugin.add("resizable", "containment", {
2209 start: function(event, ui) {
2210 var self = $(this).data("resizable"), o = self.options, el = self.element;
2211 var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
2214 self.containerElement = $(ce);
2216 if (/document/.test(oc) || oc == document) {
2217 self.containerOffset = { left: 0, top: 0 };
2218 self.containerPosition = { left: 0, top: 0 };
2221 element: $(document), left: 0, top: 0,
2222 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
2226 // i'm a node, so compute top, left, right, bottom
2228 var element = $(ce), p = [];
2229 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
2231 self.containerOffset = element.offset();
2232 self.containerPosition = element.position();
2233 self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
2235 var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width,
2236 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
2239 element: ce, left: co.left, top: co.top, width: width, height: height
2244 resize: function(event, ui) {
2245 var self = $(this).data("resizable"), o = self.options,
2246 ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
2247 pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement;
2249 if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
2251 if (cp.left < (self._helper ? co.left : 0)) {
2252 self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left));
2253 if (pRatio) self.size.height = self.size.width / o.aspectRatio;
2254 self.position.left = o.helper ? co.left : 0;
2257 if (cp.top < (self._helper ? co.top : 0)) {
2258 self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top);
2259 if (pRatio) self.size.width = self.size.height * o.aspectRatio;
2260 self.position.top = self._helper ? co.top : 0;
2263 self.offset.left = self.parentData.left+self.position.left;
2264 self.offset.top = self.parentData.top+self.position.top;
2266 var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ),
2267 hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height );
2269 var isParent = self.containerElement.get(0) == self.element.parent().get(0),
2270 isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position'));
2272 if(isParent && isOffsetRelative) woset -= self.parentData.left;
2274 if (woset + self.size.width >= self.parentData.width) {
2275 self.size.width = self.parentData.width - woset;
2276 if (pRatio) self.size.height = self.size.width / self.aspectRatio;
2279 if (hoset + self.size.height >= self.parentData.height) {
2280 self.size.height = self.parentData.height - hoset;
2281 if (pRatio) self.size.width = self.size.height * self.aspectRatio;
2285 stop: function(event, ui){
2286 var self = $(this).data("resizable"), o = self.options, cp = self.position,
2287 co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
2289 var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height;
2291 if (self._helper && !o.animate && (/relative/).test(ce.css('position')))
2292 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
2294 if (self._helper && !o.animate && (/static/).test(ce.css('position')))
2295 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
2300 $.ui.plugin.add("resizable", "ghost", {
2302 start: function(event, ui) {
2304 var self = $(this).data("resizable"), o = self.options, cs = self.size;
2306 self.ghost = self.originalElement.clone();
2308 .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
2309 .addClass('ui-resizable-ghost')
2310 .addClass(typeof o.ghost == 'string' ? o.ghost : '');
2312 self.ghost.appendTo(self.helper);
2316 resize: function(event, ui){
2317 var self = $(this).data("resizable"), o = self.options;
2318 if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
2321 stop: function(event, ui){
2322 var self = $(this).data("resizable"), o = self.options;
2323 if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
2328 $.ui.plugin.add("resizable", "grid", {
2330 resize: function(event, ui) {
2331 var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey;
2332 o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
2333 var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
2335 if (/^(se|s|e)$/.test(a)) {
2336 self.size.width = os.width + ox;
2337 self.size.height = os.height + oy;
2339 else if (/^(ne)$/.test(a)) {
2340 self.size.width = os.width + ox;
2341 self.size.height = os.height + oy;
2342 self.position.top = op.top - oy;
2344 else if (/^(sw)$/.test(a)) {
2345 self.size.width = os.width + ox;
2346 self.size.height = os.height + oy;
2347 self.position.left = op.left - ox;
2350 self.size.width = os.width + ox;
2351 self.size.height = os.height + oy;
2352 self.position.top = op.top - oy;
2353 self.position.left = op.left - ox;
2359 var num = function(v) {
2360 return parseInt(v, 10) || 0;
2363 var isNumber = function(value) {
2364 return !isNaN(parseInt(value, 10));
2369 * jQuery UI Selectable 1.7.2
2371 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
2372 * Dual licensed under the MIT (MIT-LICENSE.txt)
2373 * and GPL (GPL-LICENSE.txt) licenses.
2375 * http://docs.jquery.com/UI/Selectables
2382 $.widget("ui.selectable", $.extend({}, $.ui.mouse, {
2387 this.element.addClass("ui-selectable");
2389 this.dragged = false;
2391 // cache selectee children based on filter
2393 this.refresh = function() {
2394 selectees = $(self.options.filter, self.element[0]);
2395 selectees.each(function() {
2396 var $this = $(this);
2397 var pos = $this.offset();
2398 $.data(this, "selectable-item", {
2403 right: pos.left + $this.outerWidth(),
2404 bottom: pos.top + $this.outerHeight(),
2405 startselected: false,
2406 selected: $this.hasClass('ui-selected'),
2407 selecting: $this.hasClass('ui-selecting'),
2408 unselecting: $this.hasClass('ui-unselecting')
2414 this.selectees = selectees.addClass("ui-selectee");
2418 this.helper = $(document.createElement('div'))
2419 .css({border:'1px dotted black'})
2420 .addClass("ui-selectable-helper");
2423 destroy: function() {
2425 .removeClass("ui-selectable ui-selectable-disabled")
2426 .removeData("selectable")
2427 .unbind(".selectable");
2428 this._mouseDestroy();
2431 _mouseStart: function(event) {
2434 this.opos = [event.pageX, event.pageY];
2436 if (this.options.disabled)
2439 var options = this.options;
2441 this.selectees = $(options.filter, this.element[0]);
2443 this._trigger("start", event);
2445 $(options.appendTo).append(this.helper);
2446 // position helper (lasso)
2449 "position": "absolute",
2450 "left": event.clientX,
2451 "top": event.clientY,
2456 if (options.autoRefresh) {
2460 this.selectees.filter('.ui-selected').each(function() {
2461 var selectee = $.data(this, "selectable-item");
2462 selectee.startselected = true;
2463 if (!event.metaKey) {
2464 selectee.$element.removeClass('ui-selected');
2465 selectee.selected = false;
2466 selectee.$element.addClass('ui-unselecting');
2467 selectee.unselecting = true;
2468 // selectable UNSELECTING callback
2469 self._trigger("unselecting", event, {
2470 unselecting: selectee.element
2475 $(event.target).parents().andSelf().each(function() {
2476 var selectee = $.data(this, "selectable-item");
2478 selectee.$element.removeClass("ui-unselecting").addClass('ui-selecting');
2479 selectee.unselecting = false;
2480 selectee.selecting = true;
2481 selectee.selected = true;
2482 // selectable SELECTING callback
2483 self._trigger("selecting", event, {
2484 selecting: selectee.element
2492 _mouseDrag: function(event) {
2494 this.dragged = true;
2496 if (this.options.disabled)
2499 var options = this.options;
2501 var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
2502 if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
2503 if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
2504 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
2506 this.selectees.each(function() {
2507 var selectee = $.data(this, "selectable-item");
2508 //prevent helper from being selected if appendTo: selectable
2509 if (!selectee || selectee.element == self.element[0])
2512 if (options.tolerance == 'touch') {
2513 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
2514 } else if (options.tolerance == 'fit') {
2515 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
2520 if (selectee.selected) {
2521 selectee.$element.removeClass('ui-selected');
2522 selectee.selected = false;
2524 if (selectee.unselecting) {
2525 selectee.$element.removeClass('ui-unselecting');
2526 selectee.unselecting = false;
2528 if (!selectee.selecting) {
2529 selectee.$element.addClass('ui-selecting');
2530 selectee.selecting = true;
2531 // selectable SELECTING callback
2532 self._trigger("selecting", event, {
2533 selecting: selectee.element
2538 if (selectee.selecting) {
2539 if (event.metaKey && selectee.startselected) {
2540 selectee.$element.removeClass('ui-selecting');
2541 selectee.selecting = false;
2542 selectee.$element.addClass('ui-selected');
2543 selectee.selected = true;
2545 selectee.$element.removeClass('ui-selecting');
2546 selectee.selecting = false;
2547 if (selectee.startselected) {
2548 selectee.$element.addClass('ui-unselecting');
2549 selectee.unselecting = true;
2551 // selectable UNSELECTING callback
2552 self._trigger("unselecting", event, {
2553 unselecting: selectee.element
2557 if (selectee.selected) {
2558 if (!event.metaKey && !selectee.startselected) {
2559 selectee.$element.removeClass('ui-selected');
2560 selectee.selected = false;
2562 selectee.$element.addClass('ui-unselecting');
2563 selectee.unselecting = true;
2564 // selectable UNSELECTING callback
2565 self._trigger("unselecting", event, {
2566 unselecting: selectee.element
2576 _mouseStop: function(event) {
2579 this.dragged = false;
2581 var options = this.options;
2583 $('.ui-unselecting', this.element[0]).each(function() {
2584 var selectee = $.data(this, "selectable-item");
2585 selectee.$element.removeClass('ui-unselecting');
2586 selectee.unselecting = false;
2587 selectee.startselected = false;
2588 self._trigger("unselected", event, {
2589 unselected: selectee.element
2592 $('.ui-selecting', this.element[0]).each(function() {
2593 var selectee = $.data(this, "selectable-item");
2594 selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
2595 selectee.selecting = false;
2596 selectee.selected = true;
2597 selectee.startselected = true;
2598 self._trigger("selected", event, {
2599 selected: selectee.element
2602 this._trigger("stop", event);
2604 this.helper.remove();
2611 $.extend($.ui.selectable, {
2616 cancel: ":input,option",