Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
save_bvnghean.vn
/
wp-content
/
plugins
/
super-forms
/
assets
/
js
/
backend
:
jquery-pep.js
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
// jshint ignore: start /* * ________ ________ * ______(_)_____ ____ __________________ __ _____________________ ______(_)_______ * _____ /_ __ `/ / / / _ \_ ___/_ / / / ___ __ \ _ \__ __ \ _____ /__ ___/ * ____ / / /_/ // /_/ // __/ / _ /_/ /____ /_/ / __/_ /_/ /______ / _(__ ) * ___ / \__, / \__,_/ \___//_/ _\__, /_(_) .___/\___/_ .___/_(_)__ / /____/ * /___/ /_/ /____/ /_/ /_/ /___/ * * http://pep.briangonzalez.org * Kinetic drag for mobile/desktop. * * Copyright (c) 2014 Brian Gonzalez * Licensed under the MIT license. * * Title generated using "Speed" @ * http://patorjk.com/software/taag/#p=display&f=Speed&t=jquery.pep.js */ ;(function ( $, window, undefined ) { "use strict"; // create the defaults once var pluginName = 'pep'; var defaults = { // Options // ---------------------------------------------------------------------------------------------- // See ** https://github.com/briangonzalez/jquery.pep.js ** for fully documented options. // It was too hard to manage options here and in the readme. // ---------------------------------------------------------------------------------------------- initiate: function(){}, start: function(){}, drag: function(){}, stop: function(){}, easing: null, rest: function(){}, moveTo: false, callIfNotStarted: ['stop', 'rest'], startThreshold: [0,0], grid: [1,1], debug: false, activeClass: 'pep-active', multiplier: 1, velocityMultiplier: 2.5, shouldPreventDefault: true, allowDragEventPropagation: true, stopEvents: '', hardwareAccelerate: true, useCSSTranslation: true, disableSelect: true, cssEaseString: "cubic-bezier(0.190, 1.000, 0.220, 1.000)", cssEaseDuration: 1000, shouldEase: true, droppable: false, droppableActiveClass: 'pep-dpa', overlapFunction: false, constrainTo: false, removeMargins: true, place: true, deferPlacement: false, axis: null, forceNonCSS3Movement: false, elementsWithInteraction: 'input', revert: false, revertAfter: 'stop', revertIf: function(){ return true; }, ignoreRightClick: true, startPos: { left: null, top: null } }; // --------------------------------- // ----- Our main Pep object ----- // --------------------------------- function Pep( el, options ) { this.name = pluginName; // reference to our DOM object // and it's jQuery equivalent. this.el = el; this.$el = $(el); // merge in defaults this.options = $.extend( {}, defaults, options) ; // store document/body so we don't need to keep grabbing them // throughout the code this.$document = $(this.$el[0].ownerDocument); this.$body = this.$document.find('body'); // Create our triggers based on touch/click device this.moveTrigger = "MSPointerMove pointermove touchmove mousemove"; this.startTrigger = "MSPointerDown pointerdown touchstart mousedown"; this.stopTrigger = "MSPointerUp pointerup touchend mouseup"; this.startTriggerArray = this.startTrigger.split(' '); this.moveTriggerArray = this.moveTrigger.split(' '); this.stopTriggerArray = this.stopTrigger.split(' '); this.stopEvents = [ this.stopTrigger, this.options.stopEvents ].join(' '); if ( this.options.constrainTo === 'window' ) this.$container = this.$document; else if ( this.options.constrainTo && (this.options.constrainTo !== 'parent') ) this.$container = $(this.options.constrainTo); else this.$container = this.$el.parent(); // IE need this if ( this.isPointerEventCompatible() ) this.applyMSDefaults(); this.CSSEaseHash = this.getCSSEaseHash(); this.scale = 1; this.started = false; this.disabled = false; this.activeDropRegions = []; this.resetVelocityQueue(); this.init(); return this; } // init(); // initialization logic // you already have access to the DOM el and the options via the instance, // e.g., this.el and this.options Pep.prototype.init = function () { if ( this.options.debug ) this.buildDebugDiv(); if ( this.options.disableSelect ) this.disableSelect(); // position the parent & place the object, if necessary. if ( this.options.place && !this.options.deferPlacement ) { this.positionParent(); this.placeObject(); } this.ev = {}; // to store our event movements this.pos = {}; // to store positions this.subscribe(); }; // subscribe(); // useful in the event we want to programmatically // interact with our Pep object. // e.g.: $('#pep').trigger('stop') Pep.prototype.subscribe = function () { var self = this; // Subscribe to our start event this.onStartEvent = function(ev){ self.handleStart(ev); }; this.$el.on(this.startTrigger, this.onStartEvent); // Prevent start events from being gobbled by elements that should allow user interaction this.onStartEventOnElementsWithInteraction = function(ev){ ev.stopPropagation(); }; this.$el.on( this.startTrigger, this.options.elementsWithInteraction, this.onStartEventOnElementsWithInteraction ); // Subscribe to our stop event this.onStopEvents = function(ev) { self.handleStop(ev); }; this.$document.on(this.stopEvents, this.onStopEvents); // Subscribe to our move event this.onMoveEvents = function(ev){ self.moveEvent = ev; }; this.$document.on(this.moveTrigger, this.onMoveEvents); }; Pep.prototype.unsubscribe = function() { this.$el.off(this.startTrigger, this.onStartEvent); this.$el.off( this.startTrigger, this.options.elementsWithInteraction, this.onStartEventOnElementsWithInteraction ); this.$document.off(this.stopEvents, this.onStopEvents); this.$document.off(this.moveTrigger, this.onMoveEvents); }; // handleStart(); // once this.startTrigger occurs, handle all of the logic // that must go on. This is where Pep's heavy lifting is done. Pep.prototype.handleStart = function(ev) { var self = this; // only continue chugging if our start event is a valid move event. if ( this.isValidMoveEvent(ev) && !this.disabled ){ if( !(this.options.ignoreRightClick && ev.which === 3) ) { // IE10 Hack. Me not happy. if ( this.isPointerEventCompatible() && ev.preventManipulation ) ev.preventManipulation(); // normalize event ev = this.normalizeEvent(ev); // position the parent & place the object, if necessary. if ( this.options.place && this.options.deferPlacement ) { this.positionParent(); this.placeObject(); } // log it this.log({ type: 'event', event: ev.type }); // hardware accelerate, if necessary. if ( this.options.hardwareAccelerate && !this.hardwareAccelerated ) { this.hardwareAccelerate(); this.hardwareAccelerated = true; } // fire user's initiate event. var shouldContinue = this.options.initiate.call(this, ev, this); if ( shouldContinue === false ) return; // cancel the rest timeout clearTimeout( this.restTimeout ); // add active class and reset css animation, if necessary this.$el.addClass( this.options.activeClass ); this.removeCSSEasing(); // store event's x & y values for later use this.startX = this.ev.x = ev.pep.x; this.startY = this.ev.y = ev.pep.y; // store initial offset. this.initialPosition = this.initialPosition || this.$el.position(); // store the initial touch/click event, used to calculate the inital delta values. this.startEvent = this.moveEvent = ev; // make object active, so watchMoveLoop starts looping. this.active = true; // preventDefault(), is necessary if ( this.options.shouldPreventDefault ) ev.preventDefault(); // allow / disallow event bubbling if ( !this.options.allowDragEventPropagation ) ev.stopPropagation(); // animation loop to ensure we don't fire // too many unneccessary repaints (function watchMoveLoop(){ if ( !self.active ) return; self.handleMove(); self.requestAnimationFrame( watchMoveLoop ); })(); (function watchEasingLoop(){ if ( !self.options.easing ) return; if ( self.easing ) self.options.easing.call(self, null, self); self.requestAnimationFrame( watchEasingLoop ); })(); } } }; // handleMove(); // the logic for when the move events occur Pep.prototype.handleMove = function() { // setup our event object if ( typeof(this.moveEvent) === 'undefined' ) return; // get our move event's x & y var ev = this.normalizeEvent( this.moveEvent ); var curX = window.parseInt(ev.pep.x / this.options.grid[0], 10) * this.options.grid[0]; var curY = window.parseInt(ev.pep.y / this.options.grid[1], 10) * this.options.grid[1]; // last in, first out (LIFO) queue to help us manage velocity this.addToLIFO( { time: ev.timeStamp, x: curX, y: curY } ); // calculate values necessary to moving var dx, dy; if ( $.inArray( ev.type, this.startTriggerArray ) > -1 ){ dx = 0; dy = 0; } else{ dx = curX - this.ev.x; dy = curY - this.ev.y; } this.dx = dx; this.dy = dy; this.ev.x = curX; this.ev.y = curY; // no movement in either direction -- so return if (dx === 0 && dy === 0){ this.log({ type: 'event', event: '** stopped **' }); return; } // check if object has moved past X/Y thresholds // if so, fire users start event var initialDx = Math.abs(this.startX - curX); var initialDy = Math.abs(this.startY - curY); if ( !this.started && ( initialDx > this.options.startThreshold[0] || initialDy > this.options.startThreshold[1] ) ){ this.started = true; this.$el.addClass('pep-start'); this.options.start.call(this, this.startEvent, this); } // Move before calculate position and fire events this.doMoveTo(dx, dy); // Calculate our drop regions if ( this.options.droppable ) { this.calculateActiveDropRegions(); } // fire user's drag event. var continueDrag = this.options.drag.call(this, ev, this); if ( continueDrag === false ) { this.resetVelocityQueue(); return; } // log the move trigger & event position this.log({ type: 'event', event: ev.type }); this.log({ type: 'event-coords', x: this.ev.x, y: this.ev.y }); this.log({ type: 'velocity' }); }; Pep.prototype.doMoveTo = function(dx, dy) { var hash = this.handleConstraint(dx, dy); var xOp, yOp; // if using not using CSS transforms, move object via absolute position if ( typeof this.options.moveTo === 'function') { xOp = ( dx >= 0 ) ? "+=" + Math.abs(dx/this.scale)*this.options.multiplier : "-=" + Math.abs(dx/this.scale)*this.options.multiplier; yOp = ( dy >= 0 ) ? "+=" + Math.abs(dy/this.scale)*this.options.multiplier : "-=" + Math.abs(dy/this.scale)*this.options.multiplier; if ( this.options.constrainTo ) { xOp = (hash.x !== false) ? hash.x : xOp; yOp = (hash.y !== false) ? hash.y : yOp; } // only move along single axis, if necessary if ( this.options.axis === 'x' ) yOp = hash.y; if ( this.options.axis === 'y' ) xOp = hash.x; this.options.moveTo.call(this, xOp, yOp); } else if ( !this.shouldUseCSSTranslation() ){ xOp = ( dx >= 0 ) ? "+=" + Math.abs(dx/this.scale)*this.options.multiplier : "-=" + Math.abs(dx/this.scale)*this.options.multiplier; yOp = ( dy >= 0 ) ? "+=" + Math.abs(dy/this.scale)*this.options.multiplier : "-=" + Math.abs(dy/this.scale)*this.options.multiplier; if ( this.options.constrainTo ) { xOp = (hash.x !== false) ? hash.x : xOp; yOp = (hash.y !== false) ? hash.y : yOp; } // only move along single axis, if necessary if ( this.options.axis === 'x' ) yOp = hash.y; if ( this.options.axis === 'y' ) xOp = hash.x; this.moveTo(xOp, yOp); } else { dx = (dx/this.scale)*this.options.multiplier; dy = (dy/this.scale)*this.options.multiplier; if ( this.options.constrainTo ) { dx = (hash.x === false) ? dx : 0 ; dy = (hash.y === false) ? dy : 0 ; } // only move along single axis, if necessary if ( this.options.axis === 'x' ) dy = 0; if ( this.options.axis === 'y' ) dx = 0; this.moveToUsingTransforms( dx, dy ); } }; // handleStop(); // the logic for when the stop events occur Pep.prototype.handleStop = function(ev) { // no need to handle stop event if we're not active if (!this.active) return; // log it this.log({ type: 'event', event: ev.type }); // make object inactive, so watchMoveLoop returns this.active = false; // make object easing. this.easing = true; // remove our start class this.$el.removeClass('pep-start') .addClass('pep-ease'); // Calculate our drop regions if ( this.options.droppable ) { this.calculateActiveDropRegions(); } // fire user's stop event. if ( this.started || (!this.started && $.inArray('stop', this.options.callIfNotStarted) > -1 ) ) { this.options.stop.call(this, ev, this); } // ease the object, if necessary. if (this.options.shouldEase) { this.ease(ev, this.started); } else { this.removeActiveClass(); } if ( this.options.revert && (this.options.revertAfter === 'stop' || !this.options.shouldEase) && ( this.options.revertIf && this.options.revertIf.call(this) ) ) { this.revert(); } // this must be set to false after // the user's stop event is called, so the dev // has access to it. this.started = false; // reset the velocity queue this.resetVelocityQueue(); }; // ease(); // used in conjunction with the LIFO queue // to ease the object after stop Pep.prototype.ease = function(ev, started){ var vel = this.velocity(); var x = (vel.x/this.scale) * this.options.multiplier; var y = (vel.y/this.scale) * this.options.multiplier; var hash = this.handleConstraint(x, y, true); // ✪ Apply the CSS3 animation easing magic ✪ if ( this.cssAnimationsSupported() ) this.$el.css( this.getCSSEaseHash() ); var xOp = ( vel.x > 0 ) ? "+=" + x : "-=" + Math.abs(x); var yOp = ( vel.y > 0 ) ? "+=" + y : "-=" + Math.abs(y); if ( this.options.constrainTo ) { xOp = (hash.x !== false) ? hash.x : xOp; yOp = (hash.y !== false) ? hash.y : yOp; } if ( this.options.axis === 'x' ) yOp = "+=0"; if ( this.options.axis === 'y' ) xOp = "+=0"; // ease it via JS, the last true tells it to animate. var jsAnimateFallback = !this.cssAnimationsSupported() || this.options.forceNonCSS3Movement; if (typeof this.options.moveTo === 'function') { this.options.moveTo.call(this, xOp, yOp); } else { this.moveTo(xOp, yOp, jsAnimateFallback); } // when the rest occurs, remove active class and call // user's rest event. var self = this; this.restTimeout = setTimeout( function(){ // Calculate our drop regions if ( self.options.droppable ) { self.calculateActiveDropRegions(); } self.easing = false; // call users rest event. if ( started || ( !started && $.inArray('rest', self.options.callIfNotStarted) > -1 ) ) { self.options.rest.call(self, ev, self); } // revert thy self! if ( self.options.revert && (self.options.revertAfter === 'ease' && self.options.shouldEase) && ( self.options.revertIf && self.options.revertIf.call(self) ) ) { self.revert(); } // remove active class self.removeActiveClass(); }, this.options.cssEaseDuration ); }; // normalizeEvent() Pep.prototype.normalizeEvent = function(ev) { ev.pep = {}; if ( this.isTouch(ev) ) { ev.pep.x = ev.originalEvent.touches[0].pageX; ev.pep.y = ev.originalEvent.touches[0].pageY; ev.pep.type = ev.type; } else if ( this.isPointerEventCompatible() || !this.isTouch(ev) ) { if ( ev.pageX ) { ev.pep.x = ev.pageX; ev.pep.y = ev.pageY; } else { ev.pep.x = ev.originalEvent.pageX; ev.pep.y = ev.originalEvent.pageY; } ev.pep.type = ev.type; } return ev; }; // resetVelocityQueue() // Pep.prototype.resetVelocityQueue = function() { this.velocityQueue = new Array(5); }; // moveTo(); // move the object to an x and/or y value // using jQuery's .css function -- this fxn uses the // .css({top: "+=20", left: "-=30"}) syntax Pep.prototype.moveTo = function(x,y, animate) { this.log({ type: 'delta', x: x, y: y }); if ( animate ) { this.$el.animate({ top: y, left: x }, 0, 'easeOutQuad', {queue: false}); } else{ this.$el.stop(true, false).css({ top: y , left: x }); } }; // moveToUsingTransforms(); // move the object to an x and/or y value Pep.prototype.moveToUsingTransforms = function(x,y) { // Check for our initial values if we don't have them. var matrixArray = this.matrixToArray( this.matrixString() ); if ( !this.cssX ) this.cssX = this.xTranslation( matrixArray ); if ( !this.cssY ) this.cssY = this.yTranslation( matrixArray ); // CSS3 transforms are additive from current position this.cssX = this.cssX + x; this.cssY = this.cssY + y; this.log({ type: 'delta', x: x, y: y }); matrixArray[4] = this.cssX; matrixArray[5] = this.cssY; this.translation = this.arrayToMatrix( matrixArray ); this.transform( this.translation ); }; Pep.prototype.transform = function(value) { this.$el.css({ '-webkit-transform': value, '-moz-transform': value, '-ms-transform': value, '-o-transform': value, 'transform': value }); }; Pep.prototype.xTranslation = function(matrixArray) { matrixArray = matrixArray || this.matrixToArray( this.matrixString() ); return parseInt(matrixArray[4], 10); }; Pep.prototype.yTranslation = function(matrixArray) { matrixArray = matrixArray || this.matrixToArray( this.matrixString() ); return parseInt(matrixArray[5], 10); }; // 3 helper functions for working with the // objects CSS3 transforms // matrixString // matrixToArray // arrayToMatrix Pep.prototype.matrixString = function() { var validMatrix = function(o){ return !( !o || o === 'none' || o.indexOf('matrix') < 0 ); }; var matrix = "matrix(1, 0, 0, 1, 0, 0)"; if ( validMatrix( this.$el.css('-webkit-transform') ) ) matrix = this.$el.css('-webkit-transform'); if ( validMatrix( this.$el.css('-moz-transform') ) ) matrix = this.$el.css('-moz-transform'); if ( validMatrix( this.$el.css('-ms-transform') ) ) matrix = this.$el.css('-ms-transform'); if ( validMatrix( this.$el.css('-o-transform') ) ) matrix = this.$el.css('-o-transform'); if ( validMatrix( this.$el.css('transform') ) ) matrix = this.$el.css('transform'); return matrix; }; Pep.prototype.matrixToArray = function(str) { return str.split('(')[1].split(')')[0].split(','); }; Pep.prototype.arrayToMatrix = function(array) { return "matrix(" + array.join(',') + ")"; }; // addToLIFO(); // a Last-In/First-Out array of the 5 most recent // velocity points, which is used for easing Pep.prototype.addToLIFO = function(val){ // last in, first out var arr = this.velocityQueue; arr = arr.slice(1, arr.length); arr.push(val); this.velocityQueue = arr; }; // velocity(); // using the LIFO, calculate velocity and return // velocity in each direction (x & y) Pep.prototype.velocity = function(){ var sumX = 0; var sumY = 0; for ( var i = 0; i < this.velocityQueue.length -1; i++ ){ if ( this.velocityQueue[i] ){ sumX += (this.velocityQueue[i+1].x - this.velocityQueue[i].x); sumY += (this.velocityQueue[i+1].y - this.velocityQueue[i].y); this.dt = ( this.velocityQueue[i+1].time - this.velocityQueue[i].time ); } } // return velocity in each direction. return { x: sumX*this.options.velocityMultiplier, y: sumY*this.options.velocityMultiplier}; }; Pep.prototype.revert = function() { if ( this.shouldUseCSSTranslation() ){ this.moveToUsingTransforms(-this.xTranslation(),-this.yTranslation()); } this.moveTo(this.initialPosition.right, this.initialPosition.bottom); }; // requestAnimationFrame(); // requestAnimationFrame Polyfill // More info: // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ Pep.prototype.requestAnimationFrame = function(callback) { return window.requestAnimationFrame && window.requestAnimationFrame(callback) || window.webkitRequestAnimationFrame && window.webkitRequestAnimationFrame(callback) || window.mozRequestAnimationFrame && window.mozRequestAnimationFrame(callback) || window.oRequestAnimationFrame && window.mozRequestAnimationFrame(callback) || window.msRequestAnimationFrame && window.msRequestAnimationFrame(callback) || window.setTimeout(callback, 1000 / 60); }; // positionParent(); // add the right positioning to the parent object Pep.prototype.positionParent = function() { if ( !this.options.constrainTo || this.parentPositioned ) return; this.parentPositioned = true; // make `relative` parent if necessary if ( this.options.constrainTo === 'parent' ) { this.$container.css({ position: 'relative' }); } else if ( this.options.constrainTo === 'window' && this.$container.get(0).nodeName !== "#document" && this.$container.css('position') !== 'static' ) { this.$container.css({ position: 'static' }); } }; // placeObject(); // add the right positioning to the object Pep.prototype.placeObject = function() { if ( this.objectPlaced ) return; this.objectPlaced = true; this.offset = (this.options.constrainTo === 'parent' || this.hasNonBodyRelative() ) ? this.$el.position() : this.$el.offset(); // better to leave absolute position alone if // it already has one. if ( parseInt( this.$el.css('left'), 10 ) ) this.offset.left = this.$el.css('left'); if (typeof this.options.startPos.left === "number") this.offset.left = this.options.startPos.left; if ( parseInt( this.$el.css('top'), 10 ) ) this.offset.top = this.$el.css('top'); if (typeof this.options.startPos.top === "number") this.offset.top = this.options.startPos.top; if ( this.options.removeMargins ) this.$el.css({margin: 0}); this.$el.css({ position: 'absolute', top: this.offset.top, left: this.offset.left }); }; // hasNonBodyRelative() // returns true if any parent other than the body // has relative positioning Pep.prototype.hasNonBodyRelative = function() { return this.$el.parents().filter(function() { var $this = $(this); return $this.is('body') || $this.css('position') === 'relative'; }).length > 1; }; // setScale() // set the scale of the object being moved. Pep.prototype.setScale = function(val) { this.scale = val; }; // setMultiplier() // set the multiplier of the object being moved. Pep.prototype.setMultiplier = function(val) { this.options.multiplier = val; }; // removeCSSEasing(); // remove CSS easing properties, if necessary Pep.prototype.removeCSSEasing = function() { if ( this.cssAnimationsSupported() ) this.$el.css( this.getCSSEaseHash(true) ); }; // disableSelect(); // add the property which causes the object // to not be selected user drags over text areas Pep.prototype.disableSelect = function() { this.$el.css({ '-webkit-touch-callout' : 'none', '-webkit-user-select' : 'none', '-khtml-user-select' : 'none', '-moz-user-select' : 'none', '-ms-user-select' : 'none', 'user-select' : 'none' }); }; // removeActiveClass() // Removes the active class. Pep.prototype.removeActiveClass = function() { this.$el.removeClass( [this.options.activeClass, 'pep-ease'].join(' ') ); }; // handleConstraint(); // returns a hash of where to move to // when we constrain to parent/window Pep.prototype.handleConstraint = function(dx, dy, accountForTranslation) { var pos = this.$el.position(); this.pos.x = pos.left; this.pos.y = pos.top; var hash = { x: false, y: false }; var upperYLimit, upperXLimit, lowerXLimit, lowerYLimit; // log our positions this.log({ type: "pos-coords", x: this.pos.x, y: this.pos.y}); if ( $.isArray( this.options.constrainTo ) ) { if ( this.options.constrainTo[3] !== undefined && this.options.constrainTo[1] !== undefined ) { upperXLimit = this.options.constrainTo[1] === false ? Infinity : this.options.constrainTo[1]; lowerXLimit = this.options.constrainTo[3] === false ? -Infinity : this.options.constrainTo[3]; } if ( this.options.constrainTo[0] !== false && this.options.constrainTo[2] !== false ) { upperYLimit = this.options.constrainTo[2] === false ? Infinity : this.options.constrainTo[2]; lowerYLimit = this.options.constrainTo[0] === false ? -Infinity : this.options.constrainTo[0]; } // is our object trying to move outside lower X & Y limits? if ( this.pos.x + dx < lowerXLimit) hash.x = lowerXLimit; if ( this.pos.y + dy < lowerYLimit) hash.y = lowerYLimit; } else if ( typeof this.options.constrainTo === 'string' ) { lowerXLimit = 0; lowerYLimit = 0; upperXLimit = this.$container.width() - this.$el.outerWidth(); upperYLimit = this.$container.height() - this.$el.outerHeight(); // is our object trying to move outside lower X & Y limits? if ( this.pos.x + dx < 0 ) hash.x = 0; if ( this.pos.y + dy < 0 ) hash.y = 0; } // is our object trying to move outside upper X & Y limits? if ( this.pos.x + dx > upperXLimit ) hash.x = upperXLimit; if ( this.pos.y + dy > upperYLimit ) hash.y = upperYLimit; // Account for translation, which makes movement a little tricky. if ( this.shouldUseCSSTranslation() && accountForTranslation ){ if (hash.x === lowerXLimit && this.xTranslation() ) hash.x = lowerXLimit - this.xTranslation(); if (hash.x === upperXLimit && this.xTranslation() ) hash.x = upperXLimit - this.xTranslation(); if (hash.y === lowerYLimit && this.yTranslation() ) hash.y = lowerYLimit - this.yTranslation(); if (hash.y === upperYLimit && this.yTranslation() ) hash.y = upperYLimit - this.yTranslation(); } return hash; }; // getCSSEaseHash(); // returns a hash of params used in conjunction // with this.options.cssEaseString Pep.prototype.getCSSEaseHash = function(reset){ if ( typeof(reset) === 'undefined' ) reset = false; var cssEaseString; if (reset){ cssEaseString = ''; } else if ( this.CSSEaseHash ) { return this.CSSEaseHash; } else { cssEaseString = ['all', this.options.cssEaseDuration + 'ms', this.options.cssEaseString].join(' '); } return { '-webkit-transition' : cssEaseString, // chrome, safari, etc. '-moz-transition' : cssEaseString, // firefox '-ms-transition' : cssEaseString, // microsoft '-o-transition' : cssEaseString, // opera 'transition' : cssEaseString // future }; }; // calculateActiveDropRegions() // sets parent droppables of this. Pep.prototype.calculateActiveDropRegions = function() { var self = this; this.activeDropRegions.length = 0; $.each( $(this.options.droppable), function(idx, el){ var $el = $(el); if ( self.isOverlapping($el, self.$el) ){ $el.addClass(self.options.droppableActiveClass); self.activeDropRegions.push($el); } else { $el.removeClass(self.options.droppableActiveClass); } }); }; // isOverlapping(); // returns true if element a over Pep.prototype.isOverlapping = function($a,$b) { if ( this.options.overlapFunction ) { return this.options.overlapFunction($a,$b); } var rect1 = $a[0].getBoundingClientRect(); var rect2 = $b[0].getBoundingClientRect(); return !( rect1.right < rect2.left || rect1.left > rect2.right || rect1.bottom < rect2.top || rect1.top > rect2.bottom ); }; // isTouch(); // returns whether or not event is a touch event Pep.prototype.isTouch = function(ev){ return ev.type.search('touch') > -1; }; // isPointerEventCompatible(); // return whether or note our device is pointer // event compatible; typically means where on a // touch Win8 device Pep.prototype.isPointerEventCompatible = function() { return ("MSPointerEvent" in window); }; // applyMSDefaults(); Pep.prototype.applyMSDefaults = function() { this.$el.css({ '-ms-touch-action' : 'none', 'touch-action' : 'none', '-ms-scroll-chaining': 'none', '-ms-scroll-limit': '0 0 0 0' }); }; // isValidMoveEvent(); // returns true if we're on a non-touch device -- or -- // if the event is **single** touch event on a touch device Pep.prototype.isValidMoveEvent = function(ev){ return ( !this.isTouch(ev) || ( this.isTouch(ev) && ev.originalEvent && ev.originalEvent.touches && ev.originalEvent.touches.length === 1 ) ); }; // shouldUseCSSTranslation(); // return true if we should use CSS transforms for move the object Pep.prototype.shouldUseCSSTranslation = function() { if ( this.options.forceNonCSS3Movement ) return false; if ( typeof(this.useCSSTranslation) !== "undefined" ) return this.useCSSTranslation; var useCSSTranslation = false; if ( !this.options.useCSSTranslation || ( typeof(Modernizr) !== "undefined" && !Modernizr.csstransforms)){ useCSSTranslation = false; } else{ useCSSTranslation = true; } this.useCSSTranslation = useCSSTranslation; return useCSSTranslation; }; // cssAnimationsSupported(): // returns true if the browser supports CSS animations // which are used for easing.. Pep.prototype.cssAnimationsSupported = function() { if ( typeof(this.cssAnimationsSupport) !== "undefined" ){ return this.cssAnimationsSupport; } // If the page has Modernizr, let them do the heavy lifting. if ( ( typeof(Modernizr) !== "undefined" && Modernizr.cssanimations) ){ this.cssAnimationsSupport = true; return true; } var animation = false, elm = document.createElement('div'), animationstring = 'animation', keyframeprefix = '', domPrefixes = 'Webkit Moz O ms Khtml'.split(' '), pfx = ''; if( elm.style.animationName ) { animation = true; } if( animation === false ) { for( var i = 0; i < domPrefixes.length; i++ ) { if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { pfx = domPrefixes[ i ]; animationstring = pfx + 'Animation'; keyframeprefix = '-' + pfx.toLowerCase() + '-'; animation = true; break; } } } this.cssAnimationsSupport = animation; return animation; }; // hardwareAccelerate(); // add fool-proof CSS3 hardware acceleration. Pep.prototype.hardwareAccelerate = function() { this.$el.css({ '-webkit-perspective': 1000, 'perspective': 1000, '-webkit-backface-visibility': 'hidden', 'backface-visibility': 'hidden' }); }; // getMovementValues(); // returns object pos, event position, and velocity in each direction. Pep.prototype.getMovementValues = function() { return { ev: this.ev, pos: this.pos, velocity: this.velocity() }; }; // buildDebugDiv(); // Create a little div in the lower right corner of the window // for extra info about the object currently moving Pep.prototype.buildDebugDiv = function() { // Build the debugDiv and it's inner HTML -- if necessary var $debugDiv; if ( $('#pep-debug').length === 0 ){ $debugDiv = $('<div></div>'); $debugDiv .attr('id', 'pep-debug') .append("<div style='font-weight:bold; background: red; color: white;'>DEBUG MODE</div>") .append("<div id='pep-debug-event'>no event</div>") .append("<div id='pep-debug-ev-coords'>event coords: <span class='pep-x'>-</span>, <span class='pep-y'>-</span></div>") .append("<div id='pep-debug-pos-coords'>position coords: <span class='pep-x'>-</span>, <span class='pep-y'>-</span></div>") .append("<div id='pep-debug-velocity'>velocity: <span class='pep-x'>-</span>, <span class='pep-y'>-</span></div>") .append("<div id='pep-debug-delta'>Δ movement: <span class='pep-x'>-</span>, <span class='pep-y'>-</span></div>") .css({ position: 'fixed', bottom: 5, right: 5, zIndex: 99999, textAlign: 'right', fontFamily: 'Arial, sans', fontSize: 10, border: '1px solid #DDD', padding: '3px', background: 'white', color: '#333' }); } var self = this; setTimeout(function(){ self.debugElements = { $event: $("#pep-debug-event"), $velocityX: $("#pep-debug-velocity .pep-x"), $velocityY: $("#pep-debug-velocity .pep-y"), $dX: $("#pep-debug-delta .pep-x"), $dY: $("#pep-debug-delta .pep-y"), $evCoordsX: $("#pep-debug-ev-coords .pep-x"), $evCoordsY: $("#pep-debug-ev-coords .pep-y"), $posCoordsX: $("#pep-debug-pos-coords .pep-x"), $posCoordsY: $("#pep-debug-pos-coords .pep-y") }; }, 0); $('body').append( $debugDiv ); }; // log() Pep.prototype.log = function(opts) { if ( !this.options.debug ) return; switch (opts.type){ case "event": this.debugElements.$event.text(opts.event); break; case "pos-coords": this.debugElements.$posCoordsX.text(opts.x); this.debugElements.$posCoordsY.text(opts.y); break; case "event-coords": this.debugElements.$evCoordsX.text(opts.x); this.debugElements.$evCoordsY.text(opts.y); break; case "delta": this.debugElements.$dX.text(opts.x); this.debugElements.$dY.text(opts.y); break; case "velocity": var vel = this.velocity(); this.debugElements.$velocityX.text( Math.round(vel.x) ); this.debugElements.$velocityY.text( Math.round(vel.y) ); break; } }; // toggle() // toggle the pep object Pep.prototype.toggle = function(on) { if ( typeof(on) === "undefined"){ this.disabled = !this.disabled; } else { this.disabled = !on; } }; // *** Special Easings functions *** // Used for JS easing fallback // We can use any of these for a // good intertia ease $.extend($.easing, { easeOutQuad: function (x, t, b, c, d) { return -c *(t/=d)*(t-2) + b; }, easeOutCirc: function (x, t, b, c, d) { return c * Math.sqrt(1 - (t=t/d-1)*t) + b; }, easeOutExpo: function (x, t, b, c, d) { return (t===d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; } }); // wrap it // A really lightweight plugin wrapper around the constructor, // preventing against multiple instantiations. $.fn[pluginName] = function ( options ) { return this.each(function () { if (!$.data(this, 'plugin_' + pluginName)) { var pepObj = new Pep( this, options ); $.data(this, 'plugin_' + pluginName, pepObj); $.pep.peps.push(pepObj); } }); }; // The _ ___ ___ // /_\ | _ \_ _| // / _ \| _/| | // /_/ \_\_| |___| // $.pep = {}; $.pep.peps = []; $.pep.toggleAll = function(on){ $.each(this.peps, function(index, pepObj){ pepObj.toggle(on); }); }; $.pep.unbind = function($obj){ var pep = $obj.data('plugin_' + pluginName); if ( typeof pep === 'undefined' ) return; pep.toggle(false); pep.unsubscribe(); $obj.removeData('plugin_' + pluginName); }; }(jQuery, window));