1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// **********
// Title: drag.js
// ----------
// Variable: drag
// The Drag that's currently in process.
var drag = {
info: null,
zIndex: 100,
lastMoveTime: 0
};
//----------
//Variable: resize
//The resize (actually a Drag) that is currently in process
var resize = {
info: null,
lastMoveTime: 0
};
// ##########
// Class: Drag (formerly DragInfo)
// Helper class for dragging <Item>s
//
// ----------
// Constructor: Drag
// Called to create a Drag in response to an <Item> draggable "start" event.
// Note that it is also used partially during <Item>'s resizable method as well.
//
// Parameters:
// item - The <Item> being dragged
// event - The DOM event that kicks off the drag
function Drag(item, event) {
Utils.assert(item && (item.isAnItem || item.isAFauxItem),
'must be an item, or at least a faux item');
this.item = item;
this.el = item.container;
this.$el = iQ(this.el);
this.parent = this.item.parent;
this.startPosition = new Point(event.clientX, event.clientY);
this.startTime = Date.now();
this.item.isDragging = true;
this.item.setZ(999999);
this.safeWindowBounds = Items.getSafeWindowBounds();
Trenches.activateOthersTrenches(this.el);
};
Drag.prototype = {
// ----------
// Function: toString
// Prints [Drag (item)] for debug use
toString: function Drag_toString() {
return "[Drag (" + this.item + ")]";
},
// ----------
// Function: snapBounds
// Adjusts the given bounds according to the currently active trenches. Used by <Drag.snap>
//
// Parameters:
// bounds - (<Rect>) bounds
// stationaryCorner - which corner is stationary? by default, the top left in LTR mode,
// and top right in RTL mode.
// "topleft", "bottomleft", "topright", "bottomright"
// assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
// keepProportional - (boolean) if assumeConstantSize is false, whether we should resize
// proportionally or not
// checkItemStatus - (boolean) make sure this is a valid item which should be snapped
snapBounds: function Drag_snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, checkItemStatus) {
if (!stationaryCorner)
stationaryCorner = UI.rtl ? 'topright' : 'topleft';
var update = false; // need to update
var updateX = false;
var updateY = false;
var newRect;
var snappedTrenches = {};
// OH SNAP!
// if we aren't holding down the meta key or have trenches disabled...
if (!Keys.meta && !Trenches.disabled) {
// snappable = true if we aren't a tab on top of something else, and
// there's no active drop site...
let snappable = !(this.item.isATabItem &&
this.item.overlapsWithOtherItems()) &&
!iQ(".acceptsDrop").length;
if (!checkItemStatus || snappable) {
newRect = Trenches.snap(bounds, stationaryCorner, assumeConstantSize,
keepProportional);
if (newRect) { // might be false if no changes were made
update = true;
snappedTrenches = newRect.snappedTrenches || {};
bounds = newRect;
}
}
}
// make sure the bounds are in the window.
newRect = this.snapToEdge(bounds, stationaryCorner, assumeConstantSize,
keepProportional);
if (newRect) {
update = true;
bounds = newRect;
Utils.extend(snappedTrenches, newRect.snappedTrenches);
}
Trenches.hideGuides();
for (var edge in snappedTrenches) {
var trench = snappedTrenches[edge];
if (typeof trench == 'object') {
trench.showGuide = true;
trench.show();
}
}
return update ? bounds : false;
},
// ----------
// Function: snap
// Called when a drag or mousemove occurs. Set the bounds based on the mouse move first, then
// call snap and it will adjust the item's bounds if appropriate. Also triggers the display of
// trenches that it snapped to.
//
// Parameters:
// stationaryCorner - which corner is stationary? by default, the top left in LTR mode,
// and top right in RTL mode.
// "topleft", "bottomleft", "topright", "bottomright"
// assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
// keepProportional - (boolean) if assumeConstantSize is false, whether we should resize
// proportionally or not
snap: function Drag_snap(stationaryCorner, assumeConstantSize, keepProportional) {
var bounds = this.item.getBounds();
bounds = this.snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, true);
if (bounds) {
this.item.setBounds(bounds, true);
return true;
}
return false;
},
// --------
// Function: snapToEdge
// Returns a version of the bounds snapped to the edge if it is close enough. If not,
// returns false. If <Keys.meta> is true, this function will simply enforce the
// window edges.
//
// Parameters:
// rect - (<Rect>) current bounds of the object
// stationaryCorner - which corner is stationary? by default, the top left in LTR mode,
// and top right in RTL mode.
// "topleft", "bottomleft", "topright", "bottomright"
// assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
// keepProportional - (boolean) if we are allowed to change the rect's size, whether the
// dimensions should scaled proportionally or not.
snapToEdge: function Drag_snapToEdge(rect, stationaryCorner, assumeConstantSize, keepProportional) {
var swb = this.safeWindowBounds;
var update = false;
var updateX = false;
var updateY = false;
var snappedTrenches = {};
var snapRadius = (Keys.meta ? 0 : Trenches.defaultRadius);
if (rect.left < swb.left + snapRadius ) {
if (stationaryCorner.indexOf('right') > -1 && !assumeConstantSize)
rect.width = rect.right - swb.left;
rect.left = swb.left;
update = true;
updateX = true;
snappedTrenches.left = 'edge';
}
if (rect.right > swb.right - snapRadius) {
if (updateX || !assumeConstantSize) {
var newWidth = swb.right - rect.left;
if (keepProportional)
rect.height = rect.height * newWidth / rect.width;
rect.width = newWidth;
update = true;
} else if (!updateX || !Trenches.preferLeft) {
rect.left = swb.right - rect.width;
update = true;
}
snappedTrenches.right = 'edge';
delete snappedTrenches.left;
}
if (rect.top < swb.top + snapRadius) {
if (stationaryCorner.indexOf('bottom') > -1 && !assumeConstantSize)
rect.height = rect.bottom - swb.top;
rect.top = swb.top;
update = true;
updateY = true;
snappedTrenches.top = 'edge';
}
if (rect.bottom > swb.bottom - snapRadius) {
if (updateY || !assumeConstantSize) {
var newHeight = swb.bottom - rect.top;
if (keepProportional)
rect.width = rect.width * newHeight / rect.height;
rect.height = newHeight;
update = true;
} else if (!updateY || !Trenches.preferTop) {
rect.top = swb.bottom - rect.height;
update = true;
}
snappedTrenches.top = 'edge';
delete snappedTrenches.bottom;
}
if (update) {
rect.snappedTrenches = snappedTrenches;
return rect;
}
return false;
},
// ----------
// Function: drag
// Called in response to an <Item> draggable "drag" event.
drag: function Drag_drag(event) {
this.snap(UI.rtl ? 'topright' : 'topleft', true);
if (this.parent && this.parent.expanded) {
var distance = this.startPosition.distance(new Point(event.clientX, event.clientY));
if (distance > 100) {
this.parent.remove(this.item);
this.parent.collapse();
}
}
},
// ----------
// Function: stop
// Called in response to an <Item> draggable "stop" event.
//
// Parameters:
// immediately - bool for doing the pushAway immediately, without animation
stop: function Drag_stop(immediately) {
Trenches.hideGuides();
this.item.isDragging = false;
if (this.parent && this.parent != this.item.parent)
this.parent.closeIfEmpty();
if (this.parent && this.parent.expanded)
this.parent.arrange();
if (this.item.parent)
this.item.parent.arrange();
if (this.item.isAGroupItem) {
this.item.setZ(drag.zIndex);
drag.zIndex++;
this.item.pushAway(immediately);
}
Trenches.disactivate();
}
};
|