Commit 18dabc33 by Andy Armstrong

Upgrade draggabilly.js to fix Firefox iframe bug

TNL-3627
parent dd5a6acf
...@@ -109,13 +109,18 @@ ...@@ -109,13 +109,18 @@
// The parameter "predef" should remain empty for this configuration file // The parameter "predef" should remain empty for this configuration file
// to remain as general as possible. // to remain as general as possible.
"predef": [ "predef": [
// jQuery library. // jQuery globals
"jQuery", "$", "jQuery", "$",
// Underscore.js library. // Underscore.js globals
"_", "_",
// Jasmine library. // RequireJS globals
"define",
"require",
"RequireJS",
// Jasmine globals
"jasmine", "jasmine",
"describe", "xdescribe", "describe", "xdescribe",
"it", "xit", "it", "xit",
...@@ -126,12 +131,15 @@ ...@@ -126,12 +131,15 @@
"waitsFor", "waitsFor",
"runs", "runs",
// jQuery-Jasmine library. // jQuery-Jasmine globals
"loadFixtures", "loadFixtures",
"appendLoadFixtures", "appendLoadFixtures",
"readFixtures", "readFixtures",
"setFixtures", "setFixtures",
"appendSetFixtures", "appendSetFixtures",
"spyOnEvent" "spyOnEvent",
// Miscellaneous globals
"JSON"
] ]
} }
...@@ -17,6 +17,7 @@ require.config({ ...@@ -17,6 +17,7 @@ require.config({
paths: { paths: {
"domReady": "js/vendor/domReady", "domReady": "js/vendor/domReady",
"gettext": "/i18n", "gettext": "/i18n",
"json2": "js/vendor/json2",
"mustache": "js/vendor/mustache", "mustache": "js/vendor/mustache",
"codemirror": "js/vendor/codemirror-compressed", "codemirror": "js/vendor/codemirror-compressed",
"codemirror/stex": "js/vendor/CodeMirror/stex", "codemirror/stex": "js/vendor/CodeMirror/stex",
...@@ -95,6 +96,9 @@ require.config({ ...@@ -95,6 +96,9 @@ require.config({
] ]
}, },
shim: { shim: {
"json2": {
exports: "JSON"
},
"gettext": { "gettext": {
exports: "gettext" exports: "gettext"
}, },
......
...@@ -23,6 +23,7 @@ requirejs.config({ ...@@ -23,6 +23,7 @@ requirejs.config({
"jquery.simulate": "xmodule_js/common_static/js/vendor/jquery.simulate", "jquery.simulate": "xmodule_js/common_static/js/vendor/jquery.simulate",
"datepair": "xmodule_js/common_static/js/vendor/timepicker/datepair", "datepair": "xmodule_js/common_static/js/vendor/timepicker/datepair",
"date": "xmodule_js/common_static/js/vendor/date", "date": "xmodule_js/common_static/js/vendor/date",
"json2": "xmodule_js/common_static/js/vendor/json2",
"moment": "xmodule_js/common_static/js/vendor/moment.min", "moment": "xmodule_js/common_static/js/vendor/moment.min",
"moment-with-locales": "xmodule_js/common_static/js/vendor/moment-with-locales.min", "moment-with-locales": "xmodule_js/common_static/js/vendor/moment-with-locales.min",
"text": "xmodule_js/common_static/js/vendor/requirejs/text", "text": "xmodule_js/common_static/js/vendor/requirejs/text",
...@@ -58,6 +59,9 @@ requirejs.config({ ...@@ -58,6 +59,9 @@ requirejs.config({
"js/spec/test_utils": "js/spec/test_utils", "js/spec/test_utils": "js/spec/test_utils",
} }
shim: { shim: {
"json2": {
exports: "JSON"
},
"gettext": { "gettext": {
exports: "gettext" exports: "gettext"
}, },
......
define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/views/feedback_notification", "draggabilly", define(["jquery", "jquery.ui", "underscore", "json2", "gettext", "draggabilly",
"js/utils/module"], "js/utils/module", "common/js/components/views/feedback_notification"],
function ($, ui, _, gettext, NotificationView, Draggabilly, ModuleUtils) { function ($, ui, _, JSON, gettext, Draggabilly, ModuleUtils, NotificationView) {
'use strict';
var contentDragger = { var contentDragger = {
droppableClasses: 'drop-target drop-target-prepend drop-target-before drop-target-after', droppableClasses: 'drop-target drop-target-prepend drop-target-before drop-target-after',
...@@ -17,14 +18,15 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -17,14 +18,15 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
var eleY = ele.offset().top; var eleY = ele.offset().top;
var eleYEnd = eleY + ele.outerHeight(); var eleYEnd = eleY + ele.outerHeight();
var containers = $(ele.data('droppable-class')); var containers = $(ele.data('droppable-class'));
var isSibling = function () {
return $(this).data('locator') !== undefined && !$(this).is(ele);
};
for (var i = 0; i < containers.length; i++) { for (var i = 0; i < containers.length; i++) {
var container = $(containers[i]); var container = $(containers[i]);
// Exclude the 'new unit' buttons, and make sure we don't // Exclude the 'new unit' buttons, and make sure we don't
// prepend an element to itself // prepend an element to itself
var siblings = container.children().filter(function () { var siblings = container.children().filter(isSibling);
return $(this).data('locator') !== undefined && !$(this).is(ele);
});
// If the container is collapsed, check to see if the // If the container is collapsed, check to see if the
// element is on top of its parent list -- don't check the // element is on top of its parent list -- don't check the
// position of the container // position of the container
...@@ -101,7 +103,8 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -101,7 +103,8 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
} }
else { else {
// Dragging up into end of list. // Dragging up into end of list.
if (j === siblings.length - 1 && yChange < 0 && Math.abs(eleY - siblingYEnd) <= fudge) { if (j === siblings.length - 1 && yChange < 0 &&
Math.abs(eleY - siblingYEnd) <= fudge) {
return { return {
ele: $sibling, ele: $sibling,
attachMethod: 'after' attachMethod: 'after'
...@@ -145,8 +148,8 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -145,8 +148,8 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
// Information about the current drag. // Information about the current drag.
dragState: {}, dragState: {},
onDragStart: function (draggie, event, pointer) { onDragStart: function (draggable) {
var ele = $(draggie.element); var ele = $(draggable.element);
this.dragState = { this.dragState = {
// Which element will be dropped into/onto on success // Which element will be dropped into/onto on success
dropDestination: null, dropDestination: null,
...@@ -162,7 +165,9 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -162,7 +165,9 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
if (!ele.hasClass(this.collapsedClass)) { if (!ele.hasClass(this.collapsedClass)) {
ele.addClass(this.collapsedClass); ele.addClass(this.collapsedClass);
ele.find('.expand-collapse').first().addClass('expand').removeClass('collapse'); ele.find('.expand-collapse').first().addClass('expand').removeClass('collapse');
// onDragStart gets called again after the collapse, so we can't just store a variable in the dragState.
// onDragStart gets called again after the collapse, so we can't
// just store a variable in the dragState.
ele.addClass(this.expandOnDropClass); ele.addClass(this.expandOnDropClass);
} }
...@@ -171,7 +176,7 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -171,7 +176,7 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
ele.removeClass('was-dragging'); ele.removeClass('was-dragging');
}, },
onDragMove: function (draggie, event, pointer) { onDragMove: function (draggable, event, pointer) {
// Handle scrolling of the browser. // Handle scrolling of the browser.
var scrollAmount = 0; var scrollAmount = 0;
var dragBuffer = 10; var dragBuffer = 10;
...@@ -186,13 +191,13 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -186,13 +191,13 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
return; return;
} }
var yChange = draggie.dragPoint.y - this.dragState.lastY; var yChange = draggable.dragPoint.y - this.dragState.lastY;
if (yChange !== 0) { if (yChange !== 0) {
this.dragState.direction = yChange; this.dragState.direction = yChange;
} }
this.dragState.lastY = draggie.dragPoint.y; this.dragState.lastY = draggable.dragPoint.y;
var ele = $(draggie.element); var ele = $(draggable.element);
var destinationInfo = this.findDestination(ele, this.dragState.direction); var destinationInfo = this.findDestination(ele, this.dragState.direction);
var destinationEle = destinationInfo.ele; var destinationEle = destinationInfo.ele;
this.dragState.parentList = destinationInfo.parentList; this.dragState.parentList = destinationInfo.parentList;
...@@ -215,8 +220,8 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -215,8 +220,8 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
} }
}, },
onDragEnd: function (draggie, event, pointer) { onDragEnd: function (draggable, event, pointer) {
var ele = $(draggie.element); var ele = $(draggable.element);
var destination = this.dragState.dropDestination; var destination = this.dragState.dropDestination;
// Clear dragging state in preparation for the next event. // Clear dragging state in preparation for the next event.
...@@ -284,7 +289,7 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -284,7 +289,7 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
// If drop was into a collapsed parent, the parent will have been // If drop was into a collapsed parent, the parent will have been
// expanded. Views using this class may need to track the // expanded. Views using this class may need to track the
// collapse/expand state, so send it with the refresh callback. // collapse/expand state, so send it with the refresh callback.
var collapsed = element.hasClass(this.collapsedClass); var collapsed = element.hasClass(contentDragger.collapsedClass);
if (_.isFunction(refresh)) { refresh(collapsed); } if (_.isFunction(refresh)) { refresh(collapsed); }
}; };
...@@ -375,9 +380,9 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi ...@@ -375,9 +380,9 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "common/js/components/vi
handle: options.handleClass, handle: options.handleClass,
containment: '.wrapper-dnd' containment: '.wrapper-dnd'
}); });
draggable.on('dragStart', _.bind(contentDragger.onDragStart, contentDragger)); draggable.on('dragStart', _.bind(contentDragger.onDragStart, contentDragger, draggable));
draggable.on('dragMove', _.bind(contentDragger.onDragMove, contentDragger)); draggable.on('dragMove', _.bind(contentDragger.onDragMove, contentDragger, draggable));
draggable.on('dragEnd', _.bind(contentDragger.onDragEnd, contentDragger)); draggable.on('dragEnd', _.bind(contentDragger.onDragEnd, contentDragger, draggable));
} }
} }
}; };
......
...@@ -59,6 +59,7 @@ lib_paths: ...@@ -59,6 +59,7 @@ lib_paths:
- xmodule_js/common_static/js/vendor/draggabilly.pkgd.js - xmodule_js/common_static/js/vendor/draggabilly.pkgd.js
- xmodule_js/common_static/js/vendor/date.js - xmodule_js/common_static/js/vendor/date.js
- xmodule_js/common_static/js/vendor/domReady.js - xmodule_js/common_static/js/vendor/domReady.js
- xmodule_js/common_static/js/vendor/json2.js
- xmodule_js/common_static/js/vendor/URI.min.js - xmodule_js/common_static/js/vendor/URI.min.js
- xmodule_js/common_static/js/vendor/jquery.smooth-scroll.min.js - xmodule_js/common_static/js/vendor/jquery.smooth-scroll.min.js
- xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js - xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
......
/*! /*!
* Draggabilly PACKAGED v1.0.5 * Draggabilly PACKAGED v1.2.4
* Make that shiz draggable * Make that shiz draggable
* http://draggabilly.desandro.com * http://draggabilly.desandro.com
* MIT license
*/
/**
* Bridget makes jQuery widgets
* v1.1.0
* MIT license
*/
( function( window ) {
// -------------------------- utils -------------------------- //
var slice = Array.prototype.slice;
function noop() {}
// -------------------------- definition -------------------------- //
function defineBridget( $ ) {
// bail if no jQuery
if ( !$ ) {
return;
}
// -------------------------- addOptionMethod -------------------------- //
/**
* adds option method -> $().plugin('option', {...})
* @param {Function} PluginClass - constructor class
*/
function addOptionMethod( PluginClass ) {
// don't overwrite original option method
if ( PluginClass.prototype.option ) {
return;
}
// option setter
PluginClass.prototype.option = function( opts ) {
// bail out if not an object
if ( !$.isPlainObject( opts ) ){
return;
}
this.options = $.extend( true, this.options, opts );
};
}
// -------------------------- plugin bridge -------------------------- //
// helper function for logging errors
// $.error breaks jQuery chaining
var logError = typeof console === 'undefined' ? noop :
function( message ) {
console.error( message );
};
/**
* jQuery plugin bridge, access methods like $elem.plugin('method')
* @param {String} namespace - plugin name
* @param {Function} PluginClass - constructor class
*/
function bridge( namespace, PluginClass ) {
// add to jQuery fn namespace
$.fn[ namespace ] = function( options ) {
if ( typeof options === 'string' ) {
// call plugin method when first argument is a string
// get arguments for method
var args = slice.call( arguments, 1 );
for ( var i=0, len = this.length; i < len; i++ ) {
var elem = this[i];
var instance = $.data( elem, namespace );
if ( !instance ) {
logError( "cannot call methods on " + namespace + " prior to initialization; " +
"attempted to call '" + options + "'" );
continue;
}
if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) {
logError( "no such method '" + options + "' for " + namespace + " instance" );
continue;
}
// trigger method with arguments
var returnValue = instance[ options ].apply( instance, args );
// break look and return first value if provided
if ( returnValue !== undefined ) {
return returnValue;
}
}
// return this if no return value
return this;
} else {
return this.each( function() {
var instance = $.data( this, namespace );
if ( instance ) {
// apply options & init
instance.option( options );
instance._init();
} else {
// initialize new instance
instance = new PluginClass( this, options );
$.data( this, namespace, instance );
}
});
}
};
}
// -------------------------- bridget -------------------------- //
/**
* converts a Prototypical class into a proper jQuery plugin
* the class must have a ._init method
* @param {String} namespace - plugin name, used in $().pluginName
* @param {Function} PluginClass - constructor class
*/ */
$.bridget = function( namespace, PluginClass ) {
addOptionMethod( PluginClass );
bridge( namespace, PluginClass );
};
return $.bridget;
}
// transport
if ( typeof define === 'function' && define.amd ) {
// AMD
define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget );
} else if ( typeof exports === 'object' ) {
defineBridget( require('jquery') );
} else {
// get jquery from browser global
defineBridget( window.jQuery );
}
})( window );
/*! /*!
* classie - class helper functions * classie v1.0.1
* class helper functions
* from bonzo https://github.com/ded/bonzo * from bonzo https://github.com/ded/bonzo
* MIT license
* *
* classie.has( elem, 'my-class' ) -> true/false * classie.has( elem, 'my-class' ) -> true/false
* classie.add( elem, 'my-new-class' ) * classie.add( elem, 'my-new-class' )
...@@ -14,24 +157,24 @@ ...@@ -14,24 +157,24 @@
* classie.toggle( elem, 'my-class' ) * classie.toggle( elem, 'my-class' )
*/ */
/*jshint browser: true, strict: true, undef: true */ /*jshint browser: true, strict: true, undef: true, unused: true */
/*global define: false */ /*global define: false, module: false */
( function( window ) { ( function( window ) {
'use strict';
// class helper functions from bonzo https://github.com/ded/bonzo // class helper functions from bonzo https://github.com/ded/bonzo
function classReg( className ) { function classReg( className ) {
return new RegExp("(^|\\s+)" + className + "(\\s+|$)"); return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
} }
// classList support for class management // classList support for class management
// altho to be fair, the api sucks because it won't accept multiple classes at once // altho to be fair, the api sucks because it won't accept multiple classes at once
var hasClass, addClass, removeClass; var hasClass, addClass, removeClass;
if ( 'classList' in document.documentElement ) { if ( 'classList' in document.documentElement ) {
hasClass = function( elem, c ) { hasClass = function( elem, c ) {
return elem.classList.contains( c ); return elem.classList.contains( c );
}; };
...@@ -41,8 +184,8 @@ ...@@ -41,8 +184,8 @@
removeClass = function( elem, c ) { removeClass = function( elem, c ) {
elem.classList.remove( c ); elem.classList.remove( c );
}; };
} }
else { else {
hasClass = function( elem, c ) { hasClass = function( elem, c ) {
return classReg( c ).test( elem.className ); return classReg( c ).test( elem.className );
}; };
...@@ -54,14 +197,14 @@ ...@@ -54,14 +197,14 @@
removeClass = function( elem, c ) { removeClass = function( elem, c ) {
elem.className = elem.className.replace( classReg( c ), ' ' ); elem.className = elem.className.replace( classReg( c ), ' ' );
}; };
} }
function toggleClass( elem, c ) { function toggleClass( elem, c ) {
var fn = hasClass( elem, c ) ? removeClass : addClass; var fn = hasClass( elem, c ) ? removeClass : addClass;
fn( elem, c ); fn( elem, c );
} }
var classie = { var classie = {
// full names // full names
hasClass: hasClass, hasClass: hasClass,
addClass: addClass, addClass: addClass,
...@@ -72,199 +215,517 @@ ...@@ -72,199 +215,517 @@
add: addClass, add: addClass,
remove: removeClass, remove: removeClass,
toggle: toggleClass toggle: toggleClass
}; };
// transport // transport
if ( typeof define === 'function' && define.amd ) { if ( typeof define === 'function' && define.amd ) {
// AMD // AMD
define("classie", classie); define( 'classie/classie',classie );
} else { } else if ( typeof exports === 'object' ) {
// CommonJS
module.exports = classie;
} else {
// browser global // browser global
window.classie = classie; window.classie = classie;
} }
})( window ); })( window );
/*! /*!
* eventie v1.0.3 * getStyleProperty v1.0.4
* event binding helper * original by kangax
* eventie.bind( elem, 'click', myFn ) * http://perfectionkills.com/feature-testing-css-properties/
* eventie.unbind( elem, 'click', myFn ) * MIT license
*/ */
/*jshint browser: true, undef: true, unused: true */ /*jshint browser: true, strict: true, undef: true */
/*global define: false */ /*global define: false, exports: false, module: false */
( function( window ) { ( function( window ) {
'use strict';
var docElem = document.documentElement;
var bind = function() {}; var prefixes = 'Webkit Moz ms Ms O'.split(' ');
var docElemStyle = document.documentElement.style;
if ( docElem.addEventListener ) { function getStyleProperty( propName ) {
bind = function( obj, type, fn ) { if ( !propName ) {
obj.addEventListener( type, fn, false ); return;
};
} else if ( docElem.attachEvent ) {
bind = function( obj, type, fn ) {
obj[ type + fn ] = fn.handleEvent ?
function() {
var event = window.event;
// add event.target
event.target = event.target || event.srcElement;
fn.handleEvent.call( fn, event );
} :
function() {
var event = window.event;
// add event.target
event.target = event.target || event.srcElement;
fn.call( obj, event );
};
obj.attachEvent( "on" + type, obj[ type + fn ] );
};
} }
var unbind = function() {}; // test standard property first
if ( typeof docElemStyle[ propName ] === 'string' ) {
return propName;
}
if ( docElem.removeEventListener ) { // capitalize
unbind = function( obj, type, fn ) { propName = propName.charAt(0).toUpperCase() + propName.slice(1);
obj.removeEventListener( type, fn, false );
}; // test vendor specific properties
} else if ( docElem.detachEvent ) { var prefixed;
unbind = function( obj, type, fn ) { for ( var i=0, len = prefixes.length; i < len; i++ ) {
obj.detachEvent( "on" + type, obj[ type + fn ] ); prefixed = prefixes[i] + propName;
try { if ( typeof docElemStyle[ prefixed ] === 'string' ) {
delete obj[ type + fn ]; return prefixed;
} catch ( err ) {
// can't delete window object properties
obj[ type + fn ] = undefined;
} }
};
} }
}
var eventie = {
bind: bind,
unbind: unbind
};
// transport // transport
if ( typeof define === 'function' && define.amd ) { if ( typeof define === 'function' && define.amd ) {
// AMD // AMD
define("eventie", eventie); define( 'get-style-property/get-style-property',[],function() {
} else { return getStyleProperty;
});
} else if ( typeof exports === 'object' ) {
// CommonJS for Component
module.exports = getStyleProperty;
} else {
// browser global // browser global
window.eventie = eventie; window.getStyleProperty = getStyleProperty;
} }
})( this ); })( window );
/*! /*!
* EventEmitter v4.2.4 - git.io/ee * getSize v1.2.2
* Oliver Caldwell * measure size of elements
* MIT license * MIT license
* @preserve
*/ */
(function () { /*jshint browser: true, strict: true, undef: true, unused: true */
'use strict'; /*global define: false, exports: false, require: false, module: false, console: false */
/** ( function( window, undefined ) {
* Class for managing events.
* Can be extended to provide event functionality in other classes.
*
* @class EventEmitter Manages event registering and emitting.
*/
function EventEmitter() {}
// Shortcuts to improve speed and size
// Easy access to the prototype
var proto = EventEmitter.prototype;
/** // -------------------------- helpers -------------------------- //
* Finds the index of the listener for the event in it's storage array.
*
* @param {Function[]} listeners Array of listeners to search through.
* @param {Function} listener Method to look for.
* @return {Number} Index of the specified listener, -1 if not found
* @api private
*/
function indexOfListener(listeners, listener) {
var i = listeners.length;
while (i--) {
if (listeners[i].listener === listener) {
return i;
}
}
return -1; // get a number from a string, not a percentage
} function getStyleSize( value ) {
var num = parseFloat( value );
// not a percent like '100%', and a number
var isValid = value.indexOf('%') === -1 && !isNaN( num );
return isValid && num;
}
/** function noop() {}
* Alias a method while keeping the context correct, to allow for overwriting of target method.
* var logError = typeof console === 'undefined' ? noop :
* @param {String} name The name of the target method. function( message ) {
* @return {Function} The aliased method console.error( message );
* @api private
*/
function alias(name) {
return function aliasClosure() {
return this[name].apply(this, arguments);
}; };
}
/** // -------------------------- measurements -------------------------- //
* Returns the listener array for the specified event.
* Will initialise the event object and listener arrays if required.
* Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
* Each property in the object response is an array of listener functions.
*
* @param {String|RegExp} evt Name of the event to return the listeners from.
* @return {Function[]|Object} All listener functions for the event.
*/
proto.getListeners = function getListeners(evt) {
var events = this._getEvents();
var response;
var key;
// Return a concatenated array of all matching events if var measurements = [
// the selector is a regular expression. 'paddingLeft',
if (typeof evt === 'object') { 'paddingRight',
response = {}; 'paddingTop',
for (key in events) { 'paddingBottom',
if (events.hasOwnProperty(key) && evt.test(key)) { 'marginLeft',
response[key] = events[key]; 'marginRight',
} 'marginTop',
} 'marginBottom',
} 'borderLeftWidth',
else { 'borderRightWidth',
response = events[evt] || (events[evt] = []); 'borderTopWidth',
} 'borderBottomWidth'
];
return response; function getZeroSize() {
var size = {
width: 0,
height: 0,
innerWidth: 0,
innerHeight: 0,
outerWidth: 0,
outerHeight: 0
}; };
for ( var i=0, len = measurements.length; i < len; i++ ) {
var measurement = measurements[i];
size[ measurement ] = 0;
}
return size;
}
/**
* Takes a list of listener objects and flattens it into a list of listener functions.
*
* @param {Object[]} listeners Raw listener objects.
* @return {Function[]} Just the listener functions.
*/
proto.flattenListeners = function flattenListeners(listeners) {
var flatListeners = [];
var i;
for (i = 0; i < listeners.length; i += 1) {
flatListeners.push(listeners[i].listener); function defineGetSize( getStyleProperty ) {
// -------------------------- setup -------------------------- //
var isSetup = false;
var getStyle, boxSizingProp, isBoxSizeOuter;
/**
* setup vars and functions
* do it on initial getSize(), rather than on script load
* For Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=548397
*/
function setup() {
// setup once
if ( isSetup ) {
return;
} }
isSetup = true;
return flatListeners; var getComputedStyle = window.getComputedStyle;
getStyle = ( function() {
var getStyleFn = getComputedStyle ?
function( elem ) {
return getComputedStyle( elem, null );
} :
function( elem ) {
return elem.currentStyle;
}; };
/** return function getStyle( elem ) {
* Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful. var style = getStyleFn( elem );
if ( !style ) {
logError( 'Style returned ' + style +
'. Are you running this code in a hidden iframe on Firefox? ' +
'See http://bit.ly/getsizebug1' );
}
return style;
};
})();
// -------------------------- box sizing -------------------------- //
boxSizingProp = getStyleProperty('boxSizing');
/**
* WebKit measures the outer-width on style.width on border-box elems
* IE & Firefox measures the inner-width
*/
if ( boxSizingProp ) {
var div = document.createElement('div');
div.style.width = '200px';
div.style.padding = '1px 2px 3px 4px';
div.style.borderStyle = 'solid';
div.style.borderWidth = '1px 2px 3px 4px';
div.style[ boxSizingProp ] = 'border-box';
var body = document.body || document.documentElement;
body.appendChild( div );
var style = getStyle( div );
isBoxSizeOuter = getStyleSize( style.width ) === 200;
body.removeChild( div );
}
}
// -------------------------- getSize -------------------------- //
function getSize( elem ) {
setup();
// use querySeletor if elem is string
if ( typeof elem === 'string' ) {
elem = document.querySelector( elem );
}
// do not proceed on non-objects
if ( !elem || typeof elem !== 'object' || !elem.nodeType ) {
return;
}
var style = getStyle( elem );
// if hidden, everything is 0
if ( style.display === 'none' ) {
return getZeroSize();
}
var size = {};
size.width = elem.offsetWidth;
size.height = elem.offsetHeight;
var isBorderBox = size.isBorderBox = !!( boxSizingProp &&
style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' );
// get all measurements
for ( var i=0, len = measurements.length; i < len; i++ ) {
var measurement = measurements[i];
var value = style[ measurement ];
value = mungeNonPixel( elem, value );
var num = parseFloat( value );
// any 'auto', 'medium' value will be 0
size[ measurement ] = !isNaN( num ) ? num : 0;
}
var paddingWidth = size.paddingLeft + size.paddingRight;
var paddingHeight = size.paddingTop + size.paddingBottom;
var marginWidth = size.marginLeft + size.marginRight;
var marginHeight = size.marginTop + size.marginBottom;
var borderWidth = size.borderLeftWidth + size.borderRightWidth;
var borderHeight = size.borderTopWidth + size.borderBottomWidth;
var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
// overwrite width and height if we can get it from style
var styleWidth = getStyleSize( style.width );
if ( styleWidth !== false ) {
size.width = styleWidth +
// add padding and border unless it's already including it
( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
}
var styleHeight = getStyleSize( style.height );
if ( styleHeight !== false ) {
size.height = styleHeight +
// add padding and border unless it's already including it
( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
}
size.innerWidth = size.width - ( paddingWidth + borderWidth );
size.innerHeight = size.height - ( paddingHeight + borderHeight );
size.outerWidth = size.width + marginWidth;
size.outerHeight = size.height + marginHeight;
return size;
}
// IE8 returns percent values, not pixels
// taken from jQuery's curCSS
function mungeNonPixel( elem, value ) {
// IE8 and has percent value
if ( window.getComputedStyle || value.indexOf('%') === -1 ) {
return value;
}
var style = elem.style;
// Remember the original values
var left = style.left;
var rs = elem.runtimeStyle;
var rsLeft = rs && rs.left;
// Put in the new values to get a computed value out
if ( rsLeft ) {
rs.left = elem.currentStyle.left;
}
style.left = value;
value = style.pixelLeft;
// Revert the changed values
style.left = left;
if ( rsLeft ) {
rs.left = rsLeft;
}
return value;
}
return getSize;
}
// transport
if ( typeof define === 'function' && define.amd ) {
// AMD for RequireJS
define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize );
} else if ( typeof exports === 'object' ) {
// CommonJS for Component
module.exports = defineGetSize( require('desandro-get-style-property') );
} else {
// browser global
window.getSize = defineGetSize( window.getStyleProperty );
}
})( window );
/*!
* eventie v1.0.6
* event binding helper
* eventie.bind( elem, 'click', myFn )
* eventie.unbind( elem, 'click', myFn )
* MIT license
*/
/*jshint browser: true, undef: true, unused: true */
/*global define: false, module: false */
( function( window ) {
var docElem = document.documentElement;
var bind = function() {};
function getIEEvent( obj ) {
var event = window.event;
// add event.target
event.target = event.target || event.srcElement || obj;
return event;
}
if ( docElem.addEventListener ) {
bind = function( obj, type, fn ) {
obj.addEventListener( type, fn, false );
};
} else if ( docElem.attachEvent ) {
bind = function( obj, type, fn ) {
obj[ type + fn ] = fn.handleEvent ?
function() {
var event = getIEEvent( obj );
fn.handleEvent.call( fn, event );
} :
function() {
var event = getIEEvent( obj );
fn.call( obj, event );
};
obj.attachEvent( "on" + type, obj[ type + fn ] );
};
}
var unbind = function() {};
if ( docElem.removeEventListener ) {
unbind = function( obj, type, fn ) {
obj.removeEventListener( type, fn, false );
};
} else if ( docElem.detachEvent ) {
unbind = function( obj, type, fn ) {
obj.detachEvent( "on" + type, obj[ type + fn ] );
try {
delete obj[ type + fn ];
} catch ( err ) {
// can't delete window object properties
obj[ type + fn ] = undefined;
}
};
}
var eventie = {
bind: bind,
unbind: unbind
};
// ----- module definition ----- //
if ( typeof define === 'function' && define.amd ) {
// AMD
define( 'eventie/eventie',eventie );
} else if ( typeof exports === 'object' ) {
// CommonJS
module.exports = eventie;
} else {
// browser global
window.eventie = eventie;
}
})( window );
/*!
* EventEmitter v4.2.11 - git.io/ee
* Unlicense - http://unlicense.org/
* Oliver Caldwell - http://oli.me.uk/
* @preserve
*/
;(function () {
/**
* Class for managing events.
* Can be extended to provide event functionality in other classes.
*
* @class EventEmitter Manages event registering and emitting.
*/
function EventEmitter() {}
// Shortcuts to improve speed and size
var proto = EventEmitter.prototype;
var exports = this;
var originalGlobalValue = exports.EventEmitter;
/**
* Finds the index of the listener for the event in its storage array.
*
* @param {Function[]} listeners Array of listeners to search through.
* @param {Function} listener Method to look for.
* @return {Number} Index of the specified listener, -1 if not found
* @api private
*/
function indexOfListener(listeners, listener) {
var i = listeners.length;
while (i--) {
if (listeners[i].listener === listener) {
return i;
}
}
return -1;
}
/**
* Alias a method while keeping the context correct, to allow for overwriting of target method.
*
* @param {String} name The name of the target method.
* @return {Function} The aliased method
* @api private
*/
function alias(name) {
return function aliasClosure() {
return this[name].apply(this, arguments);
};
}
/**
* Returns the listener array for the specified event.
* Will initialise the event object and listener arrays if required.
* Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
* Each property in the object response is an array of listener functions.
*
* @param {String|RegExp} evt Name of the event to return the listeners from.
* @return {Function[]|Object} All listener functions for the event.
*/
proto.getListeners = function getListeners(evt) {
var events = this._getEvents();
var response;
var key;
// Return a concatenated array of all matching events if
// the selector is a regular expression.
if (evt instanceof RegExp) {
response = {};
for (key in events) {
if (events.hasOwnProperty(key) && evt.test(key)) {
response[key] = events[key];
}
}
}
else {
response = events[evt] || (events[evt] = []);
}
return response;
};
/**
* Takes a list of listener objects and flattens it into a list of listener functions.
*
* @param {Object[]} listeners Raw listener objects.
* @return {Function[]} Just the listener functions.
*/
proto.flattenListeners = function flattenListeners(listeners) {
var flatListeners = [];
var i;
for (i = 0; i < listeners.length; i += 1) {
flatListeners.push(listeners[i].listener);
}
return flatListeners;
};
/**
* Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
* *
* @param {String|RegExp} evt Name of the event to return the listeners from. * @param {String|RegExp} evt Name of the event to return the listeners from.
* @return {Object} All listener functions for an event in an object. * @return {Object} All listener functions for an event in an object.
...@@ -315,7 +776,7 @@ ...@@ -315,7 +776,7 @@
/** /**
* Semi-alias of addListener. It will add a listener that will be * Semi-alias of addListener. It will add a listener that will be
* automatically removed after it's first execution. * automatically removed after its first execution.
* *
* @param {String|RegExp} evt Name of the event to attach the listener to. * @param {String|RegExp} evt Name of the event to attach the listener to.
* @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
...@@ -437,7 +898,7 @@ ...@@ -437,7 +898,7 @@
var single = remove ? this.removeListener : this.addListener; var single = remove ? this.removeListener : this.addListener;
var multiple = remove ? this.removeListeners : this.addListeners; var multiple = remove ? this.removeListeners : this.addListeners;
// If evt is an object then pass each of it's properties to this method // If evt is an object then pass each of its properties to this method
if (typeof evt === 'object' && !(evt instanceof RegExp)) { if (typeof evt === 'object' && !(evt instanceof RegExp)) {
for (i in evt) { for (i in evt) {
if (evt.hasOwnProperty(i) && (value = evt[i])) { if (evt.hasOwnProperty(i) && (value = evt[i])) {
...@@ -484,7 +945,7 @@ ...@@ -484,7 +945,7 @@
// Remove all listeners for the specified event // Remove all listeners for the specified event
delete events[evt]; delete events[evt];
} }
else if (type === 'object') { else if (evt instanceof RegExp) {
// Remove all events matching the regex. // Remove all events matching the regex.
for (key in events) { for (key in events) {
if (events.hasOwnProperty(key) && evt.test(key)) { if (events.hasOwnProperty(key) && evt.test(key)) {
...@@ -569,324 +1030,777 @@ ...@@ -569,324 +1030,777 @@
return this.emitEvent(evt, args); return this.emitEvent(evt, args);
}; };
/** /**
* Sets the current value to check against when executing listeners. If a * Sets the current value to check against when executing listeners. If a
* listeners return value matches the one set here then it will be removed * listeners return value matches the one set here then it will be removed
* after execution. This value defaults to true. * after execution. This value defaults to true.
* *
* @param {*} value The new value to check for when executing listeners. * @param {*} value The new value to check for when executing listeners.
* @return {Object} Current instance of EventEmitter for chaining. * @return {Object} Current instance of EventEmitter for chaining.
*/ */
proto.setOnceReturnValue = function setOnceReturnValue(value) { proto.setOnceReturnValue = function setOnceReturnValue(value) {
this._onceReturnValue = value; this._onceReturnValue = value;
return this; return this;
}; };
/**
* Fetches the current value to check against when executing listeners. If
* the listeners return value matches this one then it should be removed
* automatically. It will return true by default.
*
* @return {*|Boolean} The current value to check for or the default, true.
* @api private
*/
proto._getOnceReturnValue = function _getOnceReturnValue() {
if (this.hasOwnProperty('_onceReturnValue')) {
return this._onceReturnValue;
}
else {
return true;
}
};
/**
* Fetches the events object and creates one if required.
*
* @return {Object} The events storage object.
* @api private
*/
proto._getEvents = function _getEvents() {
return this._events || (this._events = {});
};
/**
* Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
*
* @return {Function} Non conflicting EventEmitter class.
*/
EventEmitter.noConflict = function noConflict() {
exports.EventEmitter = originalGlobalValue;
return EventEmitter;
};
// Expose the class either via AMD, CommonJS or the global object
if (typeof define === 'function' && define.amd) {
define('eventEmitter/EventEmitter',[],function () {
return EventEmitter;
});
}
else if (typeof module === 'object' && module.exports){
module.exports = EventEmitter;
}
else {
exports.EventEmitter = EventEmitter;
}
}.call(this));
/*!
* Unipointer v1.1.0
* base class for doing one thing with pointer event
* MIT license
*/
/*jshint browser: true, undef: true, unused: true, strict: true */
/*global define: false, module: false, require: false */
( function( window, factory ) {
// universal module definition
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'unipointer/unipointer',[
'eventEmitter/EventEmitter',
'eventie/eventie'
], function( EventEmitter, eventie ) {
return factory( window, EventEmitter, eventie );
});
} else if ( typeof exports == 'object' ) {
// CommonJS
module.exports = factory(
window,
require('wolfy87-eventemitter'),
require('eventie')
);
} else {
// browser global
window.Unipointer = factory(
window,
window.EventEmitter,
window.eventie
);
}
}( window, function factory( window, EventEmitter, eventie ) {
function noop() {}
function Unipointer() {}
// inherit EventEmitter
Unipointer.prototype = new EventEmitter();
Unipointer.prototype.bindStartEvent = function( elem ) {
this._bindStartEvent( elem, true );
};
Unipointer.prototype.unbindStartEvent = function( elem ) {
this._bindStartEvent( elem, false );
};
/**
* works as unbinder, as you can ._bindStart( false ) to unbind
* @param {Boolean} isBind - will unbind if falsey
*/
Unipointer.prototype._bindStartEvent = function( elem, isBind ) {
// munge isBind, default to true
isBind = isBind === undefined ? true : !!isBind;
var bindMethod = isBind ? 'bind' : 'unbind';
if ( window.navigator.pointerEnabled ) {
// W3C Pointer Events, IE11. See https://coderwall.com/p/mfreca
eventie[ bindMethod ]( elem, 'pointerdown', this );
} else if ( window.navigator.msPointerEnabled ) {
// IE10 Pointer Events
eventie[ bindMethod ]( elem, 'MSPointerDown', this );
} else {
// listen for both, for devices like Chrome Pixel
eventie[ bindMethod ]( elem, 'mousedown', this );
eventie[ bindMethod ]( elem, 'touchstart', this );
}
};
// trigger handler methods for events
Unipointer.prototype.handleEvent = function( event ) {
var method = 'on' + event.type;
if ( this[ method ] ) {
this[ method ]( event );
}
};
// returns the touch that we're keeping track of
Unipointer.prototype.getTouch = function( touches ) {
for ( var i=0, len = touches.length; i < len; i++ ) {
var touch = touches[i];
if ( touch.identifier == this.pointerIdentifier ) {
return touch;
}
}
};
// ----- start event ----- //
Unipointer.prototype.onmousedown = function( event ) {
// dismiss clicks from right or middle buttons
var button = event.button;
if ( button && ( button !== 0 && button !== 1 ) ) {
return;
}
this._pointerDown( event, event );
};
Unipointer.prototype.ontouchstart = function( event ) {
this._pointerDown( event, event.changedTouches[0] );
};
/** Unipointer.prototype.onMSPointerDown =
* Fetches the current value to check against when executing listeners. If Unipointer.prototype.onpointerdown = function( event ) {
* the listeners return value matches this one then it should be removed this._pointerDown( event, event );
* automatically. It will return true by default. };
*
* @return {*|Boolean} The current value to check for or the default, true. /**
* @api private * pointer start
* @param {Event} event
* @param {Event or Touch} pointer
*/ */
proto._getOnceReturnValue = function _getOnceReturnValue() { Unipointer.prototype._pointerDown = function( event, pointer ) {
if (this.hasOwnProperty('_onceReturnValue')) { // dismiss other pointers
return this._onceReturnValue; if ( this.isPointerDown ) {
return;
} }
else {
return true; this.isPointerDown = true;
// save pointer identifier to match up touch events
this.pointerIdentifier = pointer.pointerId !== undefined ?
// pointerId for pointer events, touch.indentifier for touch events
pointer.pointerId : pointer.identifier;
this.pointerDown( event, pointer );
};
Unipointer.prototype.pointerDown = function( event, pointer ) {
this._bindPostStartEvents( event );
this.emitEvent( 'pointerDown', [ event, pointer ] );
};
// hash of events to be bound after start event
var postStartEvents = {
mousedown: [ 'mousemove', 'mouseup' ],
touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
MSPointerDown: [ 'MSPointerMove', 'MSPointerUp', 'MSPointerCancel' ]
};
Unipointer.prototype._bindPostStartEvents = function( event ) {
if ( !event ) {
return;
} }
// get proper events to match start event
var events = postStartEvents[ event.type ];
// IE8 needs to be bound to document
var node = event.preventDefault ? window : document;
// bind events to node
for ( var i=0, len = events.length; i < len; i++ ) {
var evnt = events[i];
eventie.bind( node, evnt, this );
}
// save these arguments
this._boundPointerEvents = {
events: events,
node: node
}; };
};
/** Unipointer.prototype._unbindPostStartEvents = function() {
* Fetches the events object and creates one if required. var args = this._boundPointerEvents;
* // IE8 can trigger dragEnd twice, check for _boundEvents
* @return {Object} The events storage object. if ( !args || !args.events ) {
* @api private return;
*/ }
proto._getEvents = function _getEvents() {
return this._events || (this._events = {});
};
// Expose the class either via AMD, CommonJS or the global object for ( var i=0, len = args.events.length; i < len; i++ ) {
if (typeof define === 'function' && define.amd) { var event = args.events[i];
define("EventEmitter", function () { eventie.unbind( args.node, event, this );
return EventEmitter;
});
} }
else if (typeof module === 'object' && module.exports){ delete this._boundPointerEvents;
module.exports = EventEmitter; };
// ----- move event ----- //
Unipointer.prototype.onmousemove = function( event ) {
this._pointerMove( event, event );
};
Unipointer.prototype.onMSPointerMove =
Unipointer.prototype.onpointermove = function( event ) {
if ( event.pointerId == this.pointerIdentifier ) {
this._pointerMove( event, event );
} }
else { };
this.EventEmitter = EventEmitter;
Unipointer.prototype.ontouchmove = function( event ) {
var touch = this.getTouch( event.changedTouches );
if ( touch ) {
this._pointerMove( event, touch );
} }
}.call(this)); };
/*! /**
* getStyleProperty by kangax * pointer move
* http://perfectionkills.com/feature-testing-css-properties/ * @param {Event} event
* @param {Event or Touch} pointer
* @private
*/ */
Unipointer.prototype._pointerMove = function( event, pointer ) {
this.pointerMove( event, pointer );
};
/*jshint browser: true, strict: true, undef: true */ // public
/*globals define: false */ Unipointer.prototype.pointerMove = function( event, pointer ) {
this.emitEvent( 'pointerMove', [ event, pointer ] );
};
( function( window ) { // ----- end event ----- //
'use strict';
var prefixes = 'Webkit Moz ms Ms O'.split(' '); Unipointer.prototype.onmouseup = function( event ) {
var docElemStyle = document.documentElement.style; this._pointerUp( event, event );
};
function getStyleProperty( propName ) { Unipointer.prototype.onMSPointerUp =
if ( !propName ) { Unipointer.prototype.onpointerup = function( event ) {
return; if ( event.pointerId == this.pointerIdentifier ) {
this._pointerUp( event, event );
} }
};
// test standard property first Unipointer.prototype.ontouchend = function( event ) {
if ( typeof docElemStyle[ propName ] === 'string' ) { var touch = this.getTouch( event.changedTouches );
return propName; if ( touch ) {
this._pointerUp( event, touch );
} }
};
// capitalize /**
propName = propName.charAt(0).toUpperCase() + propName.slice(1); * pointer up
* @param {Event} event
* @param {Event or Touch} pointer
* @private
*/
Unipointer.prototype._pointerUp = function( event, pointer ) {
this._pointerDone();
this.pointerUp( event, pointer );
};
// public
Unipointer.prototype.pointerUp = function( event, pointer ) {
this.emitEvent( 'pointerUp', [ event, pointer ] );
};
// ----- pointer done ----- //
// triggered on pointer up & pointer cancel
Unipointer.prototype._pointerDone = function() {
// reset properties
this.isPointerDown = false;
delete this.pointerIdentifier;
// remove events
this._unbindPostStartEvents();
this.pointerDone();
};
// test vendor specific properties Unipointer.prototype.pointerDone = noop;
var prefixed;
for ( var i=0, len = prefixes.length; i < len; i++ ) { // ----- pointer cancel ----- //
prefixed = prefixes[i] + propName;
if ( typeof docElemStyle[ prefixed ] === 'string' ) { Unipointer.prototype.onMSPointerCancel =
return prefixed; Unipointer.prototype.onpointercancel = function( event ) {
} if ( event.pointerId == this.pointerIdentifier ) {
this._pointerCancel( event, event );
} }
};
Unipointer.prototype.ontouchcancel = function( event ) {
var touch = this.getTouch( event.changedTouches );
if ( touch ) {
this._pointerCancel( event, touch );
} }
};
// transport /**
if ( typeof define === 'function' && define.amd ) { * pointer cancel
* @param {Event} event
* @param {Event or Touch} pointer
* @private
*/
Unipointer.prototype._pointerCancel = function( event, pointer ) {
this._pointerDone();
this.pointerCancel( event, pointer );
};
// public
Unipointer.prototype.pointerCancel = function( event, pointer ) {
this.emitEvent( 'pointerCancel', [ event, pointer ] );
};
// ----- ----- //
// utility function for getting x/y cooridinates from event, because IE8
Unipointer.getPointerPoint = function( pointer ) {
return {
x: pointer.pageX !== undefined ? pointer.pageX : pointer.clientX,
y: pointer.pageY !== undefined ? pointer.pageY : pointer.clientY
};
};
// ----- ----- //
return Unipointer;
}));
/*!
* Unidragger v1.1.0
* Draggable base class
* MIT license
*/
/*jshint browser: true, unused: true, undef: true, strict: true */
( function( window, factory ) {
/*global define: false, module: false, require: false */
// universal module definition
if ( typeof define == 'function' && define.amd ) {
// AMD // AMD
define("getStyleProperty", function() { define( 'unidragger/unidragger',[
return getStyleProperty; 'eventie/eventie',
'unipointer/unipointer'
], function( eventie, Unipointer ) {
return factory( window, eventie, Unipointer );
}); });
} else if ( typeof exports == 'object' ) {
// CommonJS
module.exports = factory(
window,
require('eventie'),
require('unipointer')
);
} else { } else {
// browser global // browser global
window.getStyleProperty = getStyleProperty; window.Unidragger = factory(
window,
window.eventie,
window.Unipointer
);
} }
})( window ); }( window, function factory( window, eventie, Unipointer ) {
/**
* getSize v1.1.4
* measure size of elements
*/
/*jshint browser: true, strict: true, undef: true, unused: true */
/*global define: false */
( function( window, undefined ) { // ----- ----- //
'use strict'; function noop() {}
// -------------------------- helpers -------------------------- // // handle IE8 prevent default
function preventDefaultEvent( event ) {
if ( event.preventDefault ) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
var defView = document.defaultView; function getParentLink( elem ) {
while ( elem != document.body ) {
elem = elem.parentNode;
if ( elem.nodeName == 'A' ) {
return elem;
}
}
}
var getStyle = defView && defView.getComputedStyle ? // -------------------------- Unidragger -------------------------- //
function( elem ) {
return defView.getComputedStyle( elem, null );
} :
function( elem ) {
return elem.currentStyle;
};
// get a number from a string, not a percentage function Unidragger() {}
function getStyleSize( value ) {
var num = parseFloat( value );
// not a percent like '100%', and a number
var isValid = value.indexOf('%') === -1 && !isNaN( num );
return isValid && num;
}
// -------------------------- measurements -------------------------- // // inherit Unipointer & EventEmitter
Unidragger.prototype = new Unipointer();
var measurements = [ // ----- bind start ----- //
'paddingLeft',
'paddingRight',
'paddingTop',
'paddingBottom',
'marginLeft',
'marginRight',
'marginTop',
'marginBottom',
'borderLeftWidth',
'borderRightWidth',
'borderTopWidth',
'borderBottomWidth'
];
function getZeroSize() { Unidragger.prototype.bindHandles = function() {
var size = { this._bindHandles( true );
width: 0, };
height: 0,
innerWidth: 0, Unidragger.prototype.unbindHandles = function() {
innerHeight: 0, this._bindHandles( false );
outerWidth: 0, };
outerHeight: 0
var navigator = window.navigator;
/**
* works as unbinder, as you can .bindHandles( false ) to unbind
* @param {Boolean} isBind - will unbind if falsey
*/
Unidragger.prototype._bindHandles = function( isBind ) {
// munge isBind, default to true
isBind = isBind === undefined ? true : !!isBind;
// extra bind logic
var binderExtra;
if ( navigator.pointerEnabled ) {
binderExtra = function( handle ) {
// disable scrolling on the element
handle.style.touchAction = isBind ? 'none' : '';
};
} else if ( navigator.msPointerEnabled ) {
binderExtra = function( handle ) {
// disable scrolling on the element
handle.style.msTouchAction = isBind ? 'none' : '';
}; };
for ( var i=0, len = measurements.length; i < len; i++ ) { } else {
var measurement = measurements[i]; binderExtra = function() {
size[ measurement ] = 0; // TODO re-enable img.ondragstart when unbinding
if ( isBind ) {
disableImgOndragstart( handle );
} }
return size; };
}
// bind each handle
var bindMethod = isBind ? 'bind' : 'unbind';
for ( var i=0, len = this.handles.length; i < len; i++ ) {
var handle = this.handles[i];
this._bindStartEvent( handle, isBind );
binderExtra( handle );
eventie[ bindMethod ]( handle, 'click', this );
} }
};
// remove default dragging interaction on all images in IE8
// IE8 does its own drag thing on images, which messes stuff up
function noDragStart() {
return false;
}
function defineGetSize( getStyleProperty ) { // TODO replace this with a IE8 test
var isIE8 = 'attachEvent' in document.documentElement;
// -------------------------- box sizing -------------------------- // // IE8 only
var disableImgOndragstart = !isIE8 ? noop : function( handle ) {
var boxSizingProp = getStyleProperty('boxSizing'); if ( handle.nodeName == 'IMG' ) {
var isBoxSizeOuter; handle.ondragstart = noDragStart;
}
/** var images = handle.querySelectorAll('img');
* WebKit measures the outer-width on style.width on border-box elems for ( var i=0, len = images.length; i < len; i++ ) {
* IE & Firefox measures the inner-width var img = images[i];
*/ img.ondragstart = noDragStart;
( function() {
if ( !boxSizingProp ) {
return;
} }
};
var div = document.createElement('div'); // ----- start event ----- //
div.style.width = '200px';
div.style.padding = '1px 2px 3px 4px';
div.style.borderStyle = 'solid';
div.style.borderWidth = '1px 2px 3px 4px';
div.style[ boxSizingProp ] = 'border-box';
var body = document.body || document.documentElement; var allowTouchstartNodes = Unidragger.allowTouchstartNodes = {
body.appendChild( div ); INPUT: true,
var style = getStyle( div ); A: true,
BUTTON: true,
SELECT: true
};
isBoxSizeOuter = getStyleSize( style.width ) === 200; /**
body.removeChild( div ); * pointer start
})(); * @param {Event} event
* @param {Event or Touch} pointer
*/
Unidragger.prototype.pointerDown = function( event, pointer ) {
this._dragPointerDown( event, pointer );
// kludge to blur focused inputs in dragger
var focused = document.activeElement;
if ( focused && focused.blur ) {
focused.blur();
}
// bind move and end events
this._bindPostStartEvents( event );
this.emitEvent( 'pointerDown', [ event, pointer ] );
};
// base pointer down logic
Unidragger.prototype._dragPointerDown = function( event, pointer ) {
// track to see when dragging starts
this.pointerDownPoint = Unipointer.getPointerPoint( pointer );
// -------------------------- getSize -------------------------- // var targetNodeName = event.target.nodeName;
// HACK iOS, allow clicks on buttons, inputs, and links, or children of links
var isTouchstartNode = event.type == 'touchstart' &&
( allowTouchstartNodes[ targetNodeName ] || getParentLink( event.target ) );
// do not prevent default on touchstart nodes or <select>
if ( !isTouchstartNode && targetNodeName != 'SELECT' ) {
preventDefaultEvent( event );
}
};
function getSize( elem ) { // ----- move event ----- //
// use querySeletor if elem is string
if ( typeof elem === 'string' ) { /**
elem = document.querySelector( elem ); * drag move
* @param {Event} event
* @param {Event or Touch} pointer
*/
Unidragger.prototype.pointerMove = function( event, pointer ) {
var moveVector = this._dragPointerMove( event, pointer );
this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
this._dragMove( event, pointer, moveVector );
};
// base pointer move logic
Unidragger.prototype._dragPointerMove = function( event, pointer ) {
var movePoint = Unipointer.getPointerPoint( pointer );
var moveVector = {
x: movePoint.x - this.pointerDownPoint.x,
y: movePoint.y - this.pointerDownPoint.y
};
// start drag if pointer has moved far enough to start drag
if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
this._dragStart( event, pointer );
} }
return moveVector;
};
// do not proceed on non-objects // condition if pointer has moved far enough to start drag
if ( !elem || typeof elem !== 'object' || !elem.nodeType ) { Unidragger.prototype.hasDragStarted = function( moveVector ) {
return; return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
};
// ----- end event ----- //
/**
* pointer up
* @param {Event} event
* @param {Event or Touch} pointer
*/
Unidragger.prototype.pointerUp = function( event, pointer ) {
this.emitEvent( 'pointerUp', [ event, pointer ] );
this._dragPointerUp( event, pointer );
};
Unidragger.prototype._dragPointerUp = function( event, pointer ) {
if ( this.isDragging ) {
this._dragEnd( event, pointer );
} else {
// pointer didn't move enough for drag to start
this._staticClick( event, pointer );
} }
};
var style = getStyle( elem ); // -------------------------- drag -------------------------- //
// if hidden, everything is 0 // dragStart
if ( style.display === 'none' ) { Unidragger.prototype._dragStart = function( event, pointer ) {
return getZeroSize(); this.isDragging = true;
this.dragStartPoint = Unidragger.getPointerPoint( pointer );
// prevent clicks
this.isPreventingClicks = true;
this.dragStart( event, pointer );
};
Unidragger.prototype.dragStart = function( event, pointer ) {
this.emitEvent( 'dragStart', [ event, pointer ] );
};
// dragMove
Unidragger.prototype._dragMove = function( event, pointer, moveVector ) {
// do not drag if not dragging yet
if ( !this.isDragging ) {
return;
} }
var size = {}; this.dragMove( event, pointer, moveVector );
size.width = elem.offsetWidth; };
size.height = elem.offsetHeight;
var isBorderBox = size.isBorderBox = !!( boxSizingProp && Unidragger.prototype.dragMove = function( event, pointer, moveVector ) {
style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' ); this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
};
// get all measurements // dragEnd
for ( var i=0, len = measurements.length; i < len; i++ ) { Unidragger.prototype._dragEnd = function( event, pointer ) {
var measurement = measurements[i]; // set flags
var value = style[ measurement ]; this.isDragging = false;
var num = parseFloat( value ); // re-enable clicking async
// any 'auto', 'medium' value will be 0 var _this = this;
size[ measurement ] = !isNaN( num ) ? num : 0; setTimeout( function() {
} delete _this.isPreventingClicks;
});
var paddingWidth = size.paddingLeft + size.paddingRight; this.dragEnd( event, pointer );
var paddingHeight = size.paddingTop + size.paddingBottom; };
var marginWidth = size.marginLeft + size.marginRight;
var marginHeight = size.marginTop + size.marginBottom;
var borderWidth = size.borderLeftWidth + size.borderRightWidth;
var borderHeight = size.borderTopWidth + size.borderBottomWidth;
var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; Unidragger.prototype.dragEnd = function( event, pointer ) {
this.emitEvent( 'dragEnd', [ event, pointer ] );
};
// overwrite width and height if we can get it from style // ----- onclick ----- //
var styleWidth = getStyleSize( style.width );
if ( styleWidth !== false ) { // handle all clicks and prevent clicks when dragging
size.width = styleWidth + Unidragger.prototype.onclick = function( event ) {
// add padding and border unless it's already including it if ( this.isPreventingClicks ) {
( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); preventDefaultEvent( event );
} }
};
var styleHeight = getStyleSize( style.height ); // ----- staticClick ----- //
if ( styleHeight !== false ) {
size.height = styleHeight + // triggered after pointer down & up with no/tiny movement
// add padding and border unless it's already including it Unidragger.prototype._staticClick = function( event, pointer ) {
( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); // allow click in text input
if ( event.target.nodeName == 'INPUT' && event.target.type == 'text' ) {
event.target.focus();
} }
this.staticClick( event, pointer );
};
size.innerWidth = size.width - ( paddingWidth + borderWidth ); Unidragger.prototype.staticClick = function( event, pointer ) {
size.innerHeight = size.height - ( paddingHeight + borderHeight ); this.emitEvent( 'staticClick', [ event, pointer ] );
};
size.outerWidth = size.width + marginWidth; // ----- ----- //
size.outerHeight = size.height + marginHeight;
return size; Unidragger.getPointerPoint = function( pointer ) {
} return {
x: pointer.pageX !== undefined ? pointer.pageX : pointer.clientX,
y: pointer.pageY !== undefined ? pointer.pageY : pointer.clientY
};
};
return getSize; // ----- ----- //
} Unidragger.getPointerPoint = Unipointer.getPointerPoint;
// transport return Unidragger;
if ( typeof define === 'function' && define.amd ) {
// AMD
define("getSize", [ 'getStyleProperty' ], defineGetSize );
} else {
// browser global
window.getSize = defineGetSize( window.getStyleProperty );
}
})( window ); }));
/*! /*!
* Draggabilly v1.0.5 * Draggabilly v1.2.4
* Make that shiz draggable * Make that shiz draggable
* http://draggabilly.desandro.com * http://draggabilly.desandro.com
* MIT license
*/ */
( function( window ) { ( function( window, factory ) {
if ( typeof define == 'function' && define.amd ) {
// AMD
define( [
'classie/classie',
'get-style-property/get-style-property',
'get-size/get-size',
'unidragger/unidragger'
],
function( classie, getStyleProperty, getSize, Unidragger ) {
return factory( window, classie, getStyleProperty, getSize, Unidragger );
});
} else if ( typeof exports == 'object' ) {
// CommonJS
module.exports = factory(
window,
require('desandro-classie'),
require('desandro-get-style-property'),
require('get-size'),
require('unidragger')
);
} else {
// browser global
window.Draggabilly = factory(
window,
window.classie,
window.getStyleProperty,
window.getSize,
window.Unidragger
);
}
}( window, function factory( window, classie, getStyleProperty, getSize, Unidragger ) {
'use strict';
// vars // vars
var document = window.document; var document = window.document;
function noop() {}
// -------------------------- helpers -------------------------- // // -------------------------- helpers -------------------------- //
// extend objects // extend objects
function extend( a, b ) { function extend( a, b ) {
for ( var prop in b ) { for ( var prop in b ) {
a[ prop ] = b[ prop ]; a[ prop ] = b[ prop ];
} }
return a; return a;
} }
function noop() {}
// ----- get style ----- // // ----- get style ----- //
var defView = document.defaultView; var defView = document.defaultView;
var getStyle = defView && defView.getComputedStyle ? var getStyle = defView && defView.getComputedStyle ?
function( elem ) { function( elem ) {
return defView.getComputedStyle( elem, null ); return defView.getComputedStyle( elem, null );
} : } :
...@@ -896,27 +1810,27 @@ ...@@ -896,27 +1810,27 @@
// http://stackoverflow.com/a/384380/182183 // http://stackoverflow.com/a/384380/182183
var isElement = ( typeof HTMLElement === 'object' ) ? var isElement = ( typeof HTMLElement == 'object' ) ?
function isElementDOM2( obj ) { function isElementDOM2( obj ) {
return obj instanceof HTMLElement; return obj instanceof HTMLElement;
} : } :
function isElementQuirky( obj ) { function isElementQuirky( obj ) {
return obj && typeof obj === 'object' && return obj && typeof obj == 'object' &&
obj.nodeType === 1 && typeof obj.nodeName === 'string'; obj.nodeType == 1 && typeof obj.nodeName == 'string';
}; };
// -------------------------- requestAnimationFrame -------------------------- // // -------------------------- requestAnimationFrame -------------------------- //
// https://gist.github.com/1866474 // https://gist.github.com/1866474
var lastTime = 0; var lastTime = 0;
var prefixes = 'webkit moz ms o'.split(' '); var prefixes = 'webkit moz ms o'.split(' ');
// get unprefixed rAF and cAF, if present // get unprefixed rAF and cAF, if present
var requestAnimationFrame = window.requestAnimationFrame; var requestAnimationFrame = window.requestAnimationFrame;
var cancelAnimationFrame = window.cancelAnimationFrame; var cancelAnimationFrame = window.cancelAnimationFrame;
// loop through vendor prefixes and get prefixed rAF and cAF // loop through vendor prefixes and get prefixed rAF and cAF
var prefix; var prefix;
for( var i = 0; i < prefixes.length; i++ ) { for( var i = 0; i < prefixes.length; i++ ) {
if ( requestAnimationFrame && cancelAnimationFrame ) { if ( requestAnimationFrame && cancelAnimationFrame ) {
break; break;
} }
...@@ -924,10 +1838,10 @@ ...@@ -924,10 +1838,10 @@
requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ]; requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ];
cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] || cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] ||
window[ prefix + 'CancelRequestAnimationFrame' ]; window[ prefix + 'CancelRequestAnimationFrame' ];
} }
// fallback to setTimeout and clearTimeout if either request/cancel is not supported // fallback to setTimeout and clearTimeout if either request/cancel is not supported
if ( !requestAnimationFrame || !cancelAnimationFrame ) { if ( !requestAnimationFrame || !cancelAnimationFrame ) {
requestAnimationFrame = function( callback ) { requestAnimationFrame = function( callback ) {
var currTime = new Date().getTime(); var currTime = new Date().getTime();
var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
...@@ -941,37 +1855,49 @@ ...@@ -941,37 +1855,49 @@
cancelAnimationFrame = function( id ) { cancelAnimationFrame = function( id ) {
window.clearTimeout( id ); window.clearTimeout( id );
}; };
} }
// -------------------------- definition -------------------------- //
function draggabillyDefinition( classie, EventEmitter, eventie, getStyleProperty, getSize ) {
// -------------------------- support -------------------------- // // -------------------------- support -------------------------- //
var transformProperty = getStyleProperty('transform'); var transformProperty = getStyleProperty('transform');
// TODO fix quick & dirty check for 3D support // TODO fix quick & dirty check for 3D support
var is3d = !!getStyleProperty('perspective'); var is3d = !!getStyleProperty('perspective');
var jQuery = window.jQuery;
// -------------------------- -------------------------- // // -------------------------- -------------------------- //
function Draggabilly( element, options ) { function Draggabilly( element, options ) {
this.element = element; // querySelector if string
this.element = typeof element == 'string' ?
document.querySelector( element ) : element;
if ( jQuery ) {
this.$element = jQuery( this.element );
}
this.options = extend( {}, this.options ); // options
extend( this.options, options ); this.options = extend( {}, this.constructor.defaults );
this.option( options );
this._create(); this._create();
}
} // inherit Unidragger methods
extend( Draggabilly.prototype, Unidragger.prototype );
// inherit EventEmitter methods Draggabilly.defaults = {
extend( Draggabilly.prototype, EventEmitter.prototype ); };
Draggabilly.prototype.options = { /**
}; * set options
* @param {Object} opts
*/
Draggabilly.prototype.option = function( opts ) {
extend( this.options, opts );
};
Draggabilly.prototype._create = function() { Draggabilly.prototype._create = function() {
// properties // properties
this.position = {}; this.position = {};
...@@ -984,60 +1910,53 @@ ...@@ -984,60 +1910,53 @@
// set relative positioning // set relative positioning
var style = getStyle( this.element ); var style = getStyle( this.element );
if ( style.position !== 'relative' && style.position !== 'absolute' ) { if ( style.position != 'relative' && style.position != 'absolute' ) {
this.element.style.position = 'relative'; this.element.style.position = 'relative';
} }
this.enable(); this.enable();
this.setHandles(); this.setHandles();
}; };
/** /**
* set this.handles and bind start events to 'em * set this.handles and bind start events to 'em
*/ */
Draggabilly.prototype.setHandles = function() { Draggabilly.prototype.setHandles = function() {
this.handles = this.options.handle ? this.handles = this.options.handle ?
this.element.querySelectorAll( this.options.handle ) : [ this.element ]; this.element.querySelectorAll( this.options.handle ) : [ this.element ];
for ( var i=0, len = this.handles.length; i < len; i++ ) { this.bindHandles();
var handle = this.handles[i]; };
// bind pointer start event
// listen for both, for devices like Chrome Pixel
// which has touch and mouse events
eventie.bind( handle, 'mousedown', this );
eventie.bind( handle, 'touchstart', this );
disableImgOndragstart( handle );
}
};
// remove default dragging interaction on all images in IE8
// IE8 does its own drag thing on images, which messes stuff up
function noDragStart() {
return false;
}
// TODO replace this with a IE8 test
var isIE8 = 'attachEvent' in document.documentElement;
// IE8 only /**
var disableImgOndragstart = !isIE8 ? noop : function( handle ) { * emits events via eventEmitter and jQuery events
* @param {String} type - name of event
if ( handle.nodeName === 'IMG' ) { * @param {Event} event - original event
handle.ondragstart = noDragStart; * @param {Array} args - extra arguments
*/
Draggabilly.prototype.dispatchEvent = function( type, event, args ) {
var emitArgs = [ event ].concat( args );
this.emitEvent( type, emitArgs );
var jQuery = window.jQuery;
// trigger jQuery event
if ( jQuery && this.$element ) {
if ( event ) {
// create jQuery event
var $event = jQuery.Event( event );
$event.type = type;
this.$element.trigger( $event, args );
} else {
// just trigger with type if no event available
this.$element.trigger( type, args );
} }
var images = handle.querySelectorAll('img');
for ( var i=0, len = images.length; i < len; i++ ) {
var img = images[i];
img.ondragstart = noDragStart;
} }
}; };
// -------------------------- position -------------------------- //
// get left/top position from style // get left/top position from style
Draggabilly.prototype._getPosition = function() { Draggabilly.prototype._getPosition = function() {
// properties // properties
var style = getStyle( this.element ); var style = getStyle( this.element );
...@@ -1049,10 +1968,10 @@ ...@@ -1049,10 +1968,10 @@
this.position.y = isNaN( y ) ? 0 : y; this.position.y = isNaN( y ) ? 0 : y;
this._addTransformPosition( style ); this._addTransformPosition( style );
}; };
// add transform: translate( x, y ) to position // add transform: translate( x, y ) to position
Draggabilly.prototype._addTransformPosition = function( style ) { Draggabilly.prototype._addTransformPosition = function( style ) {
if ( !transformProperty ) { if ( !transformProperty ) {
return; return;
} }
...@@ -1070,123 +1989,68 @@ ...@@ -1070,123 +1989,68 @@
var translateY = parseInt( matrixValues[ xIndex + 1 ], 10 ); var translateY = parseInt( matrixValues[ xIndex + 1 ], 10 );
this.position.x += translateX; this.position.x += translateX;
this.position.y += translateY; this.position.y += translateY;
}; };
// -------------------------- events -------------------------- // // -------------------------- events -------------------------- //
// trigger handler methods for events /**
Draggabilly.prototype.handleEvent = function( event ) { * pointer start
var method = 'on' + event.type; * @param {Event} event
if ( this[ method ] ) { * @param {Event or Touch} pointer
this[ method ]( event ); */
} Draggabilly.prototype.pointerDown = function( event, pointer ) {
}; this._dragPointerDown( event, pointer );
// kludge to blur focused inputs in dragger
// returns the touch that we're keeping track of var focused = document.activeElement;
Draggabilly.prototype.getTouch = function( touches ) { if ( focused && focused.blur ) {
for ( var i=0, len = touches.length; i < len; i++ ) { focused.blur();
var touch = touches[i];
if ( touch.identifier === this.pointerIdentifier ) {
return touch;
}
}
};
// ----- start event ----- //
Draggabilly.prototype.onmousedown = function( event ) {
this.dragStart( event, event );
};
Draggabilly.prototype.ontouchstart = function( event ) {
// disregard additional touches
if ( this.isDragging ) {
return;
} }
// bind move and end events
this._bindPostStartEvents( event );
classie.add( this.element, 'is-pointer-down' );
this.dispatchEvent( 'pointerDown', event, [ pointer ] );
};
this.dragStart( event, event.changedTouches[0] ); /**
}; * drag move
* @param {Event} event
function setPointerPoint( point, pointer ) { * @param {Event or Touch} pointer
point.x = pointer.pageX !== undefined ? pointer.pageX : pointer.clientX; */
point.y = pointer.pageY !== undefined ? pointer.pageY : pointer.clientY; Draggabilly.prototype.pointerMove = function( event, pointer ) {
} var moveVector = this._dragPointerMove( event, pointer );
this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] );
this._dragMove( event, pointer, moveVector );
};
/** /**
* drag start * drag start
* @param {Event} event * @param {Event} event
* @param {Event or Touch} pointer * @param {Event or Touch} pointer
*/ */
Draggabilly.prototype.dragStart = function( event, pointer ) { Draggabilly.prototype.dragStart = function( event, pointer ) {
if ( !this.isEnabled ) { if ( !this.isEnabled ) {
return; return;
} }
if ( event.preventDefault ) {
event.preventDefault();
} else {
event.returnValue = false;
}
var isTouch = event.type === 'touchstart';
// save pointer identifier to match up touch events
this.pointerIdentifier = pointer.identifier;
this._getPosition(); this._getPosition();
this.measureContainment(); this.measureContainment();
// point where drag began
setPointerPoint( this.startPoint, pointer );
// position _when_ drag began // position _when_ drag began
this.startPosition.x = this.position.x; this.startPosition.x = this.position.x;
this.startPosition.y = this.position.y; this.startPosition.y = this.position.y;
// reset left/top style // reset left/top style
this.setLeftTop(); this.setLeftTop();
this.dragPoint.x = 0; this.dragPoint.x = 0;
this.dragPoint.y = 0; this.dragPoint.y = 0;
// bind move and end events
this._bindEvents({
events: isTouch ? [ 'touchmove', 'touchend', 'touchcancel' ] :
[ 'mousemove', 'mouseup' ],
// IE8 needs to be bound to document
node: event.preventDefault ? window : document
});
classie.add( this.element, 'is-dragging' );
// reset isDragging flag // reset isDragging flag
this.isDragging = true; this.isDragging = true;
classie.add( this.element, 'is-dragging' );
this.emitEvent( 'dragStart', [ this, event, pointer ] ); this.dispatchEvent( 'dragStart', event, [ pointer ] );
// start animation // start animation
this.animate(); this.animate();
}; };
Draggabilly.prototype._bindEvents = function( args ) {
for ( var i=0, len = args.events.length; i < len; i++ ) {
var event = args.events[i];
eventie.bind( args.node, event, this );
}
// save these arguments
this._boundEvents = args;
};
Draggabilly.prototype._unbindEvents = function() {
var args = this._boundEvents;
for ( var i=0, len = args.events.length; i < len; i++ ) {
var event = args.events[i];
eventie.unbind( args.node, event, this );
}
delete this._boundEvents;
};
Draggabilly.prototype.measureContainment = function() { Draggabilly.prototype.measureContainment = function() {
var containment = this.options.containment; var containment = this.options.containment;
if ( !containment ) { if ( !containment ) {
return; return;
...@@ -1198,7 +2062,7 @@ ...@@ -1198,7 +2062,7 @@
// use element if element // use element if element
var container = isElement( containment ) ? containment : var container = isElement( containment ) ? containment :
// fallback to querySelector if string // fallback to querySelector if string
typeof containment === 'string' ? document.querySelector( containment ) : typeof containment == 'string' ? document.querySelector( containment ) :
// otherwise just `true`, use the parent // otherwise just `true`, use the parent
this.element.parentNode; this.element.parentNode;
...@@ -1209,97 +2073,98 @@ ...@@ -1209,97 +2073,98 @@
x: elemRect.left - containerRect.left, x: elemRect.left - containerRect.left,
y: elemRect.top - containerRect.top y: elemRect.top - containerRect.top
}; };
}; };
// ----- move event ----- // // ----- move event ----- //
Draggabilly.prototype.onmousemove = function( event ) { /**
this.dragMove( event, event );
};
Draggabilly.prototype.ontouchmove = function( event ) {
var touch = this.getTouch( event.changedTouches );
if ( touch ) {
this.dragMove( event, touch );
}
};
/**
* drag move * drag move
* @param {Event} event * @param {Event} event
* @param {Event or Touch} pointer * @param {Event or Touch} pointer
*/ */
Draggabilly.prototype.dragMove = function( event, pointer ) { Draggabilly.prototype.dragMove = function( event, pointer, moveVector ) {
if ( !this.isEnabled ) {
return;
}
var dragX = moveVector.x;
var dragY = moveVector.y;
setPointerPoint( this.dragPoint, pointer ); var grid = this.options.grid;
this.dragPoint.x -= this.startPoint.x; var gridX = grid && grid[0];
this.dragPoint.y -= this.startPoint.y; var gridY = grid && grid[1];
if ( this.options.containment ) { dragX = applyGrid( dragX, gridX );
var relX = this.relativeStartPosition.x; dragY = applyGrid( dragY, gridY );
var relY = this.relativeStartPosition.y;
this.dragPoint.x = Math.max( this.dragPoint.x, -relX );
this.dragPoint.y = Math.max( this.dragPoint.y, -relY );
this.dragPoint.x = Math.min( this.dragPoint.x, this.containerSize.width - relX - this.size.width );
this.dragPoint.y = Math.min( this.dragPoint.y, this.containerSize.height - relY - this.size.height );
}
this.position.x = this.startPosition.x + this.dragPoint.x; dragX = this.containDrag( 'x', dragX, gridX );
this.position.y = this.startPosition.y + this.dragPoint.y; dragY = this.containDrag( 'y', dragY, gridY );
this.emitEvent( 'dragMove', [ this, event, pointer ] ); // constrain to axis
}; dragX = this.options.axis == 'y' ? 0 : dragX;
dragY = this.options.axis == 'x' ? 0 : dragY;
this.position.x = this.startPosition.x + dragX;
this.position.y = this.startPosition.y + dragY;
// set dragPoint properties
this.dragPoint.x = dragX;
this.dragPoint.y = dragY;
// ----- end event ----- // this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] );
};
Draggabilly.prototype.onmouseup = function( event ) { function applyGrid( value, grid, method ) {
this.dragEnd( event, event ); method = method || 'round';
}; return grid ? Math[ method ]( value / grid ) * grid : value;
}
Draggabilly.prototype.ontouchend = function( event ) { Draggabilly.prototype.containDrag = function( axis, drag, grid ) {
var touch = this.getTouch( event.changedTouches ); if ( !this.options.containment ) {
if ( touch ) { return drag;
this.dragEnd( event, touch );
} }
}; var measure = axis == 'x' ? 'width' : 'height';
/** var rel = this.relativeStartPosition[ axis ];
var min = applyGrid( -rel, grid, 'ceil' );
var max = this.containerSize[ measure ] - rel - this.size[ measure ];
max = applyGrid( max, grid, 'floor' );
return Math.min( max, Math.max( min, drag ) );
};
// ----- end event ----- //
/**
* pointer up
* @param {Event} event
* @param {Event or Touch} pointer
*/
Draggabilly.prototype.pointerUp = function( event, pointer ) {
classie.remove( this.element, 'is-pointer-down' );
this.dispatchEvent( 'pointerUp', event, [ pointer ] );
this._dragPointerUp( event, pointer );
};
/**
* drag end * drag end
* @param {Event} event * @param {Event} event
* @param {Event or Touch} pointer * @param {Event or Touch} pointer
*/ */
Draggabilly.prototype.dragEnd = function( event, pointer ) { Draggabilly.prototype.dragEnd = function( event, pointer ) {
if ( !this.isEnabled ) {
return;
}
this.isDragging = false; this.isDragging = false;
delete this.pointerIdentifier;
// use top left position when complete // use top left position when complete
if ( transformProperty ) { if ( transformProperty ) {
this.element.style[ transformProperty ] = ''; this.element.style[ transformProperty ] = '';
this.setLeftTop(); this.setLeftTop();
} }
// remove events
this._unbindEvents();
classie.remove( this.element, 'is-dragging' ); classie.remove( this.element, 'is-dragging' );
this.dispatchEvent( 'dragEnd', event, [ pointer ] );
this.emitEvent( 'dragEnd', [ this, event, pointer ] ); };
};
// ----- cancel event ----- //
// coerce to end event
Draggabilly.prototype.ontouchcancel = function( event ) {
var touch = this.getTouch( event.changedTouches );
this.dragEnd( event, touch );
};
// -------------------------- animation -------------------------- // // -------------------------- animation -------------------------- //
Draggabilly.prototype.animate = function() { Draggabilly.prototype.animate = function() {
// only render and animate if dragging // only render and animate if dragging
if ( !this.isDragging ) { if ( !this.isDragging ) {
return; return;
...@@ -1312,10 +2177,10 @@ ...@@ -1312,10 +2177,10 @@
_this.animate(); _this.animate();
}); });
}; };
// transform translate function // transform translate function
var translate = is3d ? var translate = is3d ?
function( x, y ) { function( x, y ) {
return 'translate3d( ' + x + 'px, ' + y + 'px, 0)'; return 'translate3d( ' + x + 'px, ' + y + 'px, 0)';
} : } :
...@@ -1324,53 +2189,65 @@ ...@@ -1324,53 +2189,65 @@
}; };
// left/top positioning // left/top positioning
Draggabilly.prototype.setLeftTop = function() { Draggabilly.prototype.setLeftTop = function() {
this.element.style.left = this.position.x + 'px'; this.element.style.left = this.position.x + 'px';
this.element.style.top = this.position.y + 'px'; this.element.style.top = this.position.y + 'px';
}; };
Draggabilly.prototype.positionDrag = transformProperty ? Draggabilly.prototype.positionDrag = transformProperty ?
function() { function() {
// position with transform // position with transform
this.element.style[ transformProperty ] = translate( this.dragPoint.x, this.dragPoint.y ); this.element.style[ transformProperty ] = translate( this.dragPoint.x, this.dragPoint.y );
} : Draggabilly.prototype.setLeftTop; } : Draggabilly.prototype.setLeftTop;
Draggabilly.prototype.enable = function() { // ----- staticClick ----- //
Draggabilly.prototype.staticClick = function( event, pointer ) {
this.dispatchEvent( 'staticClick', event, [ pointer ] );
};
// ----- methods ----- //
Draggabilly.prototype.enable = function() {
this.isEnabled = true; this.isEnabled = true;
}; };
Draggabilly.prototype.disable = function() { Draggabilly.prototype.disable = function() {
this.isEnabled = false; this.isEnabled = false;
if ( this.isDragging ) { if ( this.isDragging ) {
this.dragEnd(); this.dragEnd();
} }
}; };
Draggabilly.prototype.destroy = function() {
this.disable();
// reset styles
if ( transformProperty ) {
this.element.style[ transformProperty ] = '';
}
this.element.style.left = '';
this.element.style.top = '';
this.element.style.position = '';
// unbind handles
this.unbindHandles();
// remove jQuery data
if ( this.$element ) {
this.$element.removeData('draggabilly');
}
};
return Draggabilly; // ----- jQuery bridget ----- //
} // end definition // required for jQuery bridget
Draggabilly.prototype._init = noop;
// -------------------------- transport -------------------------- // if ( jQuery && jQuery.bridget ) {
jQuery.bridget( 'draggabilly', Draggabilly );
}
if ( typeof define === 'function' && define.amd ) { // ----- ----- //
// AMD
define('draggabilly', [ return Draggabilly;
'classie',
'EventEmitter', }));
'eventie',
'getStyleProperty',
'getSize'
],
draggabillyDefinition );
} else {
// browser global
window.Draggabilly = draggabillyDefinition(
window.classie,
window.EventEmitter,
window.eventie,
window.getStyleProperty,
window.getSize
);
}
})( window );
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment