/*! * jQuery UI Droppable - v1.10.4 * http://jqueryui.com * * Copyright 2014 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/droppable/ */ define([ 'jquery', 'jquery-ui-modules/draggable' ], function ($, undefined) { function isOverAxis(x, reference, size) { return (x > reference) && (x < (reference + size)); } $.widget("ui.droppable", { version: "1.10.4", widgetEventPrefix: "drop", options: { accept: "*", activeClass: false, addClasses: true, greedy: false, hoverClass: false, scope: "default", tolerance: "intersect", // callbacks activate: null, deactivate: null, drop: null, out: null, over: null }, _create: function () { var proportions, o = this.options, accept = o.accept; this.isover = false; this.isout = true; this.accept = $.isFunction(accept) ? accept : function (d) { return d.is(accept); }; this.proportions = function ( /* valueToWrite */) { if (arguments.length) { // Store the droppable's proportions proportions = arguments[0]; } else { // Retrieve or derive the droppable's proportions return proportions ? proportions : proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight }; } }; // Add the reference and positions to the manager $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || []; $.ui.ddmanager.droppables[o.scope].push(this); (o.addClasses && this.element.addClass("ui-droppable")); }, _destroy: function () { var i = 0, drop = $.ui.ddmanager.droppables[this.options.scope]; for (; i < drop.length; i++) { if (drop[i] === this) { drop.splice(i, 1); } } this.element.removeClass("ui-droppable ui-droppable-disabled"); }, _setOption: function (key, value) { if (key === "accept") { this.accept = $.isFunction(value) ? value : function (d) { return d.is(value); }; } $.Widget.prototype._setOption.apply(this, arguments); }, _activate: function (event) { var draggable = $.ui.ddmanager.current; if (this.options.activeClass) { this.element.addClass(this.options.activeClass); } if (draggable) { this._trigger("activate", event, this.ui(draggable)); } }, _deactivate: function (event) { var draggable = $.ui.ddmanager.current; if (this.options.activeClass) { this.element.removeClass(this.options.activeClass); } if (draggable) { this._trigger("deactivate", event, this.ui(draggable)); } }, _over: function (event) { var draggable = $.ui.ddmanager.current; // Bail if draggable and droppable are same element if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { return; } if (this.accept.call(this.element[0], (draggable.currentItem || draggable.element))) { if (this.options.hoverClass) { this.element.addClass(this.options.hoverClass); } this._trigger("over", event, this.ui(draggable)); } }, _out: function (event) { var draggable = $.ui.ddmanager.current; // Bail if draggable and droppable are same element if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { return; } if (this.accept.call(this.element[0], (draggable.currentItem || draggable.element))) { if (this.options.hoverClass) { this.element.removeClass(this.options.hoverClass); } this._trigger("out", event, this.ui(draggable)); } }, _drop: function (event, custom) { var draggable = custom || $.ui.ddmanager.current, childrenIntersection = false; // Bail if draggable and droppable are same element if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { return false; } this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function () { var inst = $.data(this, "ui-droppable"); if ( inst.options.greedy && !inst.options.disabled && inst.options.scope === draggable.options.scope && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) && $.ui.intersect(draggable, $.extend(inst, {offset: inst.element.offset()}), inst.options.tolerance) ) { childrenIntersection = true; return false; } }); if (childrenIntersection) { return false; } if (this.accept.call(this.element[0], (draggable.currentItem || draggable.element))) { if (this.options.activeClass) { this.element.removeClass(this.options.activeClass); } if (this.options.hoverClass) { this.element.removeClass(this.options.hoverClass); } this._trigger("drop", event, this.ui(draggable)); return this.element; } return false; }, ui: function (c) { return { draggable: (c.currentItem || c.element), helper: c.helper, position: c.position, offset: c.positionAbs }; } }); $.ui.intersect = function (draggable, droppable, toleranceMode) { if (!droppable.offset) { return false; } var draggableLeft, draggableTop, x1 = (draggable.positionAbs || draggable.position.absolute).left, y1 = (draggable.positionAbs || draggable.position.absolute).top, x2 = x1 + draggable.helperProportions.width, y2 = y1 + draggable.helperProportions.height, l = droppable.offset.left, t = droppable.offset.top, r = l + droppable.proportions().width, b = t + droppable.proportions().height; switch (toleranceMode) { case "fit": return (l <= x1 && x2 <= r && t <= y1 && y2 <= b); case "intersect": return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half x2 - (draggable.helperProportions.width / 2) < r && // Left Half t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half y2 - (draggable.helperProportions.height / 2) < b); // Top Half case "pointer": draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left); draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top); return isOverAxis(draggableTop, t, droppable.proportions().height) && isOverAxis(draggableLeft, l, droppable.proportions().width); case "touch": return ( (y1 >= t && y1 <= b) || // Top edge touching (y2 >= t && y2 <= b) || // Bottom edge touching (y1 < t && y2 > b) // Surrounded vertically ) && ( (x1 >= l && x1 <= r) || // Left edge touching (x2 >= l && x2 <= r) || // Right edge touching (x1 < l && x2 > r) // Surrounded horizontally ); default: return false; } }; /* This manager tracks offsets of draggables and droppables */ $.ui.ddmanager = { current: null, droppables: {"default": []}, prepareOffsets: function (t, event) { var i, j, m = $.ui.ddmanager.droppables[t.options.scope] || [], type = event ? event.type : null, // workaround for #2317 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack(); droppablesLoop: for (i = 0; i < m.length; i++) { //No disabled and non-accepted if (m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0], (t.currentItem || t.element)))) { continue; } // Filter out elements in the current dragged item for (j = 0; j < list.length; j++) { if (list[j] === m[i].element[0]) { m[i].proportions().height = 0; continue droppablesLoop; } } m[i].visible = m[i].element.css("display") !== "none"; if (!m[i].visible) { continue; } //Activate the droppable if used directly from draggables if (type === "mousedown") { m[i]._activate.call(m[i], event); } m[i].offset = m[i].element.offset(); m[i].proportions({width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight}); } }, drop: function (draggable, event) { var dropped = false; // Create a copy of the droppables in case the list changes during the drop (#9116) $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function () { if (!this.options) { return; } if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) { dropped = this._drop.call(this, event) || dropped; } if (!this.options.disabled && this.visible && this.accept.call(this.element[0], (draggable.currentItem || draggable.element))) { this.isout = true; this.isover = false; this._deactivate.call(this, event); } }); return dropped; }, dragStart: function (draggable, event) { //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003) draggable.element.parentsUntil("body").bind("scroll.droppable", function () { if (!draggable.options.refreshPositions) { $.ui.ddmanager.prepareOffsets(draggable, event); } }); }, drag: function (draggable, event) { //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse. if (draggable.options.refreshPositions) { $.ui.ddmanager.prepareOffsets(draggable, event); } //Run through all droppables and check their positions based on specific tolerance options $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function () { if (this.options.disabled || this.greedyChild || !this.visible) { return; } var parentInstance, scope, parent, intersects = $.ui.intersect(draggable, this, this.options.tolerance), c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null); if (!c) { return; } if (this.options.greedy) { // find droppable parents with same scope scope = this.options.scope; parent = this.element.parents(":data(ui-droppable)").filter(function () { return $.data(this, "ui-droppable").options.scope === scope; }); if (parent.length) { parentInstance = $.data(parent[0], "ui-droppable"); parentInstance.greedyChild = (c === "isover"); } } // we just moved into a greedy child if (parentInstance && c === "isover") { parentInstance.isover = false; parentInstance.isout = true; parentInstance._out.call(parentInstance, event); } this[c] = true; this[c === "isout" ? "isover" : "isout"] = false; this[c === "isover" ? "_over" : "_out"].call(this, event); // we just moved out of a greedy child if (parentInstance && c === "isout") { parentInstance.isout = false; parentInstance.isover = true; parentInstance._over.call(parentInstance, event); } }); }, dragStop: function (draggable, event) { draggable.element.parentsUntil("body").unbind("scroll.droppable"); //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003) if (!draggable.options.refreshPositions) { $.ui.ddmanager.prepareOffsets(draggable, event); } } }; });