UNPKG

@ovine/core

Version:

Build flexible admin system with json.

1,206 lines (1,205 loc) 41.9 kB
"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; });