[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('dd-constrain', function (Y, NAME) { 9 10 11 /** 12 * The Drag & Drop Utility allows you to create a draggable interface efficiently, 13 * buffering you from browser-level abnormalities and enabling you to focus on the interesting 14 * logic surrounding your particular implementation. This component enables you to create a 15 * variety of standard draggable objects with just a few lines of code and then, 16 * using its extensive API, add your own specific implementation logic. 17 * @module dd 18 * @main dd 19 * @submodule dd-constrain 20 */ 21 /** 22 * Plugin for the dd-drag module to add the constraining methods to it. 23 * It supports constraining to a node or viewport. It supports tick based moves and XY axis constraints. 24 * @class DDConstrained 25 * @extends Base 26 * @constructor 27 * @namespace Plugin 28 */ 29 30 var DRAG_NODE = 'dragNode', 31 OFFSET_HEIGHT = 'offsetHeight', 32 OFFSET_WIDTH = 'offsetWidth', 33 HOST = 'host', 34 TICK_X_ARRAY = 'tickXArray', 35 TICK_Y_ARRAY = 'tickYArray', 36 DDM = Y.DD.DDM, 37 TOP = 'top', 38 RIGHT = 'right', 39 BOTTOM = 'bottom', 40 LEFT = 'left', 41 VIEW = 'view', 42 proto = null, 43 44 /** 45 * Fires when this node is aligned with the tickX value. 46 * @event drag:tickAlignX 47 * @param {EventFacade} event An Event Facade object 48 * @type {CustomEvent} 49 */ 50 EV_TICK_ALIGN_X = 'drag:tickAlignX', 51 52 /** 53 * Fires when this node is aligned with the tickY value. 54 * @event drag:tickAlignY 55 * @param {EventFacade} event An Event Facade object 56 * @type {CustomEvent} 57 */ 58 EV_TICK_ALIGN_Y = 'drag:tickAlignY', 59 60 C = function() { 61 this._lazyAddAttrs = false; 62 C.superclass.constructor.apply(this, arguments); 63 }; 64 65 C.NAME = 'ddConstrained'; 66 /** 67 * The Constrained instance will be placed on the Drag instance under the con namespace. 68 * @property NS 69 * @default con 70 * @readonly 71 * @protected 72 * @static 73 * @type {String} 74 */ 75 C.NS = 'con'; 76 77 C.ATTRS = { 78 host: { 79 }, 80 /** 81 * Stick the drag movement to the X-Axis. Default: false 82 * @attribute stickX 83 * @type Boolean 84 */ 85 stickX: { 86 value: false 87 }, 88 /** 89 * Stick the drag movement to the Y-Axis 90 * @type Boolean 91 * @attribute stickY 92 */ 93 stickY: { 94 value: false 95 }, 96 /** 97 * The X tick offset the drag node should snap to on each drag move. False for no ticks. Default: false 98 * @type Number/false 99 * @attribute tickX 100 */ 101 tickX: { 102 value: false 103 }, 104 /** 105 * The Y tick offset the drag node should snap to on each drag move. False for no ticks. Default: false 106 * @type Number/false 107 * @attribute tickY 108 */ 109 tickY: { 110 value: false 111 }, 112 /** 113 * An array of page coordinates to use as X ticks for drag movement. 114 * @type Array 115 * @attribute tickXArray 116 */ 117 tickXArray: { 118 value: false 119 }, 120 /** 121 * An array of page coordinates to use as Y ticks for drag movement. 122 * @type Array 123 * @attribute tickYArray 124 */ 125 tickYArray: { 126 value: false 127 }, 128 /** 129 * CSS style string for the gutter of a region (supports negative values): '5 0' 130 * (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px) 131 * @attribute gutter 132 * @type String 133 */ 134 gutter: { 135 value: '0', 136 setter: function(gutter) { 137 return Y.DD.DDM.cssSizestoObject(gutter); 138 } 139 }, 140 /** 141 * Will attempt to constrain the drag node to the boundaries. Arguments:<br> 142 * 'view': Contrain to Viewport<br> 143 * '#selector_string': Constrain to this node<br> 144 * '{Region Object}': An Object Literal containing a valid region (top, right, bottom, left) of page positions 145 * @attribute constrain 146 * @type {String/Object/Node} 147 */ 148 constrain: { 149 value: VIEW, 150 setter: function(con) { 151 var node = Y.one(con); 152 if (node) { 153 con = node; 154 } 155 return con; 156 } 157 }, 158 /** 159 * An Object Literal containing a valid region (top, right, bottom, left) of page positions to constrain the drag node to. 160 * @deprecated 161 * @attribute constrain2region 162 * @type Object 163 */ 164 constrain2region: { 165 setter: function(r) { 166 return this.set('constrain', r); 167 } 168 }, 169 /** 170 * Will attempt to constrain the drag node to the boundaries of this node. 171 * @deprecated 172 * @attribute constrain2node 173 * @type Object 174 */ 175 constrain2node: { 176 setter: function(n) { 177 return this.set('constrain', Y.one(n)); 178 } 179 }, 180 /** 181 * Will attempt to constrain the drag node to the boundaries of the viewport region. 182 * @deprecated 183 * @attribute constrain2view 184 * @type Object 185 */ 186 constrain2view: { 187 setter: function() { 188 return this.set('constrain', VIEW); 189 } 190 }, 191 /** 192 * Should the region be cached for performace. Default: true 193 * @attribute cacheRegion 194 * @type Boolean 195 */ 196 cacheRegion: { 197 value: true 198 } 199 }; 200 201 proto = { 202 _lastTickXFired: null, 203 _lastTickYFired: null, 204 205 initializer: function() { 206 this._createEvents(); 207 208 this._eventHandles = [ 209 this.get(HOST).on('drag:end', Y.bind(this._handleEnd, this)), 210 this.get(HOST).on('drag:start', Y.bind(this._handleStart, this)), 211 this.get(HOST).after('drag:align', Y.bind(this.align, this)), 212 this.get(HOST).after('drag:drag', Y.bind(this.drag, this)) 213 ]; 214 }, 215 destructor: function() { 216 Y.Array.each( 217 this._eventHandles, 218 function(handle) { 219 handle.detach(); 220 } 221 ); 222 223 this._eventHandles.length = 0; 224 }, 225 /** 226 * This method creates all the events for this Event Target and publishes them so we get Event Bubbling. 227 * @private 228 * @method _createEvents 229 */ 230 _createEvents: function() { 231 var ev = [ 232 EV_TICK_ALIGN_X, 233 EV_TICK_ALIGN_Y 234 ]; 235 236 Y.Array.each(ev, function(v) { 237 this.publish(v, { 238 type: v, 239 emitFacade: true, 240 bubbles: true, 241 queuable: false, 242 prefix: 'drag' 243 }); 244 }, this); 245 }, 246 /** 247 * Fires on drag:end 248 * @private 249 * @method _handleEnd 250 */ 251 _handleEnd: function() { 252 this._lastTickYFired = null; 253 this._lastTickXFired = null; 254 }, 255 /** 256 * Fires on drag:start and clears the _regionCache 257 * @private 258 * @method _handleStart 259 */ 260 _handleStart: function() { 261 this.resetCache(); 262 }, 263 /** 264 * Store a cache of the region that we are constraining to 265 * @private 266 * @property _regionCache 267 * @type Object 268 */ 269 _regionCache: null, 270 /** 271 * Get's the region and caches it, called from window.resize and when the cache is null 272 * @private 273 * @method _cacheRegion 274 */ 275 _cacheRegion: function() { 276 this._regionCache = this.get('constrain').get('region'); 277 }, 278 /** 279 * Reset the internal region cache. 280 * @method resetCache 281 */ 282 resetCache: function() { 283 this._regionCache = null; 284 }, 285 /** 286 * Standardizes the 'constraint' attribute 287 * @private 288 * @method _getConstraint 289 */ 290 _getConstraint: function() { 291 var con = this.get('constrain'), 292 g = this.get('gutter'), 293 region; 294 295 if (con) { 296 if (con instanceof Y.Node) { 297 if (!this._regionCache) { 298 this._eventHandles.push(Y.on('resize', Y.bind(this._cacheRegion, this), Y.config.win)); 299 this._cacheRegion(); 300 } 301 region = Y.clone(this._regionCache); 302 if (!this.get('cacheRegion')) { 303 this.resetCache(); 304 } 305 } else if (Y.Lang.isObject(con)) { 306 region = Y.clone(con); 307 } 308 } 309 if (!con || !region) { 310 con = VIEW; 311 } 312 if (con === VIEW) { 313 region = this.get(HOST).get(DRAG_NODE).get('viewportRegion'); 314 } 315 316 Y.Object.each(g, function(i, n) { 317 if ((n === RIGHT) || (n === BOTTOM)) { 318 region[n] -= i; 319 } else { 320 region[n] += i; 321 } 322 }); 323 return region; 324 }, 325 326 /** 327 * Get the active region: viewport, node, custom region 328 * @method getRegion 329 * @param {Boolean} inc Include the node's height and width 330 * @return {Object} The active region. 331 */ 332 getRegion: function(inc) { 333 var r = {}, oh = null, ow = null, 334 host = this.get(HOST); 335 336 r = this._getConstraint(); 337 338 if (inc) { 339 oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT); 340 ow = host.get(DRAG_NODE).get(OFFSET_WIDTH); 341 r[RIGHT] = r[RIGHT] - ow; 342 r[BOTTOM] = r[BOTTOM] - oh; 343 } 344 return r; 345 }, 346 /** 347 * Check if xy is inside a given region, if not change to it be inside. 348 * @private 349 * @method _checkRegion 350 * @param {Array} _xy The XY to check if it's in the current region, if it isn't 351 * inside the region, it will reset the xy array to be inside the region. 352 * @return {Array} The new XY that is inside the region 353 */ 354 _checkRegion: function(_xy) { 355 var oxy = _xy, 356 r = this.getRegion(), 357 host = this.get(HOST), 358 oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT), 359 ow = host.get(DRAG_NODE).get(OFFSET_WIDTH); 360 361 if (oxy[1] > (r[BOTTOM] - oh)) { 362 _xy[1] = (r[BOTTOM] - oh); 363 } 364 if (r[TOP] > oxy[1]) { 365 _xy[1] = r[TOP]; 366 367 } 368 if (oxy[0] > (r[RIGHT] - ow)) { 369 _xy[0] = (r[RIGHT] - ow); 370 } 371 if (r[LEFT] > oxy[0]) { 372 _xy[0] = r[LEFT]; 373 } 374 375 return _xy; 376 }, 377 /** 378 * Checks if the XY passed or the dragNode is inside the active region. 379 * @method inRegion 380 * @param {Array} xy Optional XY to check, if not supplied this.get('dragNode').getXY() is used. 381 * @return {Boolean} True if the XY is inside the region, false otherwise. 382 */ 383 inRegion: function(xy) { 384 xy = xy || this.get(HOST).get(DRAG_NODE).getXY(); 385 386 var _xy = this._checkRegion([xy[0], xy[1]]), 387 inside = false; 388 if ((xy[0] === _xy[0]) && (xy[1] === _xy[1])) { 389 inside = true; 390 } 391 return inside; 392 }, 393 /** 394 * Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens. 395 * @method align 396 */ 397 align: function() { 398 var host = this.get(HOST), 399 _xy = [host.actXY[0], host.actXY[1]], 400 r = this.getRegion(true); 401 402 if (this.get('stickX')) { 403 _xy[1] = (host.startXY[1] - host.deltaXY[1]); 404 } 405 if (this.get('stickY')) { 406 _xy[0] = (host.startXY[0] - host.deltaXY[0]); 407 } 408 409 if (r) { 410 _xy = this._checkRegion(_xy); 411 } 412 413 _xy = this._checkTicks(_xy, r); 414 415 host.actXY = _xy; 416 }, 417 /** 418 * Fires after drag:drag. Handle the tickX and tickX align events. 419 * @method drag 420 */ 421 drag: function() { 422 var host = this.get(HOST), 423 xt = this.get('tickX'), 424 yt = this.get('tickY'), 425 _xy = [host.actXY[0], host.actXY[1]]; 426 427 if ((Y.Lang.isNumber(xt) || this.get(TICK_X_ARRAY)) && (this._lastTickXFired !== _xy[0])) { 428 this._tickAlignX(); 429 this._lastTickXFired = _xy[0]; 430 } 431 432 if ((Y.Lang.isNumber(yt) || this.get(TICK_Y_ARRAY)) && (this._lastTickYFired !== _xy[1])) { 433 this._tickAlignY(); 434 this._lastTickYFired = _xy[1]; 435 } 436 }, 437 /** 438 * This method delegates the proper helper method for tick calculations 439 * @private 440 * @method _checkTicks 441 * @param {Array} xy The XY coords for the Drag 442 * @param {Object} r The optional region that we are bound to. 443 * @return {Array} The calced XY coords 444 */ 445 _checkTicks: function(xy, r) { 446 var host = this.get(HOST), 447 lx = (host.startXY[0] - host.deltaXY[0]), 448 ly = (host.startXY[1] - host.deltaXY[1]), 449 xt = this.get('tickX'), 450 yt = this.get('tickY'); 451 if (xt && !this.get(TICK_X_ARRAY)) { 452 xy[0] = DDM._calcTicks(xy[0], lx, xt, r[LEFT], r[RIGHT]); 453 } 454 if (yt && !this.get(TICK_Y_ARRAY)) { 455 xy[1] = DDM._calcTicks(xy[1], ly, yt, r[TOP], r[BOTTOM]); 456 } 457 if (this.get(TICK_X_ARRAY)) { 458 xy[0] = DDM._calcTickArray(xy[0], this.get(TICK_X_ARRAY), r[LEFT], r[RIGHT]); 459 } 460 if (this.get(TICK_Y_ARRAY)) { 461 xy[1] = DDM._calcTickArray(xy[1], this.get(TICK_Y_ARRAY), r[TOP], r[BOTTOM]); 462 } 463 464 return xy; 465 }, 466 /** 467 * Fires when the actXY[0] reach a new value respecting the tickX gap. 468 * @private 469 * @method _tickAlignX 470 */ 471 _tickAlignX: function() { 472 this.fire(EV_TICK_ALIGN_X); 473 }, 474 /** 475 * Fires when the actXY[1] reach a new value respecting the tickY gap. 476 * @private 477 * @method _tickAlignY 478 */ 479 _tickAlignY: function() { 480 this.fire(EV_TICK_ALIGN_Y); 481 } 482 }; 483 484 Y.namespace('Plugin'); 485 Y.extend(C, Y.Base, proto); 486 Y.Plugin.DDConstrained = C; 487 488 Y.mix(DDM, { 489 /** 490 * Helper method to calculate the tick offsets for a given position 491 * @for DDM 492 * @namespace DD 493 * @private 494 * @method _calcTicks 495 * @param {Number} pos The current X or Y position 496 * @param {Number} start The start X or Y position 497 * @param {Number} tick The X or Y tick increment 498 * @param {Number} off1 The min offset that we can't pass (region) 499 * @param {Number} off2 The max offset that we can't pass (region) 500 * @return {Number} The new position based on the tick calculation 501 */ 502 _calcTicks: function(pos, start, tick, off1, off2) { 503 var ix = ((pos - start) / tick), 504 min = Math.floor(ix), 505 max = Math.ceil(ix); 506 if ((min !== 0) || (max !== 0)) { 507 if ((ix >= min) && (ix <= max)) { 508 pos = (start + (tick * min)); 509 if (off1 && off2) { 510 if (pos < off1) { 511 pos = (start + (tick * (min + 1))); 512 } 513 if (pos > off2) { 514 pos = (start + (tick * (min - 1))); 515 } 516 } 517 } 518 } 519 return pos; 520 }, 521 /** 522 * This method is used with the tickXArray and tickYArray config options 523 * @for DDM 524 * @namespace DD 525 * @private 526 * @method _calcTickArray 527 * @param {Number} pos The current X or Y position 528 * @param {Number} ticks The array containing our custom tick positions. 529 * @param {Number} off1 The min offset that we can't pass (region) 530 * @param {Number} off2 The max offset that we can't pass (region) 531 * @return The tick position 532 */ 533 _calcTickArray: function(pos, ticks, off1, off2) { 534 var i = 0, len = ticks.length, next = 0, 535 diff1, diff2, ret; 536 537 if (!ticks || (ticks.length === 0)) { 538 return pos; 539 } 540 if (ticks[0] >= pos) { 541 return ticks[0]; 542 } 543 544 for (i = 0; i < len; i++) { 545 next = (i + 1); 546 if (ticks[next] && ticks[next] >= pos) { 547 diff1 = pos - ticks[i]; 548 diff2 = ticks[next] - pos; 549 ret = (diff2 > diff1) ? ticks[i] : ticks[next]; 550 if (off1 && off2) { 551 if (ret > off2) { 552 if (ticks[i]) { 553 ret = ticks[i]; 554 } else { 555 ret = ticks[len - 1]; 556 } 557 } 558 } 559 return ret; 560 } 561 562 } 563 return ticks[ticks.length - 1]; 564 } 565 }); 566 567 568 569 }, '3.17.2', {"requires": ["dd-drag"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |