1/* 2 * ________ ________ 3 * ______(_)_____ ____ __________________ __ _____________________ ______(_)_______ 4 * _____ /_ __ `/ / / / _ \_ ___/_ / / / ___ __ \ _ \__ __ \ _____ /__ ___/ 5 * ____ / / /_/ // /_/ // __/ / _ /_/ /____ /_/ / __/_ /_/ /______ / _(__ ) 6 * ___ / \__, / \__,_/ \___//_/ _\__, /_(_) .___/\___/_ .___/_(_)__ / /____/ 7 * /___/ /_/ /____/ /_/ /_/ /___/ 8 * 9 * http://pep.briangonzalez.org 10 * Kinetic drag for mobile/desktop. 11 * 12 * Copyright (c) 2014 Brian Gonzalez 13 * Licensed under the MIT license. 14 * 15 * Title generated using "Speed" @ 16 * http://patorjk.com/software/taag/#p=display&f=Speed&t=jquery.pep.js 17 */ 18 19;(function ( $, window, undefined ) { 20 21 "use strict"; 22 23 // create the defaults once 24 var pluginName = 'pep'; 25 var defaults = { 26 27 // Options 28 // ---------------------------------------------------------------------------------------------- 29 // See ** https://github.com/briangonzalez/jquery.pep.js ** for fully documented options. 30 // It was too hard to manage options here and in the readme. 31 // ---------------------------------------------------------------------------------------------- 32 initiate: function(){}, 33 start: function(){}, 34 drag: function(){}, 35 stop: function(){}, 36 easing: null, 37 rest: function(){}, 38 moveTo: false, 39 callIfNotStarted: ['stop', 'rest'], 40 startThreshold: [0,0], 41 grid: [1,1], 42 debug: false, 43 activeClass: 'pep-active', 44 startClass: 'pep-start', 45 easeClass: 'pep-ease', 46 multiplier: 1, 47 velocityMultiplier: 2.5, 48 shouldPreventDefault: true, 49 allowDragEventPropagation: true, 50 stopEvents: '', 51 hardwareAccelerate: true, 52 useCSSTranslation: true, 53 disableSelect: true, 54 cssEaseString: "cubic-bezier(0.190, 1.000, 0.220, 1.000)", 55 cssEaseDuration: 1000, 56 shouldEase: true, 57 droppable: false, 58 droppableActiveClass: 'pep-dpa', 59 overlapFunction: false, 60 constrainTo: false, 61 removeMargins: true, 62 place: true, 63 deferPlacement: false, 64 axis: null, 65 forceNonCSS3Movement: false, 66 elementsWithInteraction: 'input', 67 revert: false, 68 revertAfter: 'stop', 69 revertIf: function(){ return true; }, 70 ignoreRightClick: true, 71 startPos: { 72 left: null, 73 top: null 74 }, 75 useBoundingClientRect: false 76 }; 77 78 // --------------------------------- 79 // ----- Our main Pep object ----- 80 // --------------------------------- 81 function Pep( el, options ) { 82 83 this.name = pluginName; 84 85 // reference to our DOM object 86 // and it's jQuery equivalent. 87 this.el = el; 88 this.$el = $(el); 89 90 // merge in defaults 91 this.options = $.extend( {}, defaults, options) ; 92 93 // store document/body so we don't need to keep grabbing them 94 // throughout the code 95 this.$document = $(this.$el[0].ownerDocument); 96 this.$body = this.$document.find('body'); 97 98 // Create our triggers based on touch/click device 99 this.moveTrigger = "MSPointerMove pointermove touchmove mousemove"; 100 this.startTrigger = "MSPointerDown pointerdown touchstart mousedown"; 101 this.stopTrigger = "MSPointerUp pointerup touchend mouseup"; 102 this.startTriggerArray = this.startTrigger.split(' '); 103 this.moveTriggerArray = this.moveTrigger.split(' '); 104 this.stopTriggerArray = this.stopTrigger.split(' '); 105 this.stopEvents = [ this.stopTrigger, this.options.stopEvents ].join(' '); 106 107 if ( this.options.constrainTo === 'window' ) 108 this.$container = this.$document; 109 else if ( this.options.constrainTo && (this.options.constrainTo !== 'parent') ) 110 this.$container = $(this.options.constrainTo); 111 else 112 this.$container = this.$el.parent(); 113 114 // IE need this 115 if ( this.isPointerEventCompatible() ) 116 this.applyMSDefaults(); 117 118 this.CSSEaseHash = this.getCSSEaseHash(); 119 this.scale = 1; 120 this.started = false;
121 this.disabled = false; 122 this.autoAxis = false; 123 this.activeDropRegions = []; 124 this.resetVelocityQueue(); 125 126 this.init(); 127 return this; 128 } 129 130 // init(); 131 // initialization logic 132 // you already have access to the DOM el and the options via the instance, 133 // e.g., this.el and this.options 134 Pep.prototype.init = function () { 135 136 if ( this.options.debug ) 137 this.buildDebugDiv(); 138 139 if ( this.options.disableSelect ) 140 this.disableSelect(); 141 142 // position the parent & place the object, if necessary. 143 if ( this.options.place && !this.options.deferPlacement ) { 144 this.positionParent(); 145 this.placeObject(); 146 } 147 148 this.ev = {}; // to store our event movements 149 this.pos = {}; // to store positions 150 this.subscribe(); 151 }; 152 153 // subscribe(); 154 // useful in the event we want to programmatically 155 // interact with our Pep object. 156 // e.g.: $('#pep').trigger('stop') 157 Pep.prototype.subscribe = function () { 158 var self = this; 159 160 // Subscribe to our start event 161 this.onStartEvent = function(ev){ self.handleStart(ev); }; 162 this.$el.on(this.startTrigger, this.onStartEvent); 163 164 // Add a flag to events that start on elements that should allow interaction 165 // so handleStart() can ignore them but allow them to bubble up through the DOM 166 this.onStartEventOnElementsWithInteraction = function(ev){ ev.ignorePropagation = true; }; 167 this.$el.on( 168 this.startTrigger, 169 this.options.elementsWithInteraction, 170 this.onStartEventOnElementsWithInteraction 171 ); 172 173 // Subscribe to our stop event 174 this.onStopEvents = function(ev) { self.handleStop(ev); }; 175 this.$document.on(this.stopEvents, this.onStopEvents); 176 177 // Subscribe to our move event 178 this.onMoveEvents = function(ev){ self.moveEvent = ev; }; 179 this.$document.on(this.moveTrigger, this.onMoveEvents); 180 }; 181 182 Pep.prototype.unsubscribe = function() { 183 this.$el.off(this.startTrigger, this.onStartEvent); 184 this.$el.off( 185 this.startTrigger, 186 this.options.elementsWithInteraction, 187 this.onStartEventOnElementsWithInteraction 188 ); 189 this.$document.off(this.stopEvents, this.onStopEvents); 190 this.$document.off(this.moveTrigger, this.onMoveEvents); 191 }; 192 193 // handleStart(); 194 // once this.startTrigger occurs, handle all of the logic 195 // that must go on. This is where Pep's heavy lifting is done. 196 Pep.prototype.handleStart = function(ev) { 197 198 // ignorePropagation is set to true if the event originates from an element 199 // listed in this.options.elementsWithInteraction 200 if (ev.ignorePropagation) return; 201 202 var self = this; 203 204 // only continue chugging if our start event is a valid move event. 205 if ( this.isValidMoveEvent(ev) && !this.disabled ){ 206 207 if( !(this.options.ignoreRightClick && ev.which === 3) ) { 208 209 // IE10 Hack. Me not happy. 210 if ( this.isPointerEventCompatible() && ev.preventManipulation ) 211 ev.preventManipulation(); 212 213 // normalize event 214 ev = this.normalizeEvent(ev); 215 216 // position the parent & place the object, if necessary. 217 if ( this.options.place && this.options.deferPlacement ) { 218 this.positionParent(); 219 this.placeObject(); 220 } 221 222 // log it 223 this.log({ type: 'event', event: ev.type }); 224 225 // hardware accelerate, if necessary. 226 if ( this.options.hardwareAccelerate && !this.hardwareAccelerated ) { 227 this.hardwareAccelerate(); 228 this.hardwareAccelerated = true; 229 } 230 231 // fire user's initiate event. 232 var shouldContinue = this.options.initiate.call(this, ev, this); 233 if ( shouldContinue === false ) 234 return; 235 236 237 // cancel the rest timeout 238 clearTimeout( this.restTimeout ); 239 240 // add active class and reset css animation, if necessary 241 this.$el.addClass( this.options.activeClass ); 242 this.removeCSSEasing(); 243 244 // store event's x & y values for later use 245 this.startX = this.ev.x = ev.pep.x; 246 this.startY = this.ev.y = ev.pep.y; 247 248 // store initial offset. 249 this.initialPosition = this.initialPosition || this.$el.position(); 250 251 // store the initial touch/click event, used to calculate the inital delta values. 252 this.startEvent = this.moveEvent = ev; 253 254 // make object active, so watchMoveLoop starts looping. 255 this.active = true; 256 257 // preventDefault(), is necessary 258 if ( this.options.shouldPreventDefault ) 259 ev.preventDefault(); 260 261 // allow / disallow event bubbling 262 if ( !this.options.allowDragEventPropagation ) 263 ev.stopPropagation(); 264 265 // animation loop to ensure we don't fire 266 // too many unneccessary repaints 267 (function watchMoveLoop(){ 268 if ( !self.active ) return; 269 self.handleMove(); 270 self.requestAnimationFrame( watchMoveLoop ); 271 })(); 272 273 (function watchEasingLoop(){ 274 if ( !self.options.easing ) return; 275 if ( self.easing ) self.options.easing.call(self, null, self); 276 self.requestAnimationFrame( watchEasingLoop ); 277 })(); 278 } 279 } 280 }; 281 282 // handleMove(); 283 // the logic for when the move events occur 284 Pep.prototype.handleMove = function() { 285 286 // setup our event object 287 if ( typeof(this.moveEvent) === 'undefined' ) 288 return; 289 290 // get our move event's x & y 291 var ev = this.normalizeEvent( this.moveEvent ); 292 var curX = window.parseInt(ev.pep.x / this.options.grid[0]) * this.options.grid[0]; 293 var curY = window.parseInt(ev.pep.y / this.options.grid[1]) * this.options.grid[1]; 294 295 // last in, first out (LIFO) queue to help us manage velocity 296 this.addToLIFO( { time: ev.timeStamp, x: curX, y: curY } ); 297 298 // calculate values necessary to moving 299 var dx, dy; 300 301 if ( $.inArray( ev.type, this.startTriggerArray ) > -1 ){ 302 dx = 0; 303 dy = 0; 304 } else{ 305 dx = curX - this.ev.x; 306 dy = curY - this.ev.y; 307 } 308 309 this.dx = dx; 310 this.dy = dy; 311 this.ev.x = curX; 312 this.ev.y = curY; 313 314 // no movement in either direction -- so return 315 if (dx === 0 && dy === 0){ 316 this.log({ type: 'event', event: '** stopped **' }); 317 return; 318 } 319 320 // check if object has moved past X/Y thresholds 321 // if so, fire users start event 322 var initialDx = Math.abs(this.startX - curX); 323 var initialDy = Math.abs(this.startY - curY); 324 if ( !this.started && ( initialDx > this.options.startThreshold[0] || initialDy > this.options.startThreshold[1] ) ){ 325 this.started = true; 326 this.$el.addClass(this.options.startClass); 327 this.options.start.call(this, this.startEvent, this); 328 } 329 330 // Move before calculate position and fire events 331 this.doMoveTo(dx, dy); 332 333 // Calculate our drop regions 334 if ( this.options.droppable ) { 335 this.calculateActiveDropRegions(); 336 } 337 338 // fire user's drag event. 339 var continueDrag = this.options.drag.call(this, ev, this); 340 341 if ( continueDrag === false ) { 342 this.resetVelocityQueue(); 343 return; 344 } 345 346 // log the move trigger & event position 347 this.log({ type: 'event', event: ev.type }); 348 this.log({ type: 'event-coords', x: this.ev.x, y: this.ev.y }); 349 this.log({ type: 'velocity' }); 350 }; 351 352 Pep.prototype.doMoveTo = function(dx, dy) { 353 var hash = this.handleConstraint(dx, dy); 354 var xOp, yOp; 355 356 // if using the "auto" axis, determine which axis to use based on 357 // whether the user has dragged more along the x or the y axis 358 if ( this.options.axis === 'auto' && !this.autoAxis) { 359 if ( Math.abs(dx) > Math.abs(dy) ) this.autoAxis = 'x'; 360 else 361 if ( Math.abs(dx) < Math.abs(dy) ) this.autoAxis = 'y'; 362 // don't move at all if the axis can't be determined 363 else { 364 dy = 0; 365 dx = 0; 366 } 367 } 368 369 // if using not using CSS transforms, move object via absolute position 370 if ( typeof this.options.moveTo === 'function') { 371 xOp = ( dx >= 0 ) ? "+=" + Math.abs(dx/this.scale)*this.options.multiplier : "-=" + Math.abs(dx/this.scale)*this.options.multiplier; 372 yOp = ( dy >= 0 ) ? "+=" + Math.abs(dy/this.scale)*this.options.multiplier : "-=" + Math.abs(dy/this.scale)*this.options.multiplier; 373 374 if ( this.options.constrainTo ) { 375 xOp = (hash.x !== false) ? hash.x : xOp; 376 yOp = (hash.y !== false) ? hash.y : yOp; 377 } 378 379 // only move along single axis, if necessary 380 if ( this.options.axis === 'x' || this.autoAxis === 'x' ) yOp = hash.y; 381 if ( this.options.axis === 'y' || this.autoAxis === 'y' ) xOp = hash.x; 382 383 this.options.moveTo.call(this, xOp, yOp); 384 } else if ( !this.shouldUseCSSTranslation() ){ 385 xOp = ( dx >= 0 ) ? "+=" + Math.abs(dx/this.scale)*this.options.multiplier : "-=" + Math.abs(dx/this.scale)*this.options.multiplier; 386 yOp = ( dy >= 0 ) ? "+=" + Math.abs(dy/this.scale)*this.options.multiplier : "-=" + Math.abs(dy/this.scale)*this.options.multiplier; 387 388 if ( this.options.constrainTo ) { 389 xOp = (hash.x !== false) ? hash.x : xOp; 390 yOp = (hash.y !== false) ? hash.y : yOp; 391 } 392 393 // only move along single axis, if necessary 394 if ( this.options.axis === 'x' || this.autoAxis === 'x' ) yOp = hash.y; 395 if ( this.options.axis === 'y' || this.autoAxis === 'y' ) xOp = hash.x; 396 397 this.moveTo(xOp, yOp); 398 } 399 else { 400 401 dx = (dx/this.scale)*this.options.multiplier; 402 dy = (dy/this.scale)*this.options.multiplier; 403 404 if ( this.options.constrainTo ) { 405 dx = (hash.x === false) ? dx : 0 ; 406 dy = (hash.y === false) ? dy : 0 ; 407 } 408 409 // only move along single axis, if necessary 410 if ( this.options.axis === 'x' || this.autoAxis === 'x' ) dy = 0; 411 if ( this.options.axis === 'y' || this.autoAxis === 'y' ) dx = 0; 412 413 this.moveToUsingTransforms( dx, dy ); 414 } 415 }; 416 417 // handleStop(); 418 // the logic for when the stop events occur 419 Pep.prototype.handleStop = function(ev) { 420 421 // no need to handle stop event if we're not active 422 if (!this.active) 423 return; 424 425 // log it 426 this.log({ type: 'event', event: ev.type }); 427 428 // make object inactive, so watchMoveLoop returns 429 this.active = false;
430 431 // make object easing. 432 this.easing = true; 433 434 // remove our start class 435 this.$el.removeClass(this.options.startClass) 436 .addClass(this.options.easeClass); 437 438 // Calculate our drop regions 439 if ( this.options.droppable ) { 440 this.calculateActiveDropRegions(); 441 } 442 443 // fire user's stop event. 444 if ( this.started || (!this.started && $.inArray('stop', this.options.callIfNotStarted) > -1 ) ) { 445 this.options.stop.call(this, ev, this); 446 } 447 448 // ease the object, if necessary. 449 if (this.options.shouldEase) { 450 this.ease(ev, this.started); 451 } else { 452 this.removeActiveClass(); 453 } 454 455 if ( this.options.revert && (this.options.revertAfter === 'stop' || !this.options.shouldEase) && ( this.options.revertIf && this.options.revertIf.call(this) ) ) { 456 this.revert(); 457 } 458 459 // this must be set to false after 460 // the user's stop event is called, so the dev 461 // has access to it. 462 this.started = false; 463 464 // reset the auto-axis 465 if ( this.autoAxis ) this.autoAxis = false; 466 467 // reset the velocity queue 468 this.resetVelocityQueue(); 469 470 }; 471 472 // ease(); 473 // used in conjunction with the LIFO queue 474 // to ease the object after stop 475 Pep.prototype.ease = function(ev, started){ 476 477 var pos = this.$el.position(); 478 var vel = this.velocity(); 479 var dt = this.dt; 480 var x = (vel.x/this.scale) * this.options.multiplier; 481 var y = (vel.y/this.scale) * this.options.multiplier; 482 483 var hash = this.handleConstraint(x, y, true); 484 485 // ⪠Apply the CSS3 animation easing magic ⪠486 if ( this.cssAnimationsSupported() ) 487 this.$el.css( this.getCSSEaseHash() ); 488 489 var xOp = ( vel.x >
489 0 ) ? "+=" + x : "-=" + Math.abs(x); 490 var yOp = ( vel.y > 0 ) ? "+=" + y : "-=" + Math.abs(y); 491 492 if ( this.options.constrainTo ) { 493 xOp = (hash.x !== false) ? hash.x : xOp; 494 yOp = (hash.y !== false) ? hash.y : yOp; 495 } 496 497 if ( this.options.axis === 'x' || this.autoAxis === 'x' ) yOp = "+=0"; 498 if ( this.options.axis === 'y' || this.autoAxis === 'y' ) xOp = "+=0"; 499 500 // ease it via JS, the last true tells it to animate. 501 var jsAnimateFallback = !this.cssAnimationsSupported() || this.options.forceNonCSS3Movement; 502 if (typeof this.options.moveTo === 'function') { 503 this.options.moveTo.call(this, xOp, yOp); 504 } else { 505 this.moveTo(xOp, yOp, jsAnimateFallback); 506 } 507 508 // when the rest occurs, remove active class and call 509 // user's rest event. 510 var self = this; 511 this.restTimeout = setTimeout( function(){ 512 513 // Calculate our drop regions 514 if ( self.options.droppable ) { 515 self.calculateActiveDropRegions(); 516 } 517 518 self.easing = false; 519 520 // call users rest event. 521 if ( started || ( !started && $.inArray('rest', self.options.callIfNotStarted) > -1 ) ) { 522 self.options.rest.call(self, ev, self); 523 } 524 525 // revert thy self! 526 if ( self.options.revert && (self.options.revertAfter === 'ease' && self.options.shouldEase) && ( self.options.revertIf && self.options.revertIf.call(self) ) ) { 527 self.revert(); 528 } 529 530 // remove active class 531 self.removeActiveClass(); 532 533 }, this.options.cssEaseDuration ); 534 535 }; 536 537 // normalizeEvent() 538 Pep.prototype.normalizeEvent = function(ev) { 539 ev.pep = {}; 540 541 if ( this.isTouch(ev) ) { 542 543 ev.pep.x = ev.originalEvent.touches[0].pageX; 544 ev.pep.y = ev.originalEvent.touches[0].pageY; 545 ev.pep.type = ev.type; 546 547 } 548 else if ( this.isPointerEventCompatible() || !this.isTouch(ev) ) { 549 550 if ( ev.pageX ) { 551 ev.pep.x = ev.pageX; 552 ev.pep.y = ev.pageY; 553 } else { 554 ev.pep.x = ev.originalEvent.pageX; 555 ev.pep.y = ev.originalEvent.pageY; 556 } 557 558 ev.pep.type = ev.type; 559 560 } 561 562 return ev; 563 }; 564 565 // resetVelocityQueue() 566 // 567 Pep.prototype.resetVelocityQueue = function() { 568 this.velocityQueue = new Array(5); 569 }; 570 571 // moveTo(); 572 // move the object to an x and/or y value 573 // using jQuery's .css function -- this fxn uses the 574 // .css({top: "+=20", left: "-=30"}) syntax 575 Pep.prototype.moveTo = function(x,y, animate) { 576 577 this.log({ type: 'delta', x: x, y: y }); 578 if ( animate ) { 579 this.$el.animate({ top: y, left: x }, 0, 'easeOutQuad', {queue: false}); 580 } else{ 581 this.$el.stop(true, false).css({ top: y , left: x }); 582 } 583 584 }; 585 586 // moveToUsingTransforms(); 587 // move the object to an x and/or y value 588 Pep.prototype.moveToUsingTransforms = function(x,y) { 589 590 // Check for our initial values if we don't have them. 591 var matrixArray = this.matrixToArray( this.matrixString() ); 592 if ( !this.cssX ) 593 this.cssX = this.xTranslation( matrixArray ); 594 595 if ( !this.cssY ) 596 this.cssY = this.yTranslation( matrixArray ); 597 598 // CSS3 transforms are additive from current position 599 this.cssX = this.cssX + x; 600 this.cssY = this.cssY + y; 601 602 this.log({ type: 'delta', x: x, y: y }); 603 604 matrixArray[4] = this.cssX; 605 matrixArray[5] = this.cssY; 606 607 this.translation = this.arrayToMatrix( matrixArray ); 608 this.transform( this.translation ); 609 }; 610 611 Pep.prototype.transform = function(value) { 612 this.$el.css({ 613 '-webkit-transform': value, 614 '-moz-transform': value, 615 '-ms-transform': value, 616 '-o-transform': value, 617 'transform': value }); 618 }; 619 620 Pep.prototype.xTranslation = function(matrixArray) { 621 matrixArray = matrixArray || this.matrixToArray( this.matrixString() ); 622 return parseInt(matrixArray[4], 10); 623 }; 624 625 Pep.prototype.yTranslation = function(matrixArray) { 626 matrixArray = matrixArray || this.matrixToArray( this.matrixString() ); 627 return parseInt(matrixArray[5], 10); 628 }; 629 630 631 // 3 helper functions for working with the 632 // objects CSS3 transforms 633 // matrixString 634 // matrixToArray 635 // arrayToMatrix 636 Pep.prototype.matrixString = function() { 637
638 var validMatrix = function(o){ 639 return !( !o || o === 'none' || o.indexOf('matrix') < 0 ); 640 }; 641 642 var matrix = "matrix(1, 0, 0, 1, 0, 0)"; 643 644 if ( validMatrix( this.$el.css('-webkit-transform') ) ) 645 matrix = this.$el.css('-webkit-transform'); 646 647 if ( validMatrix( this.$el.css('-moz-transform') ) ) 648 matrix = this.$el.css('-moz-transform'); 649 650 if ( validMatrix( this.$el.css('-ms-transform') ) ) 651 matrix = this.$el.css('-ms-transform'); 652 653 if ( validMatrix( this.$el.css('-o-transform') ) ) 654 matrix = this.$el.css('-o-transform'); 655 656 if ( validMatrix( this.$el.css('transform') ) ) 657 matrix = this.$el.css('transform'); 658 659 return matrix; 660 }; 661 662 Pep.prototype.matrixToArray = function(str) { 663 return str.split('(')[1].split(')')[0].split(','); 664 }; 665 666 Pep.prototype.arrayToMatrix = function(array) { 667 return "matrix(" + array.join(',') + ")"; 668 }; 669 670 // addToLIFO(); 671 // a Last-In/First-Out array of the 5 most recent 672 // velocity points, which is used for easing 673 Pep.prototype.addToLIFO = function(val){ 674 // last in, first out 675 var arr = this.velocityQueue; 676 arr = arr.slice(1, arr.length); 677 arr.push(val); 678 this.velocityQueue = arr; 679 }; 680 681 // velocity(); 682 // using the LIFO, calculate velocity and return 683 // velocity in each direction (x & y) 684 Pep.prototype.velocity = function(){ 685 var sumX = 0; 686 var sumY = 0; 687 688 for ( var i = 0; i < this.velocityQueue.length -1; i++ ){ 689 if ( this.velocityQueue[i] ){ 690 sumX += (this.velocityQueue[i+1].x - this.velocityQueue[i].x); 691 sumY += (this.velocityQueue[i+1].y - this.velocityQueue[i].y); 692 this.dt = ( this.velocityQueue[i+1].time - this.velocityQueue[i].time ); 693 } 694 } 695 696 // return velocity in each direction. 697 return { x: sumX*this.options.velocityMultiplier, y: sumY*this.options.velocityMultiplier}; 698 }; 699 700 Pep.prototype.revert = function() { 701 if ( this.shouldUseCSSTranslation() ){ 702 this.moveToUsingTransforms(-this.xTranslation(),-this.yTranslation()); 703 } 704 705 if (this.options.place) { 706 this.moveTo(this.initialPosition.left, this.initialPosition.top); 707 } 708 }; 709 710 // requestAnimationFrame(); 711 // requestAnimationFrame Polyfill 712 // More info: 713 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 714 Pep.prototype.requestAnimationFrame = function(callback) { 715 return window.requestAnimationFrame && window.requestAnimationFrame(callback) || 716 window.webkitRequestAnimationFrame && window.webkitRequestAnimationFrame(callback) || 717 window.mozRequestAnimationFrame && window.mozRequestAnimationFrame(callback) || 718 window.oRequestAnimationFrame && window.mozRequestAnimationFrame(callback) || 719 window.msRequestAnimationFrame && window.msRequestAnimationFrame(callback) || 720 window.setTimeout(callback, 1000 / 60); 721 }; 722 723 // positionParent(); 724 // add the right positioning to the parent object 725 Pep.prototype.positionParent = function() { 726 727 if ( !this.options.constrainTo || this.parentPositioned ) 728 return; 729 730 this.parentPositioned = true; 731 732 // make `relative` parent if necessary 733 if ( this.options.constrainTo === 'parent' ) { 734 this.$container.css({ position: 'relative' }); 735 } else if ( this.options.constrainTo === 'window' && 736 this.$container.get(0).nodeName !== "#document" && 737 this.$container.css('position') !== 'static' ) 738 { 739 this.$container.css({ position: 'static' }); 740 } 741 742 }; 743 744 // placeObject(); 745 // add the right positioning to the object 746 Pep.prototype.placeObject = function() { 747 748 if ( this.objectPlaced ) 749 return; 750 751 this.objectPlaced = true; 752 753 this.offset = (this.options.constrainTo === 'parent' || this.hasNonBodyRelative() ) ? 754 this.$el.position() : this.$el.offset(); 755 756 // better to leave absolute position alone if 757 // it already has one. 758 if ( parseInt( this.$el.css('left'), 10 ) ) 759 this.offset.left = this.$el.css('left'); 760 761 if (typeof this.options.startPos.left === "number") 762 this.offset.left = this.options.startPos.left; 763 764 if ( parseInt( this.$el.css('top'), 10 ) ) 765 this.offset.top = this.$el.css('top'); 766 767 if (typeof this.options.startPos.top === "number") 768 this.offset.top = this.options.startPos.top; 769 770 if ( this.options.removeMargins ) 771 this.$el.css({margin: 0}); 772 773 this.$el.css({ 774 position: 'absolute', 775 top: this.offset.top, 776 left: this.offset.left 777 }); 778 779 }; 780 781 // hasNonBodyRelative() 782 // returns true if any parent other than the body 783 // has relative positioning 784 Pep.prototype.hasNonBodyRelative = function() { 785 return this.$el.parents().filter(function() { 786 var $this = $(this); 787 return $this.is('body') || $this.css('position') === 'relative'; 788 }).length > 1; 789 }; 790 791 // setScale() 792 // set the scale of the object being moved. 793 Pep.prototype.setScale = function(val) { 794 this.scale = val; 795 }; 796 797 // setMultiplier() 798 // set the multiplier of the object being moved. 799 Pep.prototype.setMultiplier = function(val) { 800 this.options.multiplier = val; 801 }; 802 803 // removeCSSEasing(); 804 // remove CSS easing properties, if necessary 805 Pep.prototype.removeCSSEasing = function() { 806 if ( this.cssAnimationsSupported() ) 807 this.$el.css( this.getCSSEaseHash(true) ); 808 }; 809 810 // disableSelect(); 811 // add the property which causes the object 812 // to not be selected user drags over text areas 813 Pep.prototype.disableSelect = function() { 814 815 this.$el.css({ 816 '-webkit-touch-callout' : 'none',
817 '-webkit-user-select' : 'none', 818 '-khtml-user-select' : 'none', 819 '-moz-user-select' : 'none', 820 '-ms-user-select' : 'none', 821 'user-select' : 'none' 822 }); 823 824 }; 825 826 // removeActiveClass() 827 // Removes the active class. 828 Pep.prototype.removeActiveClass = function() { 829 this.$el.removeClass( [this.options.activeClass, this.options.easeClass].join(' ') ); 830 }; 831 832 // handleConstraint(); 833 // returns a hash of where to move to 834 // when we constrain to parent/window 835 Pep.prototype.handleConstraint = function(dx, dy, accountForTranslation) { 836 var pos = this.$el.position(); 837 this.pos.x = pos.left; 838 this.pos.y = pos.top; 839 840 var hash = { x: false, y: false }; 841 842 var upperYLimit, upperXLimit, lowerXLimit, lowerYLimit; 843 844 // log our positions 845 this.log({ type: "pos-coords", x: this.pos.x, y: this.pos.y}); 846 847 if ( $.isArray( this.options.constrainTo ) ) { 848 849 if ( this.options.constrainTo[3] !== undefined && this.options.constrainTo[1] !== undefined ) { 850 upperXLimit = this.options.constrainTo[1] === false ? Infinity : this.options.constrainTo[1]; 851 lowerXLimit = this.options.constrainTo[3] === false ? -Infinity : this.options.constrainTo[3]; 852 } 853 if ( this.options.constrainTo[0] !== false && this.options.constrainTo[2] !== false ) { 854 upperYLimit = this.options.constrainTo[2] === false ? Infinity : this.options.constrainTo[2]; 855 lowerYLimit = this.options.constrainTo[0] === false ? -Infinity : this.options.constrainTo[0]; 856 } 857 858 // is our object trying to move outside lower X & Y limits? 859 if ( this.pos.x + dx < lowerXLimit) hash.x = lowerXLimit; 860 if ( this.pos.y + dy < lowerYLimit) hash.y = lowerYLimit; 861 862 } else if ( typeof this.options.constrainTo === 'string' ) { 863 lowerXLimit = 0; 864 lowerYLimit = 0; 865 upperXLimit = this.$container.width() - (this.options.useBoundingClientRect ? this.$el[0].getBoundingClientRect().width : this.$el.outerWidth()); 866 upperYLimit = this.$container.height() - (this.options.useBoundingClientRect ? this.$el[0].getBoundingClientRect().height : this.$el.outerHeight()); 867 868 // is our object trying to move outside lower X & Y limits? 869 if ( this.pos.x + dx < 0 ) hash.x = 0; 870 if ( this.pos.y + dy < 0 ) hash.y = 0; 871 } 872 873 // is our object trying to move outside upper X & Y limits? 874 if ( this.pos.x + dx > upperXLimit ) hash.x = upperXLimit; 875 if ( this.pos.y + dy > upperYLimit ) hash.y = upperYLimit; 876 877 // Account for translation, which makes movement a little tricky. 878 if ( this.shouldUseCSSTranslation() && accountForTranslation ){ 879 if (hash.x === lowerXLimit && this.xTranslation() ) hash.x = lowerXLimit - this.xTranslation(); 880 if (hash.x === upperXLimit && this.xTranslation() ) hash.x = upperXLimit - this.xTranslation(); 881 882 if (hash.y === lowerYLimit && this.yTranslation() ) hash.y = lowerYLimit - this.yTranslation(); 883 if (hash.y === upperYLimit && this.yTranslation() ) hash.y = upperYLimit - this.yTranslation(); 884 } 885 886 return hash; 887 }; 888 889 // getCSSEaseHash(); 890 // returns a hash of params used in conjunction 891 // with this.options.cssEaseString 892 Pep.prototype.getCSSEaseHash = function(reset){ 893 if ( typeof(reset) === 'undefined' ) reset = false; 894 895 var cssEaseString; 896 if (reset){ 897 cssEaseString = ''; 898 } else if ( this.CSSEaseHash ) { 899 return this.CSSEaseHash; 900 } else { 901 cssEaseString = ['all', this.options.cssEaseDuration + 'ms', this.options.cssEaseString].join(' '); 902 } 903 904 return { 905 '-webkit-transition' : cssEaseString, // chrome, safari, etc. 906 '-moz-transition' : cssEaseString, // firefox 907 '-ms-transition' : cssEaseString, // microsoft 908 '-o-transition' : cssEaseString, // opera 909 'transition' : cssEaseString // future 910 }; 911 }; 912 913 // calculateActiveDropRegions() 914 // sets parent droppables of this. 915 Pep.prototype.calculateActiveDropRegions = function() { 916 var self = this; 917 this.activeDropRegions.length = 0; 918 919 $.each( $(this.options.droppable), function(idx, el){ 920 var $el = $(el); 921 if ( self.isOverlapping($el, self.$el) ){ 922 $el.addClass(self.options.droppableActiveClass); 923 self.activeDropRegions.push($el); 924 } else { 925 $el.removeClass(self.options.droppableActiveClass); 926 } 927 }); 928 929 }; 930 931 // isOverlapping(); 932 // returns true if element a over 933 Pep.prototype.isOverlapping = function($a,$b) { 934 935 if ( this.options.overlapFunction ) { 936 return this.options.overlapFunction($a,$b); 937 } 938 939 var rect1 = $a[0].getBoundingClientRect(); 940 var rect2 = $b[0].getBoundingClientRect(); 941 942 return !( rect1.right < rect2.left || 943 rect1.left > rect2.right || 944 rect1.bottom < rect2.top || 945 rect1.top > rect2.bottom ); 946 }; 947 948 // isTouch(); 949 // returns whether or not event is a touch event 950 Pep.prototype.isTouch = function(ev){ 951 return ev.type.search('touch') > -1; 952 }; 953 954 // isPointerEventCompatible(); 955 // return whether or note our device is pointer 956 // event compatible; typically means where on a 957 // touch Win8 device 958 Pep.prototype.isPointerEventCompatible = function() { 959 return ("MSPointerEvent" in window); 960 }; 961 962 // applyMSDefaults(); 963 Pep.prototype.applyMSDefaults = function(first_argument) { 964 this.$el.css({
965 '-ms-touch-action' : 'none', 966 'touch-action' : 'none', 967 '-ms-scroll-chaining': 'none', 968 '-ms-scroll-limit': '0 0 0 0' 969 }); 970 }; 971 972 // isValidMoveEvent(); 973 // returns true if we're on a non-touch device -- or -- 974 // if the event is **single** touch event on a touch device 975 Pep.prototype.isValidMoveEvent = function(ev){ 976 return ( !this.isTouch(ev) || ( this.isTouch(ev) && ev.originalEvent && ev.originalEvent.touches && ev.originalEvent.touches.length === 1 ) ); 977 }; 978 979 // shouldUseCSSTranslation(); 980 // return true if we should use CSS transforms for move the object 981 Pep.prototype.shouldUseCSSTranslation = function() { 982 983 if ( this.options.forceNonCSS3Movement ) 984 return false; 985 986 if ( typeof(this.useCSSTranslation) !== "undefined" ) 987 return this.useCSSTranslation; 988 989 var useCSSTranslation = false; 990 991 if ( !this.options.useCSSTranslation || ( typeof(Modernizr) !== "undefined" && !Modernizr.csstransforms)){ 992 useCSSTranslation = false; 993 } 994 else{ 995 useCSSTranslation = true; 996 } 997 998 this.useCSSTranslation = useCSSTranslation; 999 return useCSSTranslation; 1000 }; 1001 1002 // cssAnimationsSupported(): 1003 // returns true if the browser supports CSS animations 1004 // which are used for easing.. 1005 Pep.prototype.cssAnimationsSupported = function() { 1006 1007 if ( typeof(this.cssAnimationsSupport) !== "undefined" ){ 1008 return this.cssAnimationsSupport; 1009 } 1010 1011 // If the page has Modernizr, let them do the heavy lifting. 1012 if ( ( typeof(Modernizr) !== "undefined" && Modernizr.cssanimations) ){ 1013 this.cssAnimationsSupport = true; 1014 return true; 1015 } 1016 1017 var animation = false, 1018 elm = document.createElement('div'), 1019 animationstring = 'animation', 1020 keyframeprefix = '', 1021 domPrefixes = 'Webkit Moz O ms Khtml'.split(' '), 1022 pfx = ''; 1023 1024 if( elm.style.animationName ) { animation = true; } 1025 1026 if( animation === false ) { 1027 for( var i = 0; i < domPrefixes.length; i++ ) { 1028 if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { 1029 pfx = domPrefixes[ i ]; 1030 animationstring = pfx + 'Animation'; 1031 keyframeprefix = '-' + pfx.toLowerCase() + '-'; 1032 animation = true; 1033 break; 1034 } 1035 } 1036 } 1037 1038 this.cssAnimationsSupport = animation; 1039 return animation; 1040 }; 1041 1042 // hardwareAccelerate(); 1043 // add fool-proof CSS3 hardware acceleration. 1044 Pep.prototype.hardwareAccelerate = function() { 1045 this.$el.css({ 1046 '-webkit-perspective': 1000, 1047 'perspective': 1000, 1048 '-webkit-backface-visibility': 'hidden', 1049 'backface-visibility': 'hidden' 1050 }); 1051 }; 1052 1053 // getMovementValues(); 1054 // returns object pos, event position, and velocity in each direction. 1055 Pep.prototype.getMovementValues = function() { 1056 return { ev: this.ev, pos: this.pos, velocity: this.velocity() }; 1057 }; 1058 1059 // buildDebugDiv(); 1060 // Create a little div in the lower right corner of the window 1061 // for extra info about the object currently moving 1062 Pep.prototype.buildDebugDiv = function() { 1063 1064 // Build the debugDiv and it's inner HTML -- if necessary 1065 var $debugDiv; 1066 if ( $('#pep-debug').length === 0 ){ 1067 $debugDiv = $('<div></div>'); 1068 $debugDiv 1069 .attr('id', 'pep-debug') 1070 .append("<div style='font-weight:bold; background: red; color: white;'>DEBUG MODE</div>") 1071 .append("<div id='pep-debug-event'>no event</div>") 1072 .append("<div id='pep-debug-ev-coords'>event coords: <span class='pep-x'>-</span>, <span class='pep-y'>-</span></div>") 1073 .append("<div id='pep-debug-pos-coords'>position coords: <span class='pep-x'>-</span>, <span class='pep-y'>-</span></div>") 1074 .append("<div id='pep-debug-velocity'>velocity: <span class='pep-x'>-</span>, <span class='pep-y'>-</span></div>") 1075 .append("<div id='pep-debug-delta'>Δ movement: <span class='pep-x'>-</span>, <span class='pep-y'>-</span></div>") 1076 .css({ 1077 position: 'fixed', 1078 bottom: 5, 1079 right: 5, 1080 zIndex: 99999, 1081 textAlign: 'right', 1082 fontFamily: 'Arial, sans', 1083 fontSize: 10, 1084 border: '1px solid #DDD', 1085 padding: '3px', 1086 background: 'white', 1087 color: '#333' 1088 }); 1089 } 1090 1091 var self = this; 1092 setTimeout(function(){ 1093 self.debugElements = { 1094 $event: $("#pep-debug-event"), 1095 $velocityX: $("#pep-debug-velocity .pep-x"), 1096 $velocityY: $("#pep-debug-velocity .pep-y"), 1097 $dX: $("#pep-debug-delta .pep-x"), 1098 $dY: $("#pep-debug-delta .pep-y"), 1099 $evCoordsX: $("#pep-debug-ev-coords .pep-x"), 1100 $evCoordsY: $("#pep-debug-ev-coords .pep-y"), 1101 $posCoordsX: $("#pep-debug-pos-coords .pep-x"), 1102 $posCoordsY: $("#pep-debug-pos-coords .pep-y") 1103 }; 1104 }, 0); 1105 1106 $('body').append( $debugDiv ); 1107 }; 1108 1109 // log() 1110 Pep.prototype.log = function(opts) { 1111 if ( !this.options.debug ) return; 1112 1113 switch (opts.type){ 1114 case "event": 1115 this.debugElements.$event.text(opts.event); 1116 break; 1117 case "pos-coords": 1118 this.debugElements.$posCoordsX.text(opts.x); 1119 this.debugElements.$posCoordsY.text(opts.y); 1120 break; 1121 case "event-coords": 1122 this.debugElements.$evCoordsX.text(opts.x); 1123 this.debugElements.$evCoordsY.text(opts.y); 1124 break; 1125 case "delta": 1126 this.debugElements.$dX.text(opts.x); 1127 this.debugElements.$dY.text(opts.y); 1128 break; 1129 case "velocity": 1130 var vel = this.velocity(); 1131 this.debugElements.$velocityX.text( Math.round(vel.x) ); 1132 this.debugElements.$velocityY.text( Math.round(vel.y) ); 1133 break; 1134 } 1135 }; 1136
1137 // toggle() 1138 // toggle the pep object 1139 Pep.prototype.toggle = function(on) { 1140 if ( typeof(on) === "undefined"){ 1141 this.disabled = !this.disabled; 1142 } 1143 else { 1144 this.disabled = !on; 1145 } 1146 }; 1147 1148 // *** Special Easings functions *** 1149 // Used for JS easing fallback 1150 // We can use any of these for a 1151 // good intertia ease 1152 $.extend($.easing, 1153 { 1154 easeOutQuad: function (x, t, b, c, d) { 1155 return -c *(t/=d)*(t-2) + b; 1156 }, 1157 easeOutCirc: function (x, t, b, c, d) { 1158 return c * Math.sqrt(1 - (t=t/d-1)*t) + b; 1159 }, 1160 easeOutExpo: function (x, t, b, c, d) { 1161 return (t===d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 1162 } 1163 }); 1164 1165 // wrap it 1166 // A really lightweight plugin wrapper around the constructor, 1167 // preventing against multiple instantiations. 1168 $.fn[pluginName] = function ( options ) { 1169 return this.each(function () { 1170 if (!$.data(this, 'plugin_' + pluginName)) { 1171 var pepObj = new Pep( this, options ); 1172 $.data(this, 'plugin_' + pluginName, pepObj); 1173 $.pep.peps.push(pepObj); 1174 } 1175 }); 1176 }; 1177 1178 // The _ ___ ___ 1179 // /_\ | _ \_ _| 1180 // / _ \| _/| | 1181 // /_/ \_\_| |___| 1182 // 1183 $.pep = {}; 1184 $.pep.peps = []; 1185 $.pep.toggleAll = function(on){ 1186 $.each(this.peps, function(index, pepObj){ 1187 pepObj.toggle(on); 1188 }); 1189 }; 1190 1191 $.pep.unbind = function($obj){ 1192 $.each($obj, function(index, pepObj) { 1193 pepObj = $(pepObj); 1194 var pep = pepObj.data('plugin_' + pluginName); 1195 1196 if ( typeof pep === 'undefined' ) 1197 return; 1198 1199 pep.toggle(false); 1200 pep.unsubscribe(); 1201 pepObj.removeData('plugin_' + pluginName); 1202 }); 1203 }; 1204 1205}(jQuery, window));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.