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