1/** 2 * Copyright (c) 2017, CTI LOGIC 3 * Copyright (c) 2006-2017, JGraph Ltd 4 * Copyright (c) 2006-2017, Gaudenz Alder 5 * 6 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 * 10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 * 12 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 15 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 16 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 17 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 */ 19 20//TODO integrate this code in mxGuide (Especially as this is now affecting the other guides) 21(function() 22{ 23 var guideMove = mxGuide.prototype.move; 24 25 mxGuide.prototype.move = function (bounds, delta, gridEnabled, clone) 26 { 27 var yShift = delta.y; 28 var xShift = delta.x; 29 var hasHorGuides = false; 30 var hasVerGuides = false; 31 32 if (this.states != null && bounds != null && delta != null) 33 { 34 var guide = this; 35 var newState = new mxCellState(); 36 var scale = this.graph.getView().scale; 37 var tolerance = Math.max(2, this.getGuideTolerance() / 2); 38 39 newState.x = bounds.x + xShift; 40 newState.y = bounds.y + yShift; 41 newState.width = bounds.width; 42 newState.height = bounds.height; 43 var verticalCells = []; 44 var horizontalCells = []; 45 46 //although states are defined as cellState, it has some mxRectangles! 47 var states = []; 48 49 for (var i = 0; i < this.states.length; i++) 50 { 51 var state = this.states[i]; 52 var found = false; 53 54 if (state instanceof mxCellState) 55 { 56 if (clone || !this.graph.isCellSelected(state.cell)) 57 { 58 if (((newState.x >= state.x && newState.x <= (state.x + state.width)) 59 || (state.x >= newState.x && state.x <= (newState.x + newState.width))) 60 && (newState.y > state.y + state.height + 4|| newState.y + newState.height + 4 < state.y)) // + 4 to avoid having dy = 0 considered which cause a bug with 3 cells case 61 { 62 verticalCells.push(state); 63 } 64 else if (((newState.y >= state.y && newState.y <= (state.y + state.height)) 65 || (state.y >= newState.y && state.y <= (newState.y + newState.height))) 66 && (newState.x > state.x + state.width + 4 || newState.x + newState.width + 4 < state.x)) // + 4 to avoid having dy = 0 considered which cause a bug with 3 cells case 67 { 68 horizontalCells.push(state); 69 } 70 } 71 } 72 } 73 74 var eqCy = 0; 75 var dy = 0; 76 var fixedDy = 0; 77 var midDy = 0; 78 var eqCx = 0; 79 var dx = 0; 80 var fixedDx = 0; 81 var midDx = 0; 82 var shift = 5 * scale; 83 84 if (verticalCells.length > 1) 85 { 86 verticalCells.push(newState); 87 88 verticalCells.sort(function(s1, s2) 89 { 90 return s1.y - s2.y; 91 }); 92 93 var newStatePassed = false; 94 var firstMoving = newState == verticalCells[0]; 95 var lastMoving = newState == verticalCells[verticalCells.length - 1]; 96 97 //find the mid space and use it as dy and fixedDy 98 if (!firstMoving && !lastMoving) 99 { 100 for (var i = 1; i < verticalCells.length - 1; i++) 101 { 102 if (newState == verticalCells[i]) 103 { 104 var s1 = verticalCells[i - 1]; 105 var s3 = verticalCells[i + 1]; 106 midDy = (s3.y - s1.y - s1.height - newState.height) / 2; 107 dy = midDy; 108 fixedDy = dy; 109 break; 110 } 111 } 112 } 113 114 for (var i = 0; i < verticalCells.length - 1; i++) 115 { 116 var s1 = verticalCells[i]; 117 var s2 = verticalCells[i + 1]; 118 var isMovingOne = newState == s1 || newState == s2; 119 var curDy = s2.y - s1.y - s1.height; 120 121 newStatePassed |= newState == s1; 122 123 if (dy == 0 && eqCy == 0) 124 { 125 dy = curDy; 126 eqCy = 1; 127 } 128 else if (Math.abs(dy - curDy) <= (isMovingOne || (i == 1 && newStatePassed)? tolerance : 0)) //non-moving cells must have exact same dy, must handle the case of having the first cell moving so we allow tolerance for second cell (until fixedDy is non-zero) 129 { 130 eqCy += 1; 131 } 132 else if (eqCy > 1 && newStatePassed) //stop and ignore the following cells 133 { 134 verticalCells = verticalCells.slice(0, i + 1); 135 break; 136 } 137 else if (verticalCells.length - i >= 3 && !newStatePassed) //reset and start counting again 138 { 139 eqCy = 0; 140 dy = midDy != 0? midDy : 0; 141 fixedDy = dy; 142 verticalCells.splice(0, i == 0? 1 : i); 143 i = -1; 144 } 145 else 146 { 147 break; 148 } 149 150 if (fixedDy == 0 && !isMovingOne) 151 { 152 fixedDy = curDy; 153 //Update dy such that following cells shows equal distance guides without tolerance 154 dy = fixedDy; 155 } 156 } 157 158 if (verticalCells.length == 3 && verticalCells[1] == newState) 159 { 160 fixedDy = 0; 161 } 162 } 163 164 if (horizontalCells.length > 1) 165 { 166 horizontalCells.push(newState) 167 168 horizontalCells.sort(function(s1, s2) 169 { 170 return s1.x - s2.x; 171 }); 172 173 var newStatePassed = false; 174 var firstMoving = newState == horizontalCells[0]; 175 var lastMoving = newState == horizontalCells[horizontalCells.length - 1]; 176 177 //find the mid space and use it as dx and fixedDx 178 if (!firstMoving && !lastMoving) 179 { 180 for (var i = 1; i < horizontalCells.length - 1; i++) 181 { 182 if (newState == horizontalCells[i]) 183 { 184 var s1 = horizontalCells[i - 1]; 185 var s3 = horizontalCells[i + 1]; 186 midDx = (s3.x - s1.x - s1.width - newState.width) / 2; 187 dx = midDx; 188 fixedDx = dx; 189 break; 190 } 191 } 192 } 193 194 for (var i = 0; i < horizontalCells.length - 1; i++) 195 { 196 var s1 = horizontalCells[i]; 197 var s2 = horizontalCells[i + 1]; 198 var isMovingOne = newState == s1 || newState == s2; 199 var curDx = s2.x - s1.x - s1.width; 200 201 newStatePassed |= newState == s1; 202 203 if (dx == 0 && eqCx == 0) 204 { 205 dx = curDx; 206 eqCx = 1; 207 } 208 else if (Math.abs(dx - curDx) <= (isMovingOne || (i == 1 && newStatePassed)? tolerance : 0)) 209 { 210 eqCx += 1; 211 } 212 else if (eqCx > 1 && newStatePassed) //stop and ignore the following cells 213 { 214 horizontalCells = horizontalCells.slice(0, i + 1); 215 break; 216 } 217 else if (horizontalCells.length - i >= 3 && !newStatePassed) //reset and start counting again 218 { 219 eqCx = 0; 220 dx = midDx != 0? midDx : 0; 221 fixedDx = dx; 222 horizontalCells.splice(0, i == 0? 1 : i); 223 i = -1; 224 } 225 else 226 { 227 break; 228 } 229 230 if (fixedDx == 0 && !isMovingOne) 231 { 232 fixedDx = curDx; 233 //Update dx such that following cells shows equal distance guides without tolerance 234 dx = fixedDx; 235 } 236 } 237 if (horizontalCells.length == 3 && horizontalCells[1] == newState) 238 { 239 fixedDx = 0; 240 } 241 } 242 243 var createEqGuide = function(p1, p2, curGuide, isVer) 244 { 245 var points = []; 246 var dx = 0 247 var dy = 0; 248 249 if (isVer) 250 { 251 dx = shift; 252 dy = 0; 253 } 254 else 255 { 256 dx = 0; 257 dy = shift; 258 } 259 260 points.push(new mxPoint(p1.x - dx, p1.y - dy)); 261 points.push(new mxPoint(p1.x + dx, p1.y + dy)); 262 points.push(p1); 263 points.push(p2); 264 points.push(new mxPoint(p2.x - dx, p2.y - dy)); 265 points.push(new mxPoint(p2.x + dx, p2.y + dy)); 266 267 if (curGuide != null) 268 { 269 curGuide.points = points; 270 return curGuide; 271 } 272 else 273 { 274 var guideEq = new mxPolyline(points, mxConstants.GUIDE_COLOR, mxConstants.GUIDE_STROKEWIDTH); 275 guideEq.dialect = mxConstants.DIALECT_SVG; 276 guideEq.pointerEvents = false; 277 guideEq.init(guide.graph.getView().getOverlayPane()); 278 return guideEq; 279 } 280 }; 281 282 var hideEqGuides = function(horizontal, vertical) 283 { 284 if (horizontal && guide.guidesArrHor != null) 285 { 286 for (var i = 0; i < guide.guidesArrHor.length; i++) 287 { 288 guide.guidesArrHor[i].node.style.visibility = "hidden"; 289 } 290 } 291 292 if (vertical && guide.guidesArrVer != null) 293 { 294 for (var i = 0; i < guide.guidesArrVer.length; i++) 295 { 296 guide.guidesArrVer[i].node.style.visibility = "hidden"; 297 } 298 } 299 }; 300 301 if (eqCx > 1 && eqCx == horizontalCells.length - 1) 302 { 303 var guidesArr = []; 304 var curArr = guide.guidesArrHor; 305 var hPoints = []; 306 var newX = 0; 307 308 //If the newState (moving cell) is the first one, use the next one for x coordinate such that the guide doesn't move with the cell 309 var firstI = horizontalCells[0] == newState? 1 : 0; 310 var firstY = horizontalCells[firstI].y + horizontalCells[firstI].height; 311 312 if (fixedDx > 0) 313 { 314 for (var i = 0; i < horizontalCells.length - 1; i++) 315 { 316 var s1 = horizontalCells[i]; 317 var s2 = horizontalCells[i + 1]; 318 319 if (newState == s1) 320 { 321 newX = s2.x - s1.width - fixedDx; 322 hPoints.push(new mxPoint(newX + s1.width + shift, firstY)); 323 hPoints.push(new mxPoint(s2.x - shift, firstY)); 324 } 325 else if (newState == s2) 326 { 327 hPoints.push(new mxPoint(s1.x + s1.width + shift, firstY)); 328 newX = s1.x + s1.width + fixedDx; 329 hPoints.push(new mxPoint(newX - shift, firstY)); 330 } 331 else 332 { 333 hPoints.push(new mxPoint(s1.x + s1.width + shift, firstY)); 334 hPoints.push(new mxPoint(s2.x - shift, firstY)); 335 } 336 } 337 } 338 else //this is the case when there are 3 cells and the middle one is moving 339 { 340 var s1 = horizontalCells[0]; 341 var s3 = horizontalCells[2]; 342 newX = s1.x + s1.width + (s3.x - s1.x - s1.width - newState.width) / 2; 343 hPoints.push(new mxPoint(s1.x + s1.width + shift, firstY)); 344 hPoints.push(new mxPoint(newX - shift, firstY)); 345 hPoints.push(new mxPoint(newX + newState.width + shift, firstY)); 346 hPoints.push(new mxPoint(s3.x - shift, firstY)); 347 } 348 349 for (var i = 0; i < hPoints.length; i += 2) 350 { 351 var p1 = hPoints[i]; 352 var p2 = hPoints[i+1]; 353 var guideEq = createEqGuide(p1, p2, curArr != null ? curArr[i/2] : null); 354 guideEq.node.style.visibility = "visible"; 355 guideEq.redraw(); 356 guidesArr.push(guideEq); 357 } 358 359 //destroy old non-recycled guides 360 for (var i = hPoints.length / 2; curArr != null && i < curArr.length; i ++) 361 { 362 curArr[i].destroy(); 363 } 364 365 guide.guidesArrHor = guidesArr; 366 367 xShift = newX - bounds.x; 368 hasHorGuides = true; 369 } 370 else 371 { 372 hideEqGuides(true); 373 } 374 375 if (eqCy > 1 && eqCy == verticalCells.length - 1) 376 { 377 var guidesArr = []; 378 var curArr = guide.guidesArrVer; 379 var vPoints = []; 380 var newY = 0; 381 382 //If the newState (moving cell) is the first one, use the next one for x coordinate such that the guide doesn't move with the cell 383 var firstI = verticalCells[0] == newState? 1 : 0; 384 var firstX = verticalCells[firstI].x + verticalCells[firstI].width; 385 386 if (fixedDy > 0) 387 { 388 for (var i = 0; i < verticalCells.length - 1; i++) 389 { 390 var s1 = verticalCells[i]; 391 var s2 = verticalCells[i + 1]; 392 393 if (newState == s1) 394 { 395 newY = s2.y - s1.height - fixedDy; 396 vPoints.push(new mxPoint(firstX, newY + s1.height + shift)); 397 vPoints.push(new mxPoint(firstX, s2.y - shift)); 398 } 399 else if (newState == s2) 400 { 401 vPoints.push(new mxPoint(firstX, s1.y + s1.height + shift)); 402 newY = s1.y + s1.height + fixedDy; 403 vPoints.push(new mxPoint(firstX, newY - shift)); 404 } 405 else 406 { 407 vPoints.push(new mxPoint(firstX, s1.y + s1.height + shift)); 408 vPoints.push(new mxPoint(firstX, s2.y - shift)); 409 } 410 } 411 } 412 else //this is the case when there are 3 cells and the middle one is moving 413 { 414 var s1 = verticalCells[0]; 415 var s3 = verticalCells[2]; 416 newY = s1.y + s1.height + (s3.y - s1.y - s1.height - newState.height) / 2; 417 vPoints.push(new mxPoint(firstX, s1.y + s1.height + shift)); 418 vPoints.push(new mxPoint(firstX, newY - shift)); 419 vPoints.push(new mxPoint(firstX, newY + newState.height + shift)); 420 vPoints.push(new mxPoint(firstX, s3.y - shift)); 421 } 422 423 for (var i = 0; i < vPoints.length; i += 2) 424 { 425 var p1 = vPoints[i]; 426 var p2 = vPoints[i+1]; 427 var guideEq = createEqGuide(p1, p2, curArr != null ? curArr[i/2] : null, true); 428 guideEq.node.style.visibility = "visible"; 429 guideEq.redraw(); 430 guidesArr.push(guideEq); 431 } 432 433 //destroy old non-recycled guides 434 for (var i = vPoints.length / 2; curArr != null && i < curArr.length; i ++) 435 { 436 curArr[i].destroy(); 437 } 438 439 guide.guidesArrVer = guidesArr; 440 441 yShift = newY - bounds.y; 442 hasVerGuides = true; 443 } 444 else 445 { 446 hideEqGuides(false, true); 447 } 448 } 449 450 if (hasHorGuides || hasVerGuides) 451 { 452 var eqPoint = new mxPoint(xShift, yShift); 453 var newPoint = guideMove.call(this, bounds, eqPoint, gridEnabled, clone); 454 455 //Adjust our point to match non-conflicting other guides 456 if (hasHorGuides && !hasVerGuides) 457 { 458 eqPoint.y = newPoint.y; 459 } 460 else if (hasVerGuides && !hasHorGuides) 461 { 462 eqPoint.x = newPoint.x; 463 } 464 465 //Hide other guide if this guide overrides them 466 if (newPoint.y != eqPoint.y) 467 { 468 if (this.guideY != null && this.guideY.node != null) 469 { 470 this.guideY.node.style.visibility = 'hidden'; 471 } 472 } 473 if (newPoint.x != eqPoint.x) 474 { 475 if (this.guideX != null && this.guideX.node != null) 476 { 477 this.guideX.node.style.visibility = 'hidden'; 478 } 479 } 480 481 return eqPoint; 482 } 483 else 484 { 485 hideEqGuides(true, true); 486 return guideMove.apply(this, arguments); 487 } 488 }; 489 490 var guideSetVisible = mxGuide.prototype.setVisible; 491 492 mxGuide.prototype.setVisible = function (visible) 493 { 494 var guide = this; 495 guideSetVisible.call(guide, visible); 496 497 var guidesArrVer = guide.guidesArrVer; 498 var guidesArrHor = guide.guidesArrHor; 499 500 if (guidesArrVer != null) 501 { 502 for (var i = 0; i < guidesArrVer.length; i++) 503 { 504 guidesArrVer[i].node.style.visibility = visible? "visible" : "hidden"; 505 } 506 } 507 508 if (guidesArrHor != null) 509 { 510 for (var i = 0; i < guidesArrHor.length; i++) 511 { 512 guidesArrHor[i].node.style.visibility = visible? "visible" : "hidden"; 513 } 514 } 515 }; 516 517 var guideDestroy = mxGuide.prototype.destroy; 518 519 mxGuide.prototype.destroy = function() 520 { 521 guideDestroy.call(this); 522 var guidesArrVer = this.guidesArrVer; 523 var guidesArrHor = this.guidesArrHor; 524 525 if (guidesArrVer != null) 526 { 527 for (var i = 0; i < guidesArrVer.length; i++) 528 { 529 guidesArrVer[i].destroy(); 530 } 531 this.guidesArrVer = null; 532 } 533 534 if (guidesArrHor != null) 535 { 536 for (var i = 0; i < guidesArrHor.length; i++) 537 { 538 guidesArrHor[i].destroy(); 539 } 540 this.guidesArrHor = null; 541 } 542 }; 543})();