@ovine/core
Version:
Build flexible admin system with json.
1,206 lines (1,205 loc) • 41.9 kB
JavaScript
"use strict";
/*!
* Draggabilly PACKAGED v2.3.0
* Make that shiz draggable
* https://draggabilly.desandro.com
* MIT license
*/
/**
* Bridget makes jQuery widgets
* v2.0.1
* MIT license
*/
/* jshint browser: true, strict: true, undef: true, unused: true */
;
(function (window, factory) {
// universal module definition
/*jshint strict: false */ /* globals define, module, require */
if (typeof define == 'function' && define.amd) {
// AMD
define('jquery-bridget/jquery-bridget', ['jquery'], function (jQuery) {
return factory(window, jQuery);
});
}
else if (typeof module == 'object' && module.exports) {
// CommonJS
module.exports = factory(window, require('jquery'));
}
else {
// browser global
window.jQueryBridget = factory(window, window.jQuery);
}
})(window, function factory(window, jQuery) {
'use strict';
// ----- utils ----- //
var arraySlice = Array.prototype.slice;
// helper function for logging errors
// $.error breaks jQuery chaining
var console = window.console;
var logError = typeof console == 'undefined'
? function () { }
: function (message) {
console.error(message);
};
// ----- jQueryBridget ----- //
function jQueryBridget(namespace, PluginClass, $) {
$ = $ || jQuery || window.jQuery;
if (!$) {
return;
}
// add option method -> $().plugin('option', {...})
if (!PluginClass.prototype.option) {
// option setter
PluginClass.prototype.option = function (opts) {
// bail out if not an object
if (!$.isPlainObject(opts)) {
return;
}
this.options = $.extend(true, this.options, opts);
};
}
// make jQuery plugin
$.fn[namespace] = function (arg0 /*, arg1 */) {
if (typeof arg0 == 'string') {
// method call $().plugin( 'methodName', { options } )
// shift arguments by 1
var args = arraySlice.call(arguments, 1);
return methodCall(this, arg0, args);
}
// just $().plugin({ options })
plainCall(this, arg0);
return this;
};
// $().plugin('methodName')
function methodCall($elems, methodName, args) {
var returnValue;
var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
$elems.each(function (i, elem) {
// get instance
var instance = $.data(elem, namespace);
if (!instance) {
logError(namespace + ' not initialized. Cannot call methods, i.e. ' + pluginMethodStr);
return;
}
var method = instance[methodName];
if (!method || methodName.charAt(0) == '_') {
logError(pluginMethodStr + ' is not a valid method');
return;
}
// apply method, get return value
var value = method.apply(instance, args);
// set return value if value is returned, use only first value
returnValue = returnValue === undefined ? value : returnValue;
});
return returnValue !== undefined ? returnValue : $elems;
}
function plainCall($elems, options) {
$elems.each(function (i, elem) {
var instance = $.data(elem, namespace);
if (instance) {
// set options & init
instance.option(options);
instance._init();
}
else {
// initialize new instance
instance = new PluginClass(elem, options);
$.data(elem, namespace, instance);
}
});
}
updateJQuery($);
}
// ----- updateJQuery ----- //
// set $.bridget for v1 backwards compatibility
function updateJQuery($) {
if (!$ || ($ && $.bridget)) {
return;
}
$.bridget = jQueryBridget;
}
updateJQuery(jQuery || window.jQuery);
// ----- ----- //
return jQueryBridget;
});
(function (window, factory) {
'use strict';
if (typeof define == 'function' && define.amd) {
// AMD
define('get-size/get-size', [], function () {
return factory();
});
}
else if (typeof module == 'object' && module.exports) {
// CommonJS
module.exports = factory();
}
else {
// browser global
window.getSize = factory();
}
})(window, function factory() {
'use strict';
// -------------------------- helpers -------------------------- //
// 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() { }
var logError = typeof console == 'undefined'
? noop
: function (message) {
console.error(message);
};
// -------------------------- measurements -------------------------- //
var measurements = [
'paddingLeft',
'paddingRight',
'paddingTop',
'paddingBottom',
'marginLeft',
'marginRight',
'marginTop',
'marginBottom',
'borderLeftWidth',
'borderRightWidth',
'borderTopWidth',
'borderBottomWidth',
];
var measurementsLength = measurements.length;
function getZeroSize() {
var size = {
width: 0,
height: 0,
innerWidth: 0,
innerHeight: 0,
outerWidth: 0,
outerHeight: 0,
};
for (var i = 0; i < measurementsLength; i++) {
var measurement = measurements[i];
size[measurement] = 0;
}
return size;
}
// -------------------------- getStyle -------------------------- //
/**
* getStyle, get style of element, check for Firefox bug
* https://bugzilla.mozilla.org/show_bug.cgi?id=548397
*/
function getStyle(elem) {
var style = getComputedStyle(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;
}
// -------------------------- setup -------------------------- //
var isSetup = false;
var isBoxSizeOuter;
/**
* setup
* check isBoxSizerOuter
* do on first getSize() rather than on page load for Firefox bug
*/
function setup() {
// setup once
if (isSetup) {
return;
}
isSetup = true;
// -------------------------- box sizing -------------------------- //
/**
* WebKit measures the outer-width on style.width on border-box elems
* IE & Firefox<29 measures the inner-width
*/
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.boxSizing = 'border-box';
var body = document.body || document.documentElement;
body.appendChild(div);
var style = getStyle(div);
getSize.isBoxSizeOuter = 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 = style.boxSizing == 'border-box');
// get all measurements
for (var i = 0; i < measurementsLength; i++) {
var measurement = measurements[i];
var value = style[measurement];
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;
}
return getSize;
});
(function (global, factory) {
// universal module definition
/* jshint strict: false */ /* globals define, module, window */
if (typeof define == 'function' && define.amd) {
// AMD - RequireJS
define('ev-emitter/ev-emitter', factory);
}
else if (typeof module == 'object' && module.exports) {
// CommonJS - Browserify, Webpack
module.exports = factory();
}
else {
// Browser globals
global.EvEmitter = factory();
}
})(typeof window != 'undefined' ? window : this, function () {
function EvEmitter() { }
var proto = EvEmitter.prototype;
proto.on = function (eventName, listener) {
if (!eventName || !listener) {
return;
}
// set events hash
var events = (this._events = this._events || {});
// set listeners array
var listeners = (events[eventName] = events[eventName] || []);
// only add once
if (listeners.indexOf(listener) == -1) {
listeners.push(listener);
}
return this;
};
proto.once = function (eventName, listener) {
if (!eventName || !listener) {
return;
}
// add event
this.on(eventName, listener);
// set once flag
// set onceEvents hash
var onceEvents = (this._onceEvents = this._onceEvents || {});
// set onceListeners object
var onceListeners = (onceEvents[eventName] = onceEvents[eventName] || {});
// set flag
onceListeners[listener] = true;
return this;
};
proto.off = function (eventName, listener) {
var listeners = this._events && this._events[eventName];
if (!listeners || !listeners.length) {
return;
}
var index = listeners.indexOf(listener);
if (index != -1) {
listeners.splice(index, 1);
}
return this;
};
proto.emitEvent = function (eventName, args) {
var listeners = this._events && this._events[eventName];
if (!listeners || !listeners.length) {
return;
}
// copy over to avoid interference if .off() in listener
listeners = listeners.slice(0);
args = args || [];
// once stuff
var onceListeners = this._onceEvents && this._onceEvents[eventName];
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
var isOnce = onceListeners && onceListeners[listener];
if (isOnce) {
// remove listener
// remove before trigger to prevent recursion
this.off(eventName, listener);
// unset once flag
delete onceListeners[listener];
}
// trigger listener
listener.apply(this, args);
}
return this;
};
proto.allOff = function () {
delete this._events;
delete this._onceEvents;
};
return EvEmitter;
});
(function (window, factory) {
// universal module definition
/* jshint strict: false */ /*global define, module, require */
if (typeof define == 'function' && define.amd) {
// AMD
define('unipointer/unipointer', ['ev-emitter/ev-emitter'], function (EvEmitter) {
return factory(window, EvEmitter);
});
}
else if (typeof module == 'object' && module.exports) {
// CommonJS
module.exports = factory(window, require('ev-emitter'));
}
else {
// browser global
window.Unipointer = factory(window, window.EvEmitter);
}
})(window, function factory(window, EvEmitter) {
function noop() { }
function Unipointer() { }
// inherit EvEmitter
var proto = (Unipointer.prototype = Object.create(EvEmitter.prototype));
proto.bindStartEvent = function (elem) {
this._bindStartEvent(elem, true);
};
proto.unbindStartEvent = function (elem) {
this._bindStartEvent(elem, false);
};
/**
* Add or remove start event
* @param {Boolean} isAdd - remove if falsey
*/
proto._bindStartEvent = function (elem, isAdd) {
// munge isAdd, default to true
isAdd = isAdd === undefined ? true : isAdd;
var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
// default to mouse events
var startEvent = 'mousedown';
if (window.PointerEvent) {
// Pointer Events
startEvent = 'pointerdown';
}
else if ('ontouchstart' in window) {
// Touch Events. iOS Safari
startEvent = 'touchstart';
}
elem[bindMethod](startEvent, this);
};
// trigger handler methods for events
proto.handleEvent = function (event) {
var method = 'on' + event.type;
if (this[method]) {
this[method](event);
}
};
// returns the touch that we're keeping track of
proto.getTouch = function (touches) {
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
if (touch.identifier == this.pointerIdentifier) {
return touch;
}
}
};
// ----- start event ----- //
proto.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);
};
proto.ontouchstart = function (event) {
this._pointerDown(event, event.changedTouches[0]);
};
proto.onpointerdown = function (event) {
this._pointerDown(event, event);
};
/**
* pointer start
* @param {Event} event
* @param {Event or Touch} pointer
*/
proto._pointerDown = function (event, pointer) {
// dismiss right click and other pointers
// button = 0 is okay, 1-4 not
if (event.button || this.isPointerDown) {
return;
}
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);
};
proto.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'],
};
proto._bindPostStartEvents = function (event) {
if (!event) {
return;
}
// get proper events to match start event
var events = postStartEvents[event.type];
// bind events to node
events.forEach(function (eventName) {
window.addEventListener(eventName, this);
}, this);
// save these arguments
this._boundPointerEvents = events;
};
proto._unbindPostStartEvents = function () {
// check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
if (!this._boundPointerEvents) {
return;
}
this._boundPointerEvents.forEach(function (eventName) {
window.removeEventListener(eventName, this);
}, this);
delete this._boundPointerEvents;
};
// ----- move event ----- //
proto.onmousemove = function (event) {
this._pointerMove(event, event);
};
proto.onpointermove = function (event) {
if (event.pointerId == this.pointerIdentifier) {
this._pointerMove(event, event);
}
};
proto.ontouchmove = function (event) {
var touch = this.getTouch(event.changedTouches);
if (touch) {
this._pointerMove(event, touch);
}
};
/**
* pointer move
* @param {Event} event
* @param {Event or Touch} pointer
* @private
*/
proto._pointerMove = function (event, pointer) {
this.pointerMove(event, pointer);
};
// public
proto.pointerMove = function (event, pointer) {
this.emitEvent('pointerMove', [event, pointer]);
};
// ----- end event ----- //
proto.onmouseup = function (event) {
this._pointerUp(event, event);
};
proto.onpointerup = function (event) {
if (event.pointerId == this.pointerIdentifier) {
this._pointerUp(event, event);
}
};
proto.ontouchend = function (event) {
var touch = this.getTouch(event.changedTouches);
if (touch) {
this._pointerUp(event, touch);
}
};
/**
* pointer up
* @param {Event} event
* @param {Event or Touch} pointer
* @private
*/
proto._pointerUp = function (event, pointer) {
this._pointerDone();
this.pointerUp(event, pointer);
};
// public
proto.pointerUp = function (event, pointer) {
this.emitEvent('pointerUp', [event, pointer]);
};
// ----- pointer done ----- //
// triggered on pointer up & pointer cancel
proto._pointerDone = function () {
this._pointerReset();
this._unbindPostStartEvents();
this.pointerDone();
};
proto._pointerReset = function () {
// reset properties
this.isPointerDown = false;
delete this.pointerIdentifier;
};
proto.pointerDone = noop;
// ----- pointer cancel ----- //
proto.onpointercancel = function (event) {
if (event.pointerId == this.pointerIdentifier) {
this._pointerCancel(event, event);
}
};
proto.ontouchcancel = function (event) {
var touch = this.getTouch(event.changedTouches);
if (touch) {
this._pointerCancel(event, touch);
}
};
/**
* pointer cancel
* @param {Event} event
* @param {Event or Touch} pointer
* @private
*/
proto._pointerCancel = function (event, pointer) {
this._pointerDone();
this.pointerCancel(event, pointer);
};
// public
proto.pointerCancel = function (event, pointer) {
this.emitEvent('pointerCancel', [event, pointer]);
};
// ----- ----- //
// utility function for getting x/y coords from event
Unipointer.getPointerPoint = function (pointer) {
return {
x: pointer.pageX,
y: pointer.pageY,
};
};
// ----- ----- //
return Unipointer;
});
(function (window, factory) {
// universal module definition
/*jshint strict: false */ /*globals define, module, require */
if (typeof define == 'function' && define.amd) {
// AMD
define('unidragger/unidragger', ['unipointer/unipointer'], function (Unipointer) {
return factory(window, Unipointer);
});
}
else if (typeof module == 'object' && module.exports) {
// CommonJS
module.exports = factory(window, require('unipointer'));
}
else {
// browser global
window.Unidragger = factory(window, window.Unipointer);
}
})(window, function factory(window, Unipointer) {
// -------------------------- Unidragger -------------------------- //
function Unidragger() { }
// inherit Unipointer & EvEmitter
var proto = (Unidragger.prototype = Object.create(Unipointer.prototype));
// ----- bind start ----- //
proto.bindHandles = function () {
this._bindHandles(true);
};
proto.unbindHandles = function () {
this._bindHandles(false);
};
/**
* Add or remove start event
* @param {Boolean} isAdd
*/
proto._bindHandles = function (isAdd) {
// munge isAdd, default to true
isAdd = isAdd === undefined ? true : isAdd;
// bind each handle
var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
var touchAction = isAdd ? this._touchActionValue : '';
for (var i = 0; i < this.handles.length; i++) {
var handle = this.handles[i];
this._bindStartEvent(handle, isAdd);
handle[bindMethod]('click', this);
// touch-action: none to override browser touch gestures. metafizzy/flickity#540
if (window.PointerEvent) {
handle.style.touchAction = touchAction;
}
}
};
// prototype so it can be overwriteable by Flickity
proto._touchActionValue = 'none';
// ----- start event ----- //
/**
* pointer start
* @param {Event} event
* @param {Event or Touch} pointer
*/
proto.pointerDown = function (event, pointer) {
var isOkay = this.okayPointerDown(event);
if (!isOkay) {
return;
}
// track start event position
this.pointerDownPointer = pointer;
event.preventDefault();
this.pointerDownBlur();
// bind move and end events
this._bindPostStartEvents(event);
this.emitEvent('pointerDown', [event, pointer]);
};
// nodes that have text fields
var cursorNodes = {
TEXTAREA: true,
INPUT: true,
SELECT: true,
OPTION: true,
};
// input types that do not have text fields
var clickTypes = {
radio: true,
checkbox: true,
button: true,
submit: true,
image: true,
file: true,
};
// dismiss inputs with text fields. flickity#403, flickity#404
proto.okayPointerDown = function (event) {
var isCursorNode = cursorNodes[event.target.nodeName];
var isClickType = clickTypes[event.target.type];
var isOkay = !isCursorNode || isClickType;
if (!isOkay) {
this._pointerReset();
}
return isOkay;
};
// kludge to blur previously focused input
proto.pointerDownBlur = function () {
var focused = document.activeElement;
// do not blur body for IE10, metafizzy/flickity#117
var canBlur = focused && focused.blur && focused != document.body;
if (canBlur) {
focused.blur();
}
};
// ----- move event ----- //
/**
* drag move
* @param {Event} event
* @param {Event or Touch} pointer
*/
proto.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
proto._dragPointerMove = function (event, pointer) {
var moveVector = {
x: pointer.pageX - this.pointerDownPointer.pageX,
y: pointer.pageY - this.pointerDownPointer.pageY,
};
// start drag if pointer has moved far enough to start drag
if (!this.isDragging && this.hasDragStarted(moveVector)) {
this._dragStart(event, pointer);
}
return moveVector;
};
// condition if pointer has moved far enough to start drag
proto.hasDragStarted = function (moveVector) {
return Math.abs(moveVector.x) > 3 || Math.abs(moveVector.y) > 3;
};
// ----- end event ----- //
/**
* pointer up
* @param {Event} event
* @param {Event or Touch} pointer
*/
proto.pointerUp = function (event, pointer) {
this.emitEvent('pointerUp', [event, pointer]);
this._dragPointerUp(event, pointer);
};
proto._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);
}
};
// -------------------------- drag -------------------------- //
// dragStart
proto._dragStart = function (event, pointer) {
this.isDragging = true;
// prevent clicks
this.isPreventingClicks = true;
this.dragStart(event, pointer);
};
proto.dragStart = function (event, pointer) {
this.emitEvent('dragStart', [event, pointer]);
};
// dragMove
proto._dragMove = function (event, pointer, moveVector) {
// do not drag if not dragging yet
if (!this.isDragging) {
return;
}
this.dragMove(event, pointer, moveVector);
};
proto.dragMove = function (event, pointer, moveVector) {
event.preventDefault();
this.emitEvent('dragMove', [event, pointer, moveVector]);
};
// dragEnd
proto._dragEnd = function (event, pointer) {
// set flags
this.isDragging = false;
// re-enable clicking async
setTimeout(function () {
delete this.isPreventingClicks;
}.bind(this));
this.dragEnd(event, pointer);
};
proto.dragEnd = function (event, pointer) {
this.emitEvent('dragEnd', [event, pointer]);
};
// ----- onclick ----- //
// handle all clicks and prevent clicks when dragging
proto.onclick = function (event) {
if (this.isPreventingClicks) {
event.preventDefault();
}
};
// ----- staticClick ----- //
// triggered after pointer down & up with no/tiny movement
proto._staticClick = function (event, pointer) {
// ignore emulated mouse up clicks
if (this.isIgnoringMouseUp && event.type == 'mouseup') {
return;
}
this.staticClick(event, pointer);
// set flag for emulated clicks 300ms after touchend
if (event.type != 'mouseup') {
this.isIgnoringMouseUp = true;
// reset flag after 300ms
setTimeout(function () {
delete this.isIgnoringMouseUp;
}.bind(this), 400);
}
};
proto.staticClick = function (event, pointer) {
this.emitEvent('staticClick', [event, pointer]);
};
// ----- utils ----- //
Unidragger.getPointerPoint = Unipointer.getPointerPoint;
// ----- ----- //
return Unidragger;
});
(function (window, factory) {
// universal module definition
/* jshint strict: false */ /* globals define */
if (typeof define == 'function' && define.amd) {
// AMD
define(['get-size/get-size', 'unidragger/unidragger'], function (getSize, Unidragger) {
return factory(window, getSize, Unidragger);
});
}
else if (typeof module == 'object' && module.exports) {
// CommonJS
module.exports = factory(window, require('get-size'), require('unidragger'));
}
else {
// browser global
window.Draggabilly = factory(window, window.getSize, window.Unidragger);
}
})(window, function factory(window, getSize, Unidragger) {
// -------------------------- helpers & variables -------------------------- //
// extend objects
function extend(a, b) {
for (var prop in b) {
a[prop] = b[prop];
}
return a;
}
function noop() { }
var jQuery = window.jQuery;
// -------------------------- -------------------------- //
function Draggabilly(element, options) {
// querySelector if string
this.element = typeof element == 'string' ? document.querySelector(element) : element;
if (jQuery) {
this.$element = jQuery(this.element);
}
// options
this.options = extend({}, this.constructor.defaults);
this.option(options);
this._create();
}
// inherit Unidragger methods
var proto = (Draggabilly.prototype = Object.create(Unidragger.prototype));
Draggabilly.defaults = {};
/**
* set options
* @param {Object} opts
*/
proto.option = function (opts) {
extend(this.options, opts);
};
// css position values that don't need to be set
var positionValues = {
relative: true,
absolute: true,
fixed: true,
};
proto._create = function () {
// properties
this.position = {};
this._getPosition();
this.startPoint = { x: 0, y: 0 };
this.dragPoint = { x: 0, y: 0 };
this.startPosition = extend({}, this.position);
// set relative positioning
var style = getComputedStyle(this.element);
if (!positionValues[style.position]) {
this.element.style.position = 'relative';
}
// events, bridge jQuery events from vanilla
this.on('pointerMove', this.onPointerMove);
this.on('pointerUp', this.onPointerUp);
this.enable();
this.setHandles();
};
/**
* set this.handles and bind start events to 'em
*/
proto.setHandles = function () {
this.handles = this.options.handle
? this.element.querySelectorAll(this.options.handle)
: [this.element];
this.bindHandles();
};
/**
* emits events via EvEmitter and jQuery events
* @param {String} type - name of event
* @param {Event} event - original event
* @param {Array} args - extra arguments
*/
proto.dispatchEvent = function (type, event, args) {
var emitArgs = [event].concat(args);
this.emitEvent(type, emitArgs);
this.dispatchJQueryEvent(type, event, args);
};
proto.dispatchJQueryEvent = function (type, event, args) {
var jquery = window.jQuery;
// trigger jQuery event
if (!jquery || !this.$element) {
return;
}
// create jQuery event
/* eslint-disable-next-line new-cap */
var $event = jquery.Event(event);
$event.type = type;
this.$element.trigger($event, args);
};
// -------------------------- position -------------------------- //
// get x/y position from style
proto._getPosition = function () {
var style = getComputedStyle(this.element);
var x = this._getPositionCoord(style.left, 'width');
var y = this._getPositionCoord(style.top, 'height');
// clean up 'auto' or other non-integer values
this.position.x = isNaN(x) ? 0 : x;
this.position.y = isNaN(y) ? 0 : y;
this._addTransformPosition(style);
};
proto._getPositionCoord = function (styleSide, measure) {
if (styleSide.indexOf('%') != -1) {
// convert percent into pixel for Safari, #75
var parentSize = getSize(this.element.parentNode);
// prevent not-in-DOM element throwing bug, #131
return !parentSize ? 0 : (parseFloat(styleSide) / 100) * parentSize[measure];
}
return parseInt(styleSide, 10);
};
// add transform: translate( x, y ) to position
proto._addTransformPosition = function (style) {
var transform = style.transform;
// bail out if value is 'none'
if (transform.indexOf('matrix') !== 0) {
return;
}
// split matrix(1, 0, 0, 1, x, y)
var matrixValues = transform.split(',');
// translate X value is in 12th or 4th position
var xIndex = transform.indexOf('matrix3d') === 0 ? 12 : 4;
var translateX = parseInt(matrixValues[xIndex], 10);
// translate Y value is in 13th or 5th position
var translateY = parseInt(matrixValues[xIndex + 1], 10);
this.position.x += translateX;
this.position.y += translateY;
};
// -------------------------- events -------------------------- //
proto.onPointerDown = function (event, pointer) {
this.element.classList.add('is-pointer-down');
this.dispatchJQueryEvent('pointerDown', event, [pointer]);
};
proto.pointerDown = function (event, pointer) {
var isOkay = this.okayPointerDown(event);
if (!isOkay || !this.isEnabled) {
this._pointerReset();
return;
}
// track start event position
// Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842
this.pointerDownPointer = {
pageX: pointer.pageX,
pageY: pointer.pageY,
};
event.preventDefault();
this.pointerDownBlur();
// bind move and end events
this._bindPostStartEvents(event);
this.element.classList.add('is-pointer-down');
this.dispatchEvent('pointerDown', event, [pointer]);
};
/**
* drag start
* @param {Event} event
* @param {[Event, Touch]} pointer
*/
proto.dragStart = function (event, pointer) {
if (!this.isEnabled) {
return;
}
this._getPosition();
this.measureContainment();
// position _when_ drag began
this.startPosition.x = this.position.x;
this.startPosition.y = this.position.y;
// reset left/top style
this.setLeftTop();
this.dragPoint.x = 0;
this.dragPoint.y = 0;
this.element.classList.add('is-dragging');
this.dispatchEvent('dragStart', event, [pointer]);
// start animation
this.animate();
};
proto.measureContainment = function () {
var container = this.getContainer();
if (!container) {
return;
}
var elemSize = getSize(this.element);
var containerSize = getSize(container);
var elemRect = this.element.getBoundingClientRect();
var containerRect = container.getBoundingClientRect();
var borderSizeX = containerSize.borderLeftWidth + containerSize.borderRightWidth;
var borderSizeY = containerSize.borderTopWidth + containerSize.borderBottomWidth;
var position = (this.relativeStartPosition = {
x: elemRect.left - (containerRect.left + containerSize.borderLeftWidth),
y: elemRect.top - (containerRect.top + containerSize.borderTopWidth),
});
this.containSize = {
width: containerSize.width - borderSizeX - position.x - elemSize.width,
height: containerSize.height - borderSizeY - position.y - elemSize.height,
};
};
proto.getContainer = function () {
var containment = this.options.containment;
if (!containment) {
return;
}
var isElement = containment instanceof HTMLElement;
// use as element
if (isElement) {
return containment;
}
// querySelector if string
if (typeof containment == 'string') {
return document.querySelector(containment);
}
// fallback to parent element
return this.element.parentNode;
};
// ----- move event ----- //
proto.onPointerMove = function (event, pointer, moveVector) {
this.dispatchJQueryEvent('pointerMove', event, [pointer, moveVector]);
};
/**
* drag move
* @param {Event} event
* @param {[Event, Touch]} pointer
* @param {Object} moveVector - x and y coordinates
*/
proto.dragMove = function (event, pointer, moveVector) {
if (!this.isEnabled) {
return;
}
var dragX = moveVector.x;
var dragY = moveVector.y;
var grid = this.options.grid;
var gridX = grid && grid[0];
var gridY = grid && grid[1];
dragX = applyGrid(dragX, gridX);
dragY = applyGrid(dragY, gridY);
dragX = this.containDrag('x', dragX, gridX);
dragY = this.containDrag('y', dragY, gridY);
// 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;
this.dispatchEvent('dragMove', event, [pointer, moveVector]);
};
function applyGrid(value, grid, method) {
method = method || 'round';
return grid ? Math[method](value / grid) * grid : value;
}
proto.containDrag = function (axis, drag, grid) {
if (!this.options.containment) {
return drag;
}
var measure = axis == 'x' ? 'width' : 'height';
var rel = this.relativeStartPosition[axis];
var min = applyGrid(-rel, grid, 'ceil');
var max = this.containSize[measure];
max = applyGrid(max, grid, 'floor');
return Math.max(min, Math.min(max, drag));
};
// ----- end event ----- //
/**
* pointer up
* @param {Event} event
* @param {[Event, Touch]} pointer
*/
proto.onPointerUp = function (event, pointer) {
this.element.classList.remove('is-pointer-down');
this.dispatchJQueryEvent('pointerUp', event, [pointer]);
};
/**
* drag end
* @param {Event} event
* @param {[Event, Touch]} pointer
*/
proto.dragEnd = function (event, pointer) {
if (!this.isEnabled) {
return;
}
// use top left position when complete
this.element.style.transform = '';
this.setLeftTop();
this.element.classList.remove('is-dragging');
this.dispatchEvent('dragEnd', event, [pointer]);
};
// -------------------------- animation -------------------------- //
proto.animate = function () {
// only render and animate if dragging
if (!this.isDragging) {
return;
}
this.positionDrag();
var _this = this;
requestAnimationFrame(function animateFrame() {
_this.animate();
});
};
// left/top positioning
proto.setLeftTop = function () {
this.element.style.left = this.position.x + 'px';
this.element.style.top = this.position.y + 'px';
};
proto.positionDrag = function () {
this.element.style.transform =
'translate3d( ' + this.dragPoint.x + 'px, ' + this.dragPoint.y + 'px, 0)';
};
// ----- staticClick ----- //
proto.staticClick = function (event, pointer) {
this.dispatchEvent('staticClick', event, [pointer]);
};
// ----- methods ----- //
/**
* @param {Number} x
* @param {Number} y
*/
proto.setPosition = function (x, y) {
this.position.x = x;
this.position.y = y;
this.setLeftTop();
};
proto.enable = function () {
this.isEnabled = true;
};
proto.disable = function () {
this.isEnabled = false;
if (this.isDragging) {
this.dragEnd();
}
};
proto.destroy = function () {
this.disable();
// reset styles
this.element.style.transform = '';
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');
}
};
// ----- jQuery bridget ----- //
// required for jQuery bridget
proto._init = noop;
if (jQuery && jQuery.bridget) {
jQuery.bridget('draggabilly', Draggabilly);
}
// ----- ----- //
return Draggabilly;
});