browser/components/tabview/trench.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 // **********
michael@0 6 // Title: trench.js
michael@0 7
michael@0 8 // ##########
michael@0 9 // Class: Trench
michael@0 10 //
michael@0 11 // Class for drag-snapping regions; called "trenches" as they are long and narrow.
michael@0 12
michael@0 13 // Constructor: Trench
michael@0 14 //
michael@0 15 // Parameters:
michael@0 16 // element - the DOM element for Item (GroupItem or TabItem) from which the trench is projected
michael@0 17 // xory - either "x" or "y": whether the trench's <position> is along the x- or y-axis.
michael@0 18 // In other words, if "x", the trench is vertical; if "y", the trench is horizontal.
michael@0 19 // type - either "border" or "guide". Border trenches mark the border of an Item.
michael@0 20 // Guide trenches extend out (unless they are intercepted) and act as "guides".
michael@0 21 // edge - which edge of the Item that this trench corresponds to.
michael@0 22 // Either "top", "left", "bottom", or "right".
michael@0 23 function Trench(element, xory, type, edge) {
michael@0 24 //----------
michael@0 25 // Variable: id
michael@0 26 // (integer) The id for the Trench. Set sequentially via <Trenches.nextId>
michael@0 27 this.id = Trenches.nextId++;
michael@0 28
michael@0 29 // ---------
michael@0 30 // Variables: Initial parameters
michael@0 31 // element - (DOMElement)
michael@0 32 // parentItem - <Item> which projects this trench; to be set with setParentItem
michael@0 33 // xory - (string) "x" or "y"
michael@0 34 // type - (string) "border" or "guide"
michael@0 35 // edge - (string) "top", "left", "bottom", or "right"
michael@0 36 this.el = element;
michael@0 37 this.parentItem = null;
michael@0 38 this.xory = xory; // either "x" or "y"
michael@0 39 this.type = type; // "border" or "guide"
michael@0 40 this.edge = edge; // "top", "left", "bottom", or "right"
michael@0 41
michael@0 42 this.$el = iQ(this.el);
michael@0 43
michael@0 44 //----------
michael@0 45 // Variable: dom
michael@0 46 // (array) DOM elements for visible reflexes of the Trench
michael@0 47 this.dom = [];
michael@0 48
michael@0 49 //----------
michael@0 50 // Variable: showGuide
michael@0 51 // (boolean) Whether this trench will project a visible guide (dotted line) or not.
michael@0 52 this.showGuide = false;
michael@0 53
michael@0 54 //----------
michael@0 55 // Variable: active
michael@0 56 // (boolean) Whether this trench is currently active or not.
michael@0 57 // Basically every trench aside for those projected by the Item currently being dragged
michael@0 58 // all become active.
michael@0 59 this.active = false;
michael@0 60 this.gutter = Items.defaultGutter;
michael@0 61
michael@0 62 //----------
michael@0 63 // Variable: position
michael@0 64 // (integer) position is the position that we should snap to.
michael@0 65 this.position = 0;
michael@0 66
michael@0 67 //----------
michael@0 68 // Variables: some Ranges
michael@0 69 // range - (<Range>) explicit range; this is along the transverse axis
michael@0 70 // minRange - (<Range>) the minimum active range
michael@0 71 // activeRange - (<Range>) the currently active range
michael@0 72 this.range = new Range(0,10000);
michael@0 73 this.minRange = new Range(0,0);
michael@0 74 this.activeRange = new Range(0,10000);
michael@0 75 };
michael@0 76
michael@0 77 Trench.prototype = {
michael@0 78 // ----------
michael@0 79 // Function: toString
michael@0 80 // Prints [Trench edge type (parentItem)] for debug use
michael@0 81 toString: function Trench_toString() {
michael@0 82 return "[Trench " + this.edge + " " + this.type +
michael@0 83 (this.parentItem ? " (" + this.parentItem + ")" : "") +
michael@0 84 "]";
michael@0 85 },
michael@0 86
michael@0 87 //----------
michael@0 88 // Variable: radius
michael@0 89 // (integer) radius is how far away we should snap from
michael@0 90 get radius() this.customRadius || Trenches.defaultRadius,
michael@0 91
michael@0 92 setParentItem: function Trench_setParentItem(item) {
michael@0 93 if (!item.isAnItem) {
michael@0 94 Utils.assert(false, "parentItem must be an Item");
michael@0 95 return false;
michael@0 96 }
michael@0 97 this.parentItem = item;
michael@0 98 return true;
michael@0 99 },
michael@0 100
michael@0 101 //----------
michael@0 102 // Function: setPosition
michael@0 103 // set the trench's position.
michael@0 104 //
michael@0 105 // Parameters:
michael@0 106 // position - (integer) px center position of the trench
michael@0 107 // range - (<Range>) the explicit active range of the trench
michael@0 108 // minRange - (<Range>) the minimum range of the trench
michael@0 109 setPosition: function Trench_setPosition(position, range, minRange) {
michael@0 110 this.position = position;
michael@0 111
michael@0 112 var page = Items.getPageBounds(true);
michael@0 113
michael@0 114 // optionally, set the range.
michael@0 115 if (Utils.isRange(range)) {
michael@0 116 this.range = range;
michael@0 117 } else {
michael@0 118 this.range = new Range(0, (this.xory == 'x' ? page.height : page.width));
michael@0 119 }
michael@0 120
michael@0 121 // if there's a minRange, set that too.
michael@0 122 if (Utils.isRange(minRange))
michael@0 123 this.minRange = minRange;
michael@0 124
michael@0 125 // set the appropriate bounds as a rect.
michael@0 126 if (this.xory == "x") // vertical
michael@0 127 this.rect = new Rect(this.position - this.radius, this.range.min, 2 * this.radius, this.range.extent);
michael@0 128 else // horizontal
michael@0 129 this.rect = new Rect(this.range.min, this.position - this.radius, this.range.extent, 2 * this.radius);
michael@0 130
michael@0 131 this.show(); // DEBUG
michael@0 132 },
michael@0 133
michael@0 134 //----------
michael@0 135 // Function: setActiveRange
michael@0 136 // set the trench's currently active range.
michael@0 137 //
michael@0 138 // Parameters:
michael@0 139 // activeRange - (<Range>)
michael@0 140 setActiveRange: function Trench_setActiveRange(activeRange) {
michael@0 141 if (!Utils.isRange(activeRange))
michael@0 142 return false;
michael@0 143 this.activeRange = activeRange;
michael@0 144 if (this.xory == "x") { // horizontal
michael@0 145 this.activeRect = new Rect(this.position - this.radius, this.activeRange.min, 2 * this.radius, this.activeRange.extent);
michael@0 146 this.guideRect = new Rect(this.position, this.activeRange.min, 0, this.activeRange.extent);
michael@0 147 } else { // vertical
michael@0 148 this.activeRect = new Rect(this.activeRange.min, this.position - this.radius, this.activeRange.extent, 2 * this.radius);
michael@0 149 this.guideRect = new Rect(this.activeRange.min, this.position, this.activeRange.extent, 0);
michael@0 150 }
michael@0 151 return true;
michael@0 152 },
michael@0 153
michael@0 154 //----------
michael@0 155 // Function: setWithRect
michael@0 156 // Set the trench's position using the given rect. We know which side of the rect we should match
michael@0 157 // because we've already recorded this information in <edge>.
michael@0 158 //
michael@0 159 // Parameters:
michael@0 160 // rect - (<Rect>)
michael@0 161 setWithRect: function Trench_setWithRect(rect) {
michael@0 162
michael@0 163 if (!Utils.isRect(rect))
michael@0 164 Utils.error('argument must be Rect');
michael@0 165
michael@0 166 // First, calculate the range for this trench.
michael@0 167 // Border trenches are always only active for the length of this range.
michael@0 168 // Guide trenches, however, still use this value as its minRange.
michael@0 169 if (this.xory == "x")
michael@0 170 var range = new Range(rect.top - this.gutter, rect.bottom + this.gutter);
michael@0 171 else
michael@0 172 var range = new Range(rect.left - this.gutter, rect.right + this.gutter);
michael@0 173
michael@0 174 if (this.type == "border") {
michael@0 175 // border trenches have a range, so set that too.
michael@0 176 if (this.edge == "left")
michael@0 177 this.setPosition(rect.left - this.gutter, range);
michael@0 178 else if (this.edge == "right")
michael@0 179 this.setPosition(rect.right + this.gutter, range);
michael@0 180 else if (this.edge == "top")
michael@0 181 this.setPosition(rect.top - this.gutter, range);
michael@0 182 else if (this.edge == "bottom")
michael@0 183 this.setPosition(rect.bottom + this.gutter, range);
michael@0 184 } else if (this.type == "guide") {
michael@0 185 // guide trenches have no range, but do have a minRange.
michael@0 186 if (this.edge == "left")
michael@0 187 this.setPosition(rect.left, false, range);
michael@0 188 else if (this.edge == "right")
michael@0 189 this.setPosition(rect.right, false, range);
michael@0 190 else if (this.edge == "top")
michael@0 191 this.setPosition(rect.top, false, range);
michael@0 192 else if (this.edge == "bottom")
michael@0 193 this.setPosition(rect.bottom, false, range);
michael@0 194 }
michael@0 195 },
michael@0 196
michael@0 197 //----------
michael@0 198 // Function: show
michael@0 199 //
michael@0 200 // Show guide (dotted line), if <showGuide> is true.
michael@0 201 //
michael@0 202 // If <Trenches.showDebug> is true, we will draw the trench. Active portions are drawn with 0.5
michael@0 203 // opacity. If <active> is false, the entire trench will be
michael@0 204 // very translucent.
michael@0 205 show: function Trench_show() { // DEBUG
michael@0 206 if (this.active && this.showGuide) {
michael@0 207 if (!this.dom.guideTrench)
michael@0 208 this.dom.guideTrench = iQ("<div/>").addClass('guideTrench').css({id: 'guideTrench'+this.id});
michael@0 209 var guideTrench = this.dom.guideTrench;
michael@0 210 guideTrench.css(this.guideRect);
michael@0 211 iQ("body").append(guideTrench);
michael@0 212 } else {
michael@0 213 if (this.dom.guideTrench) {
michael@0 214 this.dom.guideTrench.remove();
michael@0 215 delete this.dom.guideTrench;
michael@0 216 }
michael@0 217 }
michael@0 218
michael@0 219 if (!Trenches.showDebug) {
michael@0 220 this.hide(true); // true for dontHideGuides
michael@0 221 return;
michael@0 222 }
michael@0 223
michael@0 224 if (!this.dom.visibleTrench)
michael@0 225 this.dom.visibleTrench = iQ("<div/>")
michael@0 226 .addClass('visibleTrench')
michael@0 227 .addClass(this.type) // border or guide
michael@0 228 .css({id: 'visibleTrench'+this.id});
michael@0 229 var visibleTrench = this.dom.visibleTrench;
michael@0 230
michael@0 231 if (!this.dom.activeVisibleTrench)
michael@0 232 this.dom.activeVisibleTrench = iQ("<div/>")
michael@0 233 .addClass('activeVisibleTrench')
michael@0 234 .addClass(this.type) // border or guide
michael@0 235 .css({id: 'activeVisibleTrench'+this.id});
michael@0 236 var activeVisibleTrench = this.dom.activeVisibleTrench;
michael@0 237
michael@0 238 if (this.active)
michael@0 239 activeVisibleTrench.addClass('activeTrench');
michael@0 240 else
michael@0 241 activeVisibleTrench.removeClass('activeTrench');
michael@0 242
michael@0 243 visibleTrench.css(this.rect);
michael@0 244 activeVisibleTrench.css(this.activeRect || this.rect);
michael@0 245 iQ("body").append(visibleTrench);
michael@0 246 iQ("body").append(activeVisibleTrench);
michael@0 247 },
michael@0 248
michael@0 249 //----------
michael@0 250 // Function: hide
michael@0 251 // Hide the trench.
michael@0 252 hide: function Trench_hide(dontHideGuides) {
michael@0 253 if (this.dom.visibleTrench)
michael@0 254 this.dom.visibleTrench.remove();
michael@0 255 if (this.dom.activeVisibleTrench)
michael@0 256 this.dom.activeVisibleTrench.remove();
michael@0 257 if (!dontHideGuides && this.dom.guideTrench)
michael@0 258 this.dom.guideTrench.remove();
michael@0 259 },
michael@0 260
michael@0 261 //----------
michael@0 262 // Function: rectOverlaps
michael@0 263 // Given a <Rect>, compute whether it overlaps with this trench. If it does, return an
michael@0 264 // adjusted ("snapped") <Rect>; if it does not overlap, simply return false.
michael@0 265 //
michael@0 266 // Note that simply overlapping is not all that is required to be affected by this function.
michael@0 267 // Trenches can only affect certain edges of rectangles... for example, a "left"-edge guide
michael@0 268 // trench should only affect left edges of rectangles. We don't snap right edges to left-edged
michael@0 269 // guide trenches. For border trenches, the logic is a bit different, so left snaps to right and
michael@0 270 // top snaps to bottom.
michael@0 271 //
michael@0 272 // Parameters:
michael@0 273 // rect - (<Rect>) the rectangle in question
michael@0 274 // stationaryCorner - which corner is stationary? by default, the top left.
michael@0 275 // "topleft", "bottomleft", "topright", "bottomright"
michael@0 276 // assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
michael@0 277 // keepProportional - (boolean) if we are allowed to change the rect's size, whether the
michael@0 278 // dimensions should scaled proportionally or not.
michael@0 279 //
michael@0 280 // Returns:
michael@0 281 // false - if rect does not overlap with this trench
michael@0 282 // newRect - (<Rect>) an adjusted version of rect, if it is affected by this trench
michael@0 283 rectOverlaps: function Trench_rectOverlaps(rect,stationaryCorner,assumeConstantSize,keepProportional) {
michael@0 284 var edgeToCheck;
michael@0 285 if (this.type == "border") {
michael@0 286 if (this.edge == "left")
michael@0 287 edgeToCheck = "right";
michael@0 288 else if (this.edge == "right")
michael@0 289 edgeToCheck = "left";
michael@0 290 else if (this.edge == "top")
michael@0 291 edgeToCheck = "bottom";
michael@0 292 else if (this.edge == "bottom")
michael@0 293 edgeToCheck = "top";
michael@0 294 } else { // if trench type is guide or barrier...
michael@0 295 edgeToCheck = this.edge;
michael@0 296 }
michael@0 297
michael@0 298 rect.adjustedEdge = edgeToCheck;
michael@0 299
michael@0 300 switch (edgeToCheck) {
michael@0 301 case "left":
michael@0 302 if (this.ruleOverlaps(rect.left, rect.yRange)) {
michael@0 303 if (stationaryCorner.indexOf('right') > -1)
michael@0 304 rect.width = rect.right - this.position;
michael@0 305 rect.left = this.position;
michael@0 306 return rect;
michael@0 307 }
michael@0 308 break;
michael@0 309 case "right":
michael@0 310 if (this.ruleOverlaps(rect.right, rect.yRange)) {
michael@0 311 if (assumeConstantSize) {
michael@0 312 rect.left = this.position - rect.width;
michael@0 313 } else {
michael@0 314 var newWidth = this.position - rect.left;
michael@0 315 if (keepProportional)
michael@0 316 rect.height = rect.height * newWidth / rect.width;
michael@0 317 rect.width = newWidth;
michael@0 318 }
michael@0 319 return rect;
michael@0 320 }
michael@0 321 break;
michael@0 322 case "top":
michael@0 323 if (this.ruleOverlaps(rect.top, rect.xRange)) {
michael@0 324 if (stationaryCorner.indexOf('bottom') > -1)
michael@0 325 rect.height = rect.bottom - this.position;
michael@0 326 rect.top = this.position;
michael@0 327 return rect;
michael@0 328 }
michael@0 329 break;
michael@0 330 case "bottom":
michael@0 331 if (this.ruleOverlaps(rect.bottom, rect.xRange)) {
michael@0 332 if (assumeConstantSize) {
michael@0 333 rect.top = this.position - rect.height;
michael@0 334 } else {
michael@0 335 var newHeight = this.position - rect.top;
michael@0 336 if (keepProportional)
michael@0 337 rect.width = rect.width * newHeight / rect.height;
michael@0 338 rect.height = newHeight;
michael@0 339 }
michael@0 340 return rect;
michael@0 341 }
michael@0 342 }
michael@0 343
michael@0 344 return false;
michael@0 345 },
michael@0 346
michael@0 347 //----------
michael@0 348 // Function: ruleOverlaps
michael@0 349 // Computes whether the given "rule" (a line segment, essentially), given by the position and
michael@0 350 // range arguments, overlaps with the current trench. Note that this function assumes that
michael@0 351 // the rule and the trench are in the same direction: both horizontal, or both vertical.
michael@0 352 //
michael@0 353 // Parameters:
michael@0 354 // position - (integer) a position in px
michael@0 355 // range - (<Range>) the rule's range
michael@0 356 ruleOverlaps: function Trench_ruleOverlaps(position, range) {
michael@0 357 return (this.position - this.radius < position &&
michael@0 358 position < this.position + this.radius &&
michael@0 359 this.activeRange.overlaps(range));
michael@0 360 },
michael@0 361
michael@0 362 //----------
michael@0 363 // Function: adjustRangeIfIntercept
michael@0 364 // Computes whether the given boundary (given as a position and its active range), perpendicular
michael@0 365 // to the trench, intercepts the trench or not. If it does, it returns an adjusted <Range> for
michael@0 366 // the trench. If not, it returns false.
michael@0 367 //
michael@0 368 // Parameters:
michael@0 369 // position - (integer) the position of the boundary
michael@0 370 // range - (<Range>) the target's range, on the trench's transverse axis
michael@0 371 adjustRangeIfIntercept: function Trench_adjustRangeIfIntercept(position, range) {
michael@0 372 if (this.position - this.radius > range.min && this.position + this.radius < range.max) {
michael@0 373 var activeRange = new Range(this.activeRange);
michael@0 374
michael@0 375 // there are three ways this can go:
michael@0 376 // 1. position < minRange.min
michael@0 377 // 2. position > minRange.max
michael@0 378 // 3. position >= minRange.min && position <= minRange.max
michael@0 379
michael@0 380 if (position < this.minRange.min) {
michael@0 381 activeRange.min = Math.min(this.minRange.min,position);
michael@0 382 } else if (position > this.minRange.max) {
michael@0 383 activeRange.max = Math.max(this.minRange.max,position);
michael@0 384 } else {
michael@0 385 // this should be impossible because items can't overlap and we've already checked
michael@0 386 // that the range intercepts.
michael@0 387 }
michael@0 388 return activeRange;
michael@0 389 }
michael@0 390 return false;
michael@0 391 },
michael@0 392
michael@0 393 //----------
michael@0 394 // Function: calculateActiveRange
michael@0 395 // Computes and sets the <activeRange> for the trench, based on the <GroupItems> around.
michael@0 396 // This makes it so trenches' active ranges don't extend through other groupItems.
michael@0 397 calculateActiveRange: function Trench_calculateActiveRange() {
michael@0 398
michael@0 399 // set it to the default: just the range itself.
michael@0 400 this.setActiveRange(this.range);
michael@0 401
michael@0 402 // only guide-type trenches need to set a separate active range
michael@0 403 if (this.type != 'guide')
michael@0 404 return;
michael@0 405
michael@0 406 var groupItems = GroupItems.groupItems;
michael@0 407 var trench = this;
michael@0 408 groupItems.forEach(function(groupItem) {
michael@0 409 if (groupItem.isDragging) // floating groupItems don't block trenches
michael@0 410 return;
michael@0 411 if (trench.el == groupItem.container) // groupItems don't block their own trenches
michael@0 412 return;
michael@0 413 var bounds = groupItem.getBounds();
michael@0 414 var activeRange = new Range();
michael@0 415 if (trench.xory == 'y') { // if this trench is horizontal...
michael@0 416 activeRange = trench.adjustRangeIfIntercept(bounds.left, bounds.yRange);
michael@0 417 if (activeRange)
michael@0 418 trench.setActiveRange(activeRange);
michael@0 419 activeRange = trench.adjustRangeIfIntercept(bounds.right, bounds.yRange);
michael@0 420 if (activeRange)
michael@0 421 trench.setActiveRange(activeRange);
michael@0 422 } else { // if this trench is vertical...
michael@0 423 activeRange = trench.adjustRangeIfIntercept(bounds.top, bounds.xRange);
michael@0 424 if (activeRange)
michael@0 425 trench.setActiveRange(activeRange);
michael@0 426 activeRange = trench.adjustRangeIfIntercept(bounds.bottom, bounds.xRange);
michael@0 427 if (activeRange)
michael@0 428 trench.setActiveRange(activeRange);
michael@0 429 }
michael@0 430 });
michael@0 431 }
michael@0 432 };
michael@0 433
michael@0 434 // ##########
michael@0 435 // Class: Trenches
michael@0 436 // Singelton for managing all <Trench>es.
michael@0 437 var Trenches = {
michael@0 438 // ---------
michael@0 439 // Variables:
michael@0 440 // nextId - (integer) a counter for the next <Trench>'s <Trench.id> value.
michael@0 441 // showDebug - (boolean) whether to draw the <Trench>es or not.
michael@0 442 // defaultRadius - (integer) the default radius for new <Trench>es.
michael@0 443 // disabled - (boolean) whether trench-snapping is disabled or not.
michael@0 444 nextId: 0,
michael@0 445 showDebug: false,
michael@0 446 defaultRadius: 10,
michael@0 447 disabled: false,
michael@0 448
michael@0 449 // ---------
michael@0 450 // Variables: snapping preferences; used to break ties in snapping.
michael@0 451 // preferTop - (boolean) prefer snapping to the top to the bottom
michael@0 452 // preferLeft - (boolean) prefer snapping to the left to the right
michael@0 453 preferTop: true,
michael@0 454 get preferLeft() { return !UI.rtl; },
michael@0 455
michael@0 456 trenches: [],
michael@0 457
michael@0 458 // ----------
michael@0 459 // Function: toString
michael@0 460 // Prints [Trenches count=count] for debug use
michael@0 461 toString: function Trenches_toString() {
michael@0 462 return "[Trenches count=" + this.trenches.length + "]";
michael@0 463 },
michael@0 464
michael@0 465 // ---------
michael@0 466 // Function: getById
michael@0 467 // Return the specified <Trench>.
michael@0 468 //
michael@0 469 // Parameters:
michael@0 470 // id - (integer)
michael@0 471 getById: function Trenches_getById(id) {
michael@0 472 return this.trenches[id];
michael@0 473 },
michael@0 474
michael@0 475 // ---------
michael@0 476 // Function: register
michael@0 477 // Register a new <Trench> and returns the resulting <Trench> ID.
michael@0 478 //
michael@0 479 // Parameters:
michael@0 480 // See the constructor <Trench.Trench>'s parameters.
michael@0 481 //
michael@0 482 // Returns:
michael@0 483 // id - (int) the new <Trench>'s ID.
michael@0 484 register: function Trenches_register(element, xory, type, edge) {
michael@0 485 var trench = new Trench(element, xory, type, edge);
michael@0 486 this.trenches[trench.id] = trench;
michael@0 487 return trench.id;
michael@0 488 },
michael@0 489
michael@0 490 // ---------
michael@0 491 // Function: registerWithItem
michael@0 492 // Register a whole set of <Trench>es using an <Item> and returns the resulting <Trench> IDs.
michael@0 493 //
michael@0 494 // Parameters:
michael@0 495 // item - the <Item> to project trenches
michael@0 496 // type - either "border" or "guide"
michael@0 497 //
michael@0 498 // Returns:
michael@0 499 // ids - array of the new <Trench>es' IDs.
michael@0 500 registerWithItem: function Trenches_registerWithItem(item, type) {
michael@0 501 var container = item.container;
michael@0 502 var ids = {};
michael@0 503 ids.left = Trenches.register(container,"x",type,"left");
michael@0 504 ids.right = Trenches.register(container,"x",type,"right");
michael@0 505 ids.top = Trenches.register(container,"y",type,"top");
michael@0 506 ids.bottom = Trenches.register(container,"y",type,"bottom");
michael@0 507
michael@0 508 this.getById(ids.left).setParentItem(item);
michael@0 509 this.getById(ids.right).setParentItem(item);
michael@0 510 this.getById(ids.top).setParentItem(item);
michael@0 511 this.getById(ids.bottom).setParentItem(item);
michael@0 512
michael@0 513 return ids;
michael@0 514 },
michael@0 515
michael@0 516 // ---------
michael@0 517 // Function: unregister
michael@0 518 // Unregister one or more <Trench>es.
michael@0 519 //
michael@0 520 // Parameters:
michael@0 521 // ids - (integer) a single <Trench> ID or (array) a list of <Trench> IDs.
michael@0 522 unregister: function Trenches_unregister(ids) {
michael@0 523 if (!Array.isArray(ids))
michael@0 524 ids = [ids];
michael@0 525 var self = this;
michael@0 526 ids.forEach(function(id) {
michael@0 527 self.trenches[id].hide();
michael@0 528 delete self.trenches[id];
michael@0 529 });
michael@0 530 },
michael@0 531
michael@0 532 // ---------
michael@0 533 // Function: activateOthersTrenches
michael@0 534 // Activate all <Trench>es other than those projected by the current element.
michael@0 535 //
michael@0 536 // Parameters:
michael@0 537 // element - (DOMElement) the DOM element of the Item being dragged or resized.
michael@0 538 activateOthersTrenches: function Trenches_activateOthersTrenches(element) {
michael@0 539 this.trenches.forEach(function(t) {
michael@0 540 if (t.el === element)
michael@0 541 return;
michael@0 542 if (t.parentItem && (t.parentItem.isAFauxItem || t.parentItem.isDragging))
michael@0 543 return;
michael@0 544 t.active = true;
michael@0 545 t.calculateActiveRange();
michael@0 546 t.show(); // debug
michael@0 547 });
michael@0 548 },
michael@0 549
michael@0 550 // ---------
michael@0 551 // Function: disactivate
michael@0 552 // After <activateOthersTrenches>, disactivates all the <Trench>es again.
michael@0 553 disactivate: function Trenches_disactivate() {
michael@0 554 this.trenches.forEach(function(t) {
michael@0 555 t.active = false;
michael@0 556 t.showGuide = false;
michael@0 557 t.show();
michael@0 558 });
michael@0 559 },
michael@0 560
michael@0 561 // ---------
michael@0 562 // Function: hideGuides
michael@0 563 // Hide all guides (dotted lines) en masse.
michael@0 564 hideGuides: function Trenches_hideGuides() {
michael@0 565 this.trenches.forEach(function(t) {
michael@0 566 t.showGuide = false;
michael@0 567 t.show();
michael@0 568 });
michael@0 569 },
michael@0 570
michael@0 571 // ---------
michael@0 572 // Function: snap
michael@0 573 // Used to "snap" an object's bounds to active trenches and to the edge of the window.
michael@0 574 // If the meta key is down (<Key.meta>), it will not snap but will still enforce the rect
michael@0 575 // not leaving the safe bounds of the window.
michael@0 576 //
michael@0 577 // Parameters:
michael@0 578 // rect - (<Rect>) the object's current bounds
michael@0 579 // stationaryCorner - which corner is stationary? by default, the top left.
michael@0 580 // "topleft", "bottomleft", "topright", "bottomright"
michael@0 581 // assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
michael@0 582 // keepProportional - (boolean) if we are allowed to change the rect's size, whether the
michael@0 583 // dimensions should scaled proportionally or not.
michael@0 584 //
michael@0 585 // Returns:
michael@0 586 // (<Rect>) - the updated bounds, if they were updated
michael@0 587 // false - if the bounds were not updated
michael@0 588 snap: function Trenches_snap(rect,stationaryCorner,assumeConstantSize,keepProportional) {
michael@0 589 // hide all the guide trenches, because the correct ones will be turned on later.
michael@0 590 Trenches.hideGuides();
michael@0 591
michael@0 592 var updated = false;
michael@0 593 var updatedX = false;
michael@0 594 var updatedY = false;
michael@0 595
michael@0 596 var snappedTrenches = {};
michael@0 597
michael@0 598 for (var i in this.trenches) {
michael@0 599 var t = this.trenches[i];
michael@0 600 if (!t.active)
michael@0 601 continue;
michael@0 602 // newRect will be a new rect, or false
michael@0 603 var newRect = t.rectOverlaps(rect,stationaryCorner,assumeConstantSize,keepProportional);
michael@0 604
michael@0 605 if (newRect) { // if rectOverlaps returned an updated rect...
michael@0 606
michael@0 607 if (assumeConstantSize && updatedX && updatedY)
michael@0 608 break;
michael@0 609 if (assumeConstantSize && updatedX && (newRect.adjustedEdge == "left"||newRect.adjustedEdge == "right"))
michael@0 610 continue;
michael@0 611 if (assumeConstantSize && updatedY && (newRect.adjustedEdge == "top"||newRect.adjustedEdge == "bottom"))
michael@0 612 continue;
michael@0 613
michael@0 614 rect = newRect;
michael@0 615 updated = true;
michael@0 616
michael@0 617 // register this trench as the "snapped trench" for the appropriate edge.
michael@0 618 snappedTrenches[newRect.adjustedEdge] = t;
michael@0 619
michael@0 620 // if updatedX, we don't need to update x any more.
michael@0 621 if (newRect.adjustedEdge == "left" && this.preferLeft)
michael@0 622 updatedX = true;
michael@0 623 if (newRect.adjustedEdge == "right" && !this.preferLeft)
michael@0 624 updatedX = true;
michael@0 625
michael@0 626 // if updatedY, we don't need to update x any more.
michael@0 627 if (newRect.adjustedEdge == "top" && this.preferTop)
michael@0 628 updatedY = true;
michael@0 629 if (newRect.adjustedEdge == "bottom" && !this.preferTop)
michael@0 630 updatedY = true;
michael@0 631
michael@0 632 }
michael@0 633 }
michael@0 634
michael@0 635 if (updated) {
michael@0 636 rect.snappedTrenches = snappedTrenches;
michael@0 637 return rect;
michael@0 638 }
michael@0 639 return false;
michael@0 640 },
michael@0 641
michael@0 642 // ---------
michael@0 643 // Function: show
michael@0 644 // <Trench.show> all <Trench>es.
michael@0 645 show: function Trenches_show() {
michael@0 646 this.trenches.forEach(function(t) {
michael@0 647 t.show();
michael@0 648 });
michael@0 649 },
michael@0 650
michael@0 651 // ---------
michael@0 652 // Function: toggleShown
michael@0 653 // Toggle <Trenches.showDebug> and trigger <Trenches.show>
michael@0 654 toggleShown: function Trenches_toggleShown() {
michael@0 655 this.showDebug = !this.showDebug;
michael@0 656 this.show();
michael@0 657 }
michael@0 658 };

mercurial