<% if current_user&.is_admin? %>
@@ -28,5 +30,7 @@
<%= keywords_and_topics(material) %>
+
+ <%= item_comment(collection_item) if defined? collection_item %>
<% end %>
diff --git a/test/controllers/collections_controller_test.rb b/test/controllers/collections_controller_test.rb
index 046632e8b..523b5edc1 100644
--- a/test/controllers/collections_controller_test.rb
+++ b/test/controllers/collections_controller_test.rb
@@ -189,6 +189,111 @@ class CollectionsControllerTest < ActionController::TestCase
assert_redirected_to collection_path(assigns(:collection))
end
+ test "should add items to collection" do
+ sign_in users(:regular_user)
+ collection = collections(:with_resources)
+ assert_difference('CollectionItem.count', 3) do
+ assert_difference('collection.events.count', 1) do
+ assert_difference('collection.materials.count', 2) do
+ patch :update, params: { id: collection.id, collection: { items_attributes: {
+ '0' => { resource_type: 'Material', resource_id: materials(:biojs).id, order: 2, comment: 'hello' },
+ '1' => { resource_type: 'Event', resource_id: events(:one).id, order: 1, comment: 'hello!' },
+ '2' => { resource_type: 'Material', resource_id: materials(:interpro).id, order: 1, comment: 'hello!!' }
+ } } }
+
+ mats = assigns(:collection).material_items
+ assert_equal 2, mats.length
+ assert_equal 1, mats[0].order
+ assert_equal 'hello!!', mats[0].comment
+ assert_equal materials(:interpro), mats[0].resource
+ assert_equal 2, mats[1].order
+ assert_equal 'hello', mats[1].comment
+ assert_equal materials(:biojs), mats[1].resource
+
+ events = assigns(:collection).event_items
+ assert_equal 1, events.length
+ assert_equal 1, events[0].order
+ assert_equal 'hello!', events[0].comment
+ assert_equal events(:one), events[0].resource
+ end
+ end
+ end
+ end
+
+ test "should remove items from collection" do
+ sign_in users(:regular_user)
+ collection = collections(:with_resources)
+ ci1 = collection.items.create!(resource: materials(:biojs), order: 2, comment: 'hello')
+ ci2 = collection.items.create!(resource: materials(:interpro), order: 1, comment: 'hello!!')
+ ci3 = collection.items.create!(resource: events(:one), order: 1, comment: 'hello!')
+
+
+ assert_difference('CollectionItem.count', -1) do
+ assert_difference('collection.materials.count', -1) do
+ assert_no_difference('collection.events.count') do
+ patch :update, params: { id: collection.id, collection: { items_attributes: {
+ '0' => { id: ci1.id, resource_type: 'Material', resource_id: materials(:biojs).id, order: 2, comment: 'hello' },
+ '1' => { id: ci3.id, resource_type: 'Event', resource_id: events(:one).id, order: 1, comment: 'hello!' },
+ '2' => { id: ci2.id, resource_type: 'Material', resource_id: materials(:interpro).id, order: 1, comment: 'hello!!', _destroy: '1' }
+ } } }
+
+ mats = assigns(:collection).material_items
+ assert_equal ci1.id, mats[0].id
+ assert_equal 1, mats.length
+ assert_equal 1, mats[0].order
+ assert_equal 'hello', mats[0].comment
+ assert_equal materials(:biojs), mats[0].resource
+
+ events = assigns(:collection).event_items
+ assert_equal 1, events.length
+ assert_equal ci3.id, events[0].id
+ assert_equal 1, events[0].order
+ assert_equal 'hello!', events[0].comment
+ assert_equal events(:one), events[0].resource
+ end
+ end
+ end
+ end
+
+ test "should modify items in collection" do
+ sign_in users(:regular_user)
+ collection = collections(:with_resources)
+ ci1 = collection.items.create!(resource: materials(:biojs), order: 2, comment: 'hello')
+ ci2 = collection.items.create!(resource: materials(:interpro), order: 1, comment: 'hello!!')
+ ci3 = collection.items.create!(resource: events(:one), order: 1, comment: 'hello!')
+
+ assert_no_difference('CollectionItem.count') do
+ assert_no_difference('collection.materials.count') do
+ assert_no_difference('collection.events.count') do
+ patch :update, params: { id: collection.id, collection: { items_attributes: {
+ '0' => { id: ci1.id, resource_type: 'Material', resource_id: materials(:biojs).id, order: 1, comment: 'hello world' },
+ '1' => { id: ci3.id, resource_type: 'Event', resource_id: events(:one).id, order: 1, comment: 'hello world!' },
+ '2' => { id: ci2.id, resource_type: 'Material', resource_id: materials(:interpro).id, order: 2, comment: 'hello world!!' }
+ } } }
+
+ mats = assigns(:collection).material_items
+ assert_equal 2, mats.length
+ assert_equal ci1.id, mats[0].id
+ assert_equal 1, mats[0].order
+ assert_equal 'hello world', mats[0].comment
+ assert_equal materials(:biojs), mats[0].resource
+ assert_equal ci2.id, mats[1].id
+ assert_equal 2, mats[1].order
+ assert_equal 'hello world!!', mats[1].comment
+ assert_equal materials(:interpro), mats[1].resource
+
+ events = assigns(:collection).event_items
+ assert_equal 1, events.length
+ assert_equal ci3.id, events[0].id
+ assert_equal 1, events[0].order
+ assert_equal 'hello world!', events[0].comment
+ assert_equal events(:one), events[0].resource
+ end
+ end
+ end
+ end
+
+
#UPDATE_CURATE TEST
test 'should add and remove elements' do
sign_in @collection.user
@@ -354,6 +459,16 @@ class CollectionsControllerTest < ActionController::TestCase
end
#API Actions
+ test "should add materials to collection" do
+ sign_in users(:regular_user)
+ collection = collections(:with_resources)
+ assert_difference('CollectionItem.count', 2) do
+ assert_difference('collection.materials.count', 2) do
+ patch :update, params: { collection: { material_ids: [materials(:biojs), materials(:interpro)] }, id: collection.id }
+ end
+ end
+ end
+
test "should remove materials from collection" do
sign_in users(:regular_user)
collection = collections(:with_resources)
@@ -495,4 +610,75 @@ class CollectionsControllerTest < ActionController::TestCase
end
assert_response :forbidden
end
+
+ test 'should render collection items in order' do
+ materials = [materials(:good_material), materials(:biojs), materials(:interpro)]
+ events = [events(:two), events(:one)]
+ @collection.items.create!(resource: materials[0], order: 2, comment: 'A good material')
+ @collection.items.create!(resource: materials[1], order: 1, comment: 'Start here')
+ @collection.items.create!(resource: materials[2], order: 3, comment: 'End here')
+ @collection.items.create!(resource: events[0], order: 2, comment: 'End here')
+ @collection.items.create!(resource: events[1], order: 1, comment: 'Start here')
+
+ get :show, params: { id: @collection }
+
+ assert_response :success
+
+ assert_select '#materials ul li:nth-child(1) .link-overlay' do
+ assert_select 'h4', text: 'BioJS'
+ assert_select '.collection-item-comment', text: 'Start here'
+ assert_select '.collection-item-order-badge', text: '1'
+ end
+
+ assert_select '#materials ul li:nth-child(2) .link-overlay' do
+ assert_select 'h4', text: 'Training Material Example'
+ assert_select '.collection-item-comment', text: 'A good material'
+ assert_select '.collection-item-order-badge', text: '2'
+ end
+
+ assert_select '#materials ul li:nth-child(3) .link-overlay' do
+ assert_select 'h4', text: 'InterPro'
+ assert_select '.collection-item-comment', text: 'End here'
+ assert_select '.collection-item-order-badge', text: '3'
+ end
+
+ assert_select '#events ul li:nth-child(1) .link-overlay' do
+ assert_select 'h4', text: 'event one'
+ assert_select '.collection-item-comment', text: 'Start here'
+ assert_select '.collection-item-order-badge', text: '1'
+ end
+
+ assert_select '#events ul li:nth-child(2) .link-overlay' do
+ assert_select 'h4', text: 'event two'
+ assert_select '.collection-item-comment', text: 'End here'
+ assert_select '.collection-item-order-badge', text: '2'
+ end
+ end
+
+ test 'should render collection items in order as json-api' do
+ materials = [materials(:good_material), materials(:biojs), materials(:interpro)]
+ events = [events(:two), events(:one)]
+ @collection.items.create!(resource: materials[0], order: 2, comment: 'A good material')
+ @collection.items.create!(resource: materials[1], order: 1, comment: 'Start here')
+ @collection.items.create!(resource: materials[2], order: 3, comment: 'End here')
+ @collection.items.create!(resource: events[0], order: 2, comment: 'End here')
+ @collection.items.create!(resource: events[1], order: 1, comment: 'Start here')
+
+ get :show, params: { id: @collection, format: :json_api }
+
+ assert_response :success
+ assert assigns(:collection)
+ assert_valid_json_api_response
+
+ body = nil
+ assert_nothing_raised do
+ body = JSON.parse(response.body)
+ end
+
+ response_materials = body.dig('data', 'relationships', 'materials', 'data')
+ assert_equal [materials[1].id, materials[0].id, materials[2].id], response_materials.map { |m| m['id'].to_i }
+
+ response_events = body.dig('data', 'relationships', 'events', 'data')
+ assert_equal [events[1].id, events[0].id], response_events.map { |e| e['id'].to_i }
+ end
end
diff --git a/test/models/collection_test.rb b/test/models/collection_test.rb
index 364819d88..40252b65e 100644
--- a/test/models/collection_test.rb
+++ b/test/models/collection_test.rb
@@ -166,4 +166,18 @@ def solr_index
assert_equal 'Collection Title', collection.title
assert_equal 'http://image.host/another_image.png', collection.image_url
end
+
+ test 'should normalize order of items' do
+ collection = collections(:secret_collection)
+ collection.items.create!(resource: materials(:biojs), order: 2)
+ collection.items.create!(resource: materials(:interpro), order: 3)
+ collection.items.create!(resource: events(:one), order: 42)
+ collection.items.create!(resource: events(:two), order: 13)
+ collection.save!
+
+ assert_equal [1, 2], collection.material_items.pluck(:order)
+ assert_equal [materials(:biojs), materials(:interpro)], collection.material_items.map(&:resource)
+ assert_equal [1, 2], collection.event_items.pluck(:order)
+ assert_equal [events(:two), events(:one)], collection.event_items.map(&:resource)
+ end
end
diff --git a/vendor/assets/javascripts/sortable.js b/vendor/assets/javascripts/sortable.js
new file mode 100644
index 000000000..51a82d7b3
--- /dev/null
+++ b/vendor/assets/javascripts/sortable.js
@@ -0,0 +1,5495 @@
+// From: https://github.com/Shopify/draggable
+// MIT License
+//
+// Copyright (c) 2018 Shopify
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+(function webpackUniversalModuleDefinition(root, factory) {
+ if(typeof exports === 'object' && typeof module === 'object')
+ module.exports = factory();
+ else if(typeof define === 'function' && define.amd)
+ define("Sortable", [], factory);
+ else if(typeof exports === 'object')
+ exports["Sortable"] = factory();
+ else
+ root["Sortable"] = factory();
+})(window, function() {
+ return /******/ (function(modules) { // webpackBootstrap
+ /******/ // The module cache
+ /******/ var installedModules = {};
+ /******/
+ /******/ // The require function
+ /******/ function __webpack_require__(moduleId) {
+ /******/
+ /******/ // Check if module is in cache
+ /******/ if(installedModules[moduleId]) {
+ /******/ return installedModules[moduleId].exports;
+ /******/ }
+ /******/ // Create a new module (and put it into the cache)
+ /******/ var module = installedModules[moduleId] = {
+ /******/ i: moduleId,
+ /******/ l: false,
+ /******/ exports: {}
+ /******/ };
+ /******/
+ /******/ // Execute the module function
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+ /******/
+ /******/ // Flag the module as loaded
+ /******/ module.l = true;
+ /******/
+ /******/ // Return the exports of the module
+ /******/ return module.exports;
+ /******/ }
+ /******/
+ /******/
+ /******/ // expose the modules object (__webpack_modules__)
+ /******/ __webpack_require__.m = modules;
+ /******/
+ /******/ // expose the module cache
+ /******/ __webpack_require__.c = installedModules;
+ /******/
+ /******/ // define getter function for harmony exports
+ /******/ __webpack_require__.d = function(exports, name, getter) {
+ /******/ if(!__webpack_require__.o(exports, name)) {
+ /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
+ /******/ }
+ /******/ };
+ /******/
+ /******/ // define __esModule on exports
+ /******/ __webpack_require__.r = function(exports) {
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+ /******/ }
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
+ /******/ };
+ /******/
+ /******/ // create a fake namespace object
+ /******/ // mode & 1: value is a module id, require it
+ /******/ // mode & 2: merge all properties of value into the ns
+ /******/ // mode & 4: return value when already ns object
+ /******/ // mode & 8|1: behave like require
+ /******/ __webpack_require__.t = function(value, mode) {
+ /******/ if(mode & 1) value = __webpack_require__(value);
+ /******/ if(mode & 8) return value;
+ /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+ /******/ var ns = Object.create(null);
+ /******/ __webpack_require__.r(ns);
+ /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+ /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+ /******/ return ns;
+ /******/ };
+ /******/
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
+ /******/ __webpack_require__.n = function(module) {
+ /******/ var getter = module && module.__esModule ?
+ /******/ function getDefault() { return module['default']; } :
+ /******/ function getModuleExports() { return module; };
+ /******/ __webpack_require__.d(getter, 'a', getter);
+ /******/ return getter;
+ /******/ };
+ /******/
+ /******/ // Object.prototype.hasOwnProperty.call
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+ /******/
+ /******/ // __webpack_public_path__
+ /******/ __webpack_require__.p = "";
+ /******/
+ /******/
+ /******/ // Load entry module and return exports
+ /******/ return __webpack_require__(__webpack_require__.s = 48);
+ /******/ })
+ /************************************************************************/
+ /******/ ([
+ /* 0 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _SensorEvent = __webpack_require__(19);
+
+ Object.keys(_SensorEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _SensorEvent[key];
+ }
+ });
+ });
+
+ /***/ }),
+ /* 1 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _Sensor = __webpack_require__(22);
+
+ var _Sensor2 = _interopRequireDefault(_Sensor);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _Sensor2.default;
+
+ /***/ }),
+ /* 2 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _closest = __webpack_require__(30);
+
+ Object.defineProperty(exports, 'closest', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_closest).default;
+ }
+ });
+
+ var _requestNextAnimationFrame = __webpack_require__(28);
+
+ Object.defineProperty(exports, 'requestNextAnimationFrame', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_requestNextAnimationFrame).default;
+ }
+ });
+
+ var _distance = __webpack_require__(26);
+
+ Object.defineProperty(exports, 'distance', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_distance).default;
+ }
+ });
+
+ var _touchCoords = __webpack_require__(24);
+
+ Object.defineProperty(exports, 'touchCoords', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_touchCoords).default;
+ }
+ });
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ /***/ }),
+ /* 3 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _AbstractEvent = __webpack_require__(46);
+
+ var _AbstractEvent2 = _interopRequireDefault(_AbstractEvent);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _AbstractEvent2.default;
+
+ /***/ }),
+ /* 4 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _AbstractPlugin = __webpack_require__(39);
+
+ var _AbstractPlugin2 = _interopRequireDefault(_AbstractPlugin);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _AbstractPlugin2.default;
+
+ /***/ }),
+ /* 5 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _Sensor = __webpack_require__(1);
+
+ Object.defineProperty(exports, 'Sensor', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_Sensor).default;
+ }
+ });
+
+ var _MouseSensor = __webpack_require__(21);
+
+ Object.defineProperty(exports, 'MouseSensor', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_MouseSensor).default;
+ }
+ });
+
+ var _TouchSensor = __webpack_require__(18);
+
+ Object.defineProperty(exports, 'TouchSensor', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_TouchSensor).default;
+ }
+ });
+
+ var _DragSensor = __webpack_require__(16);
+
+ Object.defineProperty(exports, 'DragSensor', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_DragSensor).default;
+ }
+ });
+
+ var _ForceTouchSensor = __webpack_require__(14);
+
+ Object.defineProperty(exports, 'ForceTouchSensor', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_ForceTouchSensor).default;
+ }
+ });
+
+ var _SensorEvent = __webpack_require__(0);
+
+ Object.keys(_SensorEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _SensorEvent[key];
+ }
+ });
+ });
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ /***/ }),
+ /* 6 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _Announcement = __webpack_require__(41);
+
+ Object.defineProperty(exports, 'Announcement', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_Announcement).default;
+ }
+ });
+ Object.defineProperty(exports, 'defaultAnnouncementOptions', {
+ enumerable: true,
+ get: function () {
+ return _Announcement.defaultOptions;
+ }
+ });
+
+ var _Focusable = __webpack_require__(38);
+
+ Object.defineProperty(exports, 'Focusable', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_Focusable).default;
+ }
+ });
+
+ var _Mirror = __webpack_require__(36);
+
+ Object.defineProperty(exports, 'Mirror', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_Mirror).default;
+ }
+ });
+ Object.defineProperty(exports, 'defaultMirrorOptions', {
+ enumerable: true,
+ get: function () {
+ return _Mirror.defaultOptions;
+ }
+ });
+
+ var _Scrollable = __webpack_require__(32);
+
+ Object.defineProperty(exports, 'Scrollable', {
+ enumerable: true,
+ get: function () {
+ return _interopRequireDefault(_Scrollable).default;
+ }
+ });
+ Object.defineProperty(exports, 'defaultScrollableOptions', {
+ enumerable: true,
+ get: function () {
+ return _Scrollable.defaultOptions;
+ }
+ });
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ /***/ }),
+ /* 7 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _DraggableEvent = __webpack_require__(42);
+
+ Object.keys(_DraggableEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _DraggableEvent[key];
+ }
+ });
+ });
+
+ /***/ }),
+ /* 8 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _DragEvent = __webpack_require__(43);
+
+ Object.keys(_DragEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _DragEvent[key];
+ }
+ });
+ });
+
+ /***/ }),
+ /* 9 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _SortableEvent = __webpack_require__(47);
+
+ Object.keys(_SortableEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _SortableEvent[key];
+ }
+ });
+ });
+
+ /***/ }),
+ /* 10 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ /**
+ * The Emitter is a simple emitter class that provides you with `on()`, `off()` and `trigger()` methods
+ * @class Emitter
+ * @module Emitter
+ */
+ class Emitter {
+ constructor() {
+ this.callbacks = {};
+ }
+
+ /**
+ * Registers callbacks by event name
+ * @param {String} type
+ * @param {...Function} callbacks
+ */
+ on(type, ...callbacks) {
+ if (!this.callbacks[type]) {
+ this.callbacks[type] = [];
+ }
+
+ this.callbacks[type].push(...callbacks);
+
+ return this;
+ }
+
+ /**
+ * Unregisters callbacks by event name
+ * @param {String} type
+ * @param {Function} callback
+ */
+ off(type, callback) {
+ if (!this.callbacks[type]) {
+ return null;
+ }
+
+ const copy = this.callbacks[type].slice(0);
+
+ for (let i = 0; i < copy.length; i++) {
+ if (callback === copy[i]) {
+ this.callbacks[type].splice(i, 1);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Triggers event callbacks by event object
+ * @param {AbstractEvent} event
+ */
+ trigger(event) {
+ if (!this.callbacks[event.type]) {
+ return null;
+ }
+
+ const callbacks = [...this.callbacks[event.type]];
+ const caughtErrors = [];
+
+ for (let i = callbacks.length - 1; i >= 0; i--) {
+ const callback = callbacks[i];
+
+ try {
+ callback(event);
+ } catch (error) {
+ caughtErrors.push(error);
+ }
+ }
+
+ if (caughtErrors.length) {
+ /* eslint-disable no-console */
+ console.error(`Draggable caught errors while triggering '${event.type}'`, caughtErrors);
+ /* eslint-disable no-console */
+ }
+
+ return this;
+ }
+ }
+ exports.default = Emitter;
+
+ /***/ }),
+ /* 11 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _Emitter = __webpack_require__(10);
+
+ var _Emitter2 = _interopRequireDefault(_Emitter);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _Emitter2.default;
+
+ /***/ }),
+ /* 12 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.defaultOptions = undefined;
+
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+ var _utils = __webpack_require__(2);
+
+ var _Plugins = __webpack_require__(6);
+
+ var _Emitter = __webpack_require__(11);
+
+ var _Emitter2 = _interopRequireDefault(_Emitter);
+
+ var _Sensors = __webpack_require__(5);
+
+ var _DraggableEvent = __webpack_require__(7);
+
+ var _DragEvent = __webpack_require__(8);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onDragStart = Symbol('onDragStart');
+ const onDragMove = Symbol('onDragMove');
+ const onDragStop = Symbol('onDragStop');
+ const onDragPressure = Symbol('onDragPressure');
+
+ /**
+ * @const {Object} defaultAnnouncements
+ * @const {Function} defaultAnnouncements['drag:start']
+ * @const {Function} defaultAnnouncements['drag:stop']
+ */
+ const defaultAnnouncements = {
+ 'drag:start': event => `Picked up ${event.source.textContent.trim() || event.source.id || 'draggable element'}`,
+ 'drag:stop': event => `Released ${event.source.textContent.trim() || event.source.id || 'draggable element'}`
+ };
+
+ const defaultClasses = {
+ 'container:dragging': 'draggable-container--is-dragging',
+ 'source:dragging': 'draggable-source--is-dragging',
+ 'source:placed': 'draggable-source--placed',
+ 'container:placed': 'draggable-container--placed',
+ 'body:dragging': 'draggable--is-dragging',
+ 'draggable:over': 'draggable--over',
+ 'container:over': 'draggable-container--over',
+ 'source:original': 'draggable--original',
+ mirror: 'draggable-mirror'
+ };
+
+ const defaultOptions = exports.defaultOptions = {
+ draggable: '.draggable-source',
+ handle: null,
+ delay: {},
+ distance: 0,
+ placedTimeout: 800,
+ plugins: [],
+ sensors: [],
+ exclude: {
+ plugins: [],
+ sensors: []
+ }
+ };
+
+ /**
+ * This is the core draggable library that does the heavy lifting
+ * @class Draggable
+ * @module Draggable
+ */
+ class Draggable {
+
+ /**
+ * Draggable constructor.
+ * @constructs Draggable
+ * @param {HTMLElement[]|NodeList|HTMLElement} containers - Draggable containers
+ * @param {Object} options - Options for draggable
+ */
+
+ /**
+ * Default plugins draggable uses
+ * @static
+ * @property {Object} Plugins
+ * @property {Announcement} Plugins.Announcement
+ * @property {Focusable} Plugins.Focusable
+ * @property {Mirror} Plugins.Mirror
+ * @property {Scrollable} Plugins.Scrollable
+ * @type {Object}
+ */
+ constructor(containers = [document.body], options = {}) {
+ /**
+ * Draggable containers
+ * @property containers
+ * @type {HTMLElement[]}
+ */
+ if (containers instanceof NodeList || containers instanceof Array) {
+ this.containers = [...containers];
+ } else if (containers instanceof HTMLElement) {
+ this.containers = [containers];
+ } else {
+ throw new Error('Draggable containers are expected to be of type `NodeList`, `HTMLElement[]` or `HTMLElement`');
+ }
+
+ this.options = _extends({}, defaultOptions, options, {
+ classes: _extends({}, defaultClasses, options.classes || {}),
+ announcements: _extends({}, defaultAnnouncements, options.announcements || {}),
+ exclude: {
+ plugins: options.exclude && options.exclude.plugins || [],
+ sensors: options.exclude && options.exclude.sensors || []
+ }
+ });
+
+ /**
+ * Draggables event emitter
+ * @property emitter
+ * @type {Emitter}
+ */
+ this.emitter = new _Emitter2.default();
+
+ /**
+ * Current drag state
+ * @property dragging
+ * @type {Boolean}
+ */
+ this.dragging = false;
+
+ /**
+ * Active plugins
+ * @property plugins
+ * @type {Plugin[]}
+ */
+ this.plugins = [];
+
+ /**
+ * Active sensors
+ * @property sensors
+ * @type {Sensor[]}
+ */
+ this.sensors = [];
+
+ this[onDragStart] = this[onDragStart].bind(this);
+ this[onDragMove] = this[onDragMove].bind(this);
+ this[onDragStop] = this[onDragStop].bind(this);
+ this[onDragPressure] = this[onDragPressure].bind(this);
+
+ document.addEventListener('drag:start', this[onDragStart], true);
+ document.addEventListener('drag:move', this[onDragMove], true);
+ document.addEventListener('drag:stop', this[onDragStop], true);
+ document.addEventListener('drag:pressure', this[onDragPressure], true);
+
+ const defaultPlugins = Object.values(Draggable.Plugins).filter(Plugin => !this.options.exclude.plugins.includes(Plugin));
+ const defaultSensors = Object.values(Draggable.Sensors).filter(sensor => !this.options.exclude.sensors.includes(sensor));
+
+ this.addPlugin(...[...defaultPlugins, ...this.options.plugins]);
+ this.addSensor(...[...defaultSensors, ...this.options.sensors]);
+
+ const draggableInitializedEvent = new _DraggableEvent.DraggableInitializedEvent({
+ draggable: this
+ });
+
+ this.on('mirror:created', ({ mirror }) => this.mirror = mirror);
+ this.on('mirror:destroy', () => this.mirror = null);
+
+ this.trigger(draggableInitializedEvent);
+ }
+
+ /**
+ * Destroys Draggable instance. This removes all internal event listeners and
+ * deactivates sensors and plugins
+ */
+
+
+ /**
+ * Default sensors draggable uses
+ * @static
+ * @property {Object} Sensors
+ * @property {MouseSensor} Sensors.MouseSensor
+ * @property {TouchSensor} Sensors.TouchSensor
+ * @type {Object}
+ */
+ destroy() {
+ document.removeEventListener('drag:start', this[onDragStart], true);
+ document.removeEventListener('drag:move', this[onDragMove], true);
+ document.removeEventListener('drag:stop', this[onDragStop], true);
+ document.removeEventListener('drag:pressure', this[onDragPressure], true);
+
+ const draggableDestroyEvent = new _DraggableEvent.DraggableDestroyEvent({
+ draggable: this
+ });
+
+ this.trigger(draggableDestroyEvent);
+
+ this.removePlugin(...this.plugins.map(plugin => plugin.constructor));
+ this.removeSensor(...this.sensors.map(sensor => sensor.constructor));
+ }
+
+ /**
+ * Adds plugin to this draggable instance. This will end up calling the attach method of the plugin
+ * @param {...typeof Plugin} plugins - Plugins that you want attached to draggable
+ * @return {Draggable}
+ * @example draggable.addPlugin(CustomA11yPlugin, CustomMirrorPlugin)
+ */
+ addPlugin(...plugins) {
+ const activePlugins = plugins.map(Plugin => new Plugin(this));
+
+ activePlugins.forEach(plugin => plugin.attach());
+ this.plugins = [...this.plugins, ...activePlugins];
+
+ return this;
+ }
+
+ /**
+ * Removes plugins that are already attached to this draggable instance. This will end up calling
+ * the detach method of the plugin
+ * @param {...typeof Plugin} plugins - Plugins that you want detached from draggable
+ * @return {Draggable}
+ * @example draggable.removePlugin(MirrorPlugin, CustomMirrorPlugin)
+ */
+ removePlugin(...plugins) {
+ const removedPlugins = this.plugins.filter(plugin => plugins.includes(plugin.constructor));
+
+ removedPlugins.forEach(plugin => plugin.detach());
+ this.plugins = this.plugins.filter(plugin => !plugins.includes(plugin.constructor));
+
+ return this;
+ }
+
+ /**
+ * Adds sensors to this draggable instance. This will end up calling the attach method of the sensor
+ * @param {...typeof Sensor} sensors - Sensors that you want attached to draggable
+ * @return {Draggable}
+ * @example draggable.addSensor(ForceTouchSensor, CustomSensor)
+ */
+ addSensor(...sensors) {
+ const activeSensors = sensors.map(Sensor => new Sensor(this.containers, this.options));
+
+ activeSensors.forEach(sensor => sensor.attach());
+ this.sensors = [...this.sensors, ...activeSensors];
+
+ return this;
+ }
+
+ /**
+ * Removes sensors that are already attached to this draggable instance. This will end up calling
+ * the detach method of the sensor
+ * @param {...typeof Sensor} sensors - Sensors that you want attached to draggable
+ * @return {Draggable}
+ * @example draggable.removeSensor(TouchSensor, DragSensor)
+ */
+ removeSensor(...sensors) {
+ const removedSensors = this.sensors.filter(sensor => sensors.includes(sensor.constructor));
+
+ removedSensors.forEach(sensor => sensor.detach());
+ this.sensors = this.sensors.filter(sensor => !sensors.includes(sensor.constructor));
+
+ return this;
+ }
+
+ /**
+ * Adds container to this draggable instance
+ * @param {...HTMLElement} containers - Containers you want to add to draggable
+ * @return {Draggable}
+ * @example draggable.addContainer(document.body)
+ */
+ addContainer(...containers) {
+ this.containers = [...this.containers, ...containers];
+ this.sensors.forEach(sensor => sensor.addContainer(...containers));
+ return this;
+ }
+
+ /**
+ * Removes container from this draggable instance
+ * @param {...HTMLElement} containers - Containers you want to remove from draggable
+ * @return {Draggable}
+ * @example draggable.removeContainer(document.body)
+ */
+ removeContainer(...containers) {
+ this.containers = this.containers.filter(container => !containers.includes(container));
+ this.sensors.forEach(sensor => sensor.removeContainer(...containers));
+ return this;
+ }
+
+ /**
+ * Adds listener for draggable events
+ * @param {String} type - Event name
+ * @param {...Function} callbacks - Event callbacks
+ * @return {Draggable}
+ * @example draggable.on('drag:start', (dragEvent) => dragEvent.cancel());
+ */
+ on(type, ...callbacks) {
+ this.emitter.on(type, ...callbacks);
+ return this;
+ }
+
+ /**
+ * Removes listener from draggable
+ * @param {String} type - Event name
+ * @param {Function} callback - Event callback
+ * @return {Draggable}
+ * @example draggable.off('drag:start', handlerFunction);
+ */
+ off(type, callback) {
+ this.emitter.off(type, callback);
+ return this;
+ }
+
+ /**
+ * Triggers draggable event
+ * @param {AbstractEvent} event - Event instance
+ * @return {Draggable}
+ * @example draggable.trigger(event);
+ */
+ trigger(event) {
+ this.emitter.trigger(event);
+ return this;
+ }
+
+ /**
+ * Returns class name for class identifier
+ * @param {String} name - Name of class identifier
+ * @return {String|null}
+ */
+ getClassNameFor(name) {
+ return this.getClassNamesFor(name)[0];
+ }
+
+ /**
+ * Returns class names for class identifier
+ * @return {String[]}
+ */
+ getClassNamesFor(name) {
+ const classNames = this.options.classes[name];
+
+ if (classNames instanceof Array) {
+ return classNames;
+ } else if (typeof classNames === 'string' || classNames instanceof String) {
+ return [classNames];
+ } else {
+ return [];
+ }
+ }
+
+ /**
+ * Returns true if this draggable instance is currently dragging
+ * @return {Boolean}
+ */
+ isDragging() {
+ return Boolean(this.dragging);
+ }
+
+ /**
+ * Returns all draggable elements
+ * @return {HTMLElement[]}
+ */
+ getDraggableElements() {
+ return this.containers.reduce((current, container) => {
+ return [...current, ...this.getDraggableElementsForContainer(container)];
+ }, []);
+ }
+
+ /**
+ * Returns draggable elements for a given container, excluding the mirror and
+ * original source element if present
+ * @param {HTMLElement} container
+ * @return {HTMLElement[]}
+ */
+ getDraggableElementsForContainer(container) {
+ const allDraggableElements = container.querySelectorAll(this.options.draggable);
+
+ return [...allDraggableElements].filter(childElement => {
+ return childElement !== this.originalSource && childElement !== this.mirror;
+ });
+ }
+
+ /**
+ * Drag start handler
+ * @private
+ * @param {Event} event - DOM Drag event
+ */
+ [onDragStart](event) {
+ const sensorEvent = getSensorEvent(event);
+ const { target, container } = sensorEvent;
+
+ if (!this.containers.includes(container)) {
+ return;
+ }
+
+ if (this.options.handle && target && !(0, _utils.closest)(target, this.options.handle)) {
+ sensorEvent.cancel();
+ return;
+ }
+
+ // Find draggable source element
+ this.originalSource = (0, _utils.closest)(target, this.options.draggable);
+ this.sourceContainer = container;
+
+ if (!this.originalSource) {
+ sensorEvent.cancel();
+ return;
+ }
+
+ if (this.lastPlacedSource && this.lastPlacedContainer) {
+ clearTimeout(this.placedTimeoutID);
+ this.lastPlacedSource.classList.remove(...this.getClassNamesFor('source:placed'));
+ this.lastPlacedContainer.classList.remove(...this.getClassNamesFor('container:placed'));
+ }
+
+ this.source = this.originalSource.cloneNode(true);
+ this.originalSource.parentNode.insertBefore(this.source, this.originalSource);
+ this.originalSource.style.display = 'none';
+
+ const dragEvent = new _DragEvent.DragStartEvent({
+ source: this.source,
+ originalSource: this.originalSource,
+ sourceContainer: container,
+ sensorEvent
+ });
+
+ this.trigger(dragEvent);
+
+ this.dragging = !dragEvent.canceled();
+
+ if (dragEvent.canceled()) {
+ this.source.parentNode.removeChild(this.source);
+ this.originalSource.style.display = null;
+ return;
+ }
+
+ this.originalSource.classList.add(...this.getClassNamesFor('source:original'));
+ this.source.classList.add(...this.getClassNamesFor('source:dragging'));
+ this.sourceContainer.classList.add(...this.getClassNamesFor('container:dragging'));
+ document.body.classList.add(...this.getClassNamesFor('body:dragging'));
+ applyUserSelect(document.body, 'none');
+
+ requestAnimationFrame(() => {
+ const oldSensorEvent = getSensorEvent(event);
+ const newSensorEvent = oldSensorEvent.clone({ target: this.source });
+
+ this[onDragMove](_extends({}, event, {
+ detail: newSensorEvent
+ }));
+ });
+ }
+
+ /**
+ * Drag move handler
+ * @private
+ * @param {Event} event - DOM Drag event
+ */
+ [onDragMove](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ const sensorEvent = getSensorEvent(event);
+ const { container } = sensorEvent;
+ let target = sensorEvent.target;
+
+ const dragMoveEvent = new _DragEvent.DragMoveEvent({
+ source: this.source,
+ originalSource: this.originalSource,
+ sourceContainer: container,
+ sensorEvent
+ });
+
+ this.trigger(dragMoveEvent);
+
+ if (dragMoveEvent.canceled()) {
+ sensorEvent.cancel();
+ }
+
+ target = (0, _utils.closest)(target, this.options.draggable);
+ const withinCorrectContainer = (0, _utils.closest)(sensorEvent.target, this.containers);
+ const overContainer = sensorEvent.overContainer || withinCorrectContainer;
+ const isLeavingContainer = this.currentOverContainer && overContainer !== this.currentOverContainer;
+ const isLeavingDraggable = this.currentOver && target !== this.currentOver;
+ const isOverContainer = overContainer && this.currentOverContainer !== overContainer;
+ const isOverDraggable = withinCorrectContainer && target && this.currentOver !== target;
+
+ if (isLeavingDraggable) {
+ const dragOutEvent = new _DragEvent.DragOutEvent({
+ source: this.source,
+ originalSource: this.originalSource,
+ sourceContainer: container,
+ sensorEvent,
+ over: this.currentOver,
+ overContainer: this.currentOverContainer
+ });
+
+ this.currentOver.classList.remove(...this.getClassNamesFor('draggable:over'));
+ this.currentOver = null;
+
+ this.trigger(dragOutEvent);
+ }
+
+ if (isLeavingContainer) {
+ const dragOutContainerEvent = new _DragEvent.DragOutContainerEvent({
+ source: this.source,
+ originalSource: this.originalSource,
+ sourceContainer: container,
+ sensorEvent,
+ overContainer: this.currentOverContainer
+ });
+
+ this.currentOverContainer.classList.remove(...this.getClassNamesFor('container:over'));
+ this.currentOverContainer = null;
+
+ this.trigger(dragOutContainerEvent);
+ }
+
+ if (isOverContainer) {
+ overContainer.classList.add(...this.getClassNamesFor('container:over'));
+
+ const dragOverContainerEvent = new _DragEvent.DragOverContainerEvent({
+ source: this.source,
+ originalSource: this.originalSource,
+ sourceContainer: container,
+ sensorEvent,
+ overContainer
+ });
+
+ this.currentOverContainer = overContainer;
+
+ this.trigger(dragOverContainerEvent);
+ }
+
+ if (isOverDraggable) {
+ target.classList.add(...this.getClassNamesFor('draggable:over'));
+
+ const dragOverEvent = new _DragEvent.DragOverEvent({
+ source: this.source,
+ originalSource: this.originalSource,
+ sourceContainer: container,
+ sensorEvent,
+ overContainer,
+ over: target
+ });
+
+ this.currentOver = target;
+
+ this.trigger(dragOverEvent);
+ }
+ }
+
+ /**
+ * Drag stop handler
+ * @private
+ * @param {Event} event - DOM Drag event
+ */
+ [onDragStop](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ this.dragging = false;
+
+ const dragStopEvent = new _DragEvent.DragStopEvent({
+ source: this.source,
+ originalSource: this.originalSource,
+ sensorEvent: event.sensorEvent,
+ sourceContainer: this.sourceContainer
+ });
+
+ this.trigger(dragStopEvent);
+
+ this.source.parentNode.insertBefore(this.originalSource, this.source);
+ this.source.parentNode.removeChild(this.source);
+ this.originalSource.style.display = '';
+
+ this.source.classList.remove(...this.getClassNamesFor('source:dragging'));
+ this.originalSource.classList.remove(...this.getClassNamesFor('source:original'));
+ this.originalSource.classList.add(...this.getClassNamesFor('source:placed'));
+ this.sourceContainer.classList.add(...this.getClassNamesFor('container:placed'));
+ this.sourceContainer.classList.remove(...this.getClassNamesFor('container:dragging'));
+ document.body.classList.remove(...this.getClassNamesFor('body:dragging'));
+ applyUserSelect(document.body, '');
+
+ if (this.currentOver) {
+ this.currentOver.classList.remove(...this.getClassNamesFor('draggable:over'));
+ }
+
+ if (this.currentOverContainer) {
+ this.currentOverContainer.classList.remove(...this.getClassNamesFor('container:over'));
+ }
+
+ this.lastPlacedSource = this.originalSource;
+ this.lastPlacedContainer = this.sourceContainer;
+
+ this.placedTimeoutID = setTimeout(() => {
+ if (this.lastPlacedSource) {
+ this.lastPlacedSource.classList.remove(...this.getClassNamesFor('source:placed'));
+ }
+
+ if (this.lastPlacedContainer) {
+ this.lastPlacedContainer.classList.remove(...this.getClassNamesFor('container:placed'));
+ }
+
+ this.lastPlacedSource = null;
+ this.lastPlacedContainer = null;
+ }, this.options.placedTimeout);
+
+ const dragStoppedEvent = new _DragEvent.DragStoppedEvent({
+ source: this.source,
+ originalSource: this.originalSource,
+ sensorEvent: event.sensorEvent,
+ sourceContainer: this.sourceContainer
+ });
+
+ this.trigger(dragStoppedEvent);
+
+ this.source = null;
+ this.originalSource = null;
+ this.currentOverContainer = null;
+ this.currentOver = null;
+ this.sourceContainer = null;
+ }
+
+ /**
+ * Drag pressure handler
+ * @private
+ * @param {Event} event - DOM Drag event
+ */
+ [onDragPressure](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ const sensorEvent = getSensorEvent(event);
+ const source = this.source || (0, _utils.closest)(sensorEvent.originalEvent.target, this.options.draggable);
+
+ const dragPressureEvent = new _DragEvent.DragPressureEvent({
+ sensorEvent,
+ source,
+ pressure: sensorEvent.pressure
+ });
+
+ this.trigger(dragPressureEvent);
+ }
+ }
+
+ exports.default = Draggable;
+ Draggable.Plugins = { Announcement: _Plugins.Announcement, Focusable: _Plugins.Focusable, Mirror: _Plugins.Mirror, Scrollable: _Plugins.Scrollable };
+ Draggable.Sensors = { MouseSensor: _Sensors.MouseSensor, TouchSensor: _Sensors.TouchSensor };
+ function getSensorEvent(event) {
+ return event.detail;
+ }
+
+ function applyUserSelect(element, value) {
+ element.style.webkitUserSelect = value;
+ element.style.mozUserSelect = value;
+ element.style.msUserSelect = value;
+ element.style.oUserSelect = value;
+ element.style.userSelect = value;
+ }
+
+ /***/ }),
+ /* 13 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _Sensor = __webpack_require__(1);
+
+ var _Sensor2 = _interopRequireDefault(_Sensor);
+
+ var _SensorEvent = __webpack_require__(0);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onMouseForceWillBegin = Symbol('onMouseForceWillBegin');
+ const onMouseForceDown = Symbol('onMouseForceDown');
+ const onMouseDown = Symbol('onMouseDown');
+ const onMouseForceChange = Symbol('onMouseForceChange');
+ const onMouseMove = Symbol('onMouseMove');
+ const onMouseUp = Symbol('onMouseUp');
+ const onMouseForceGlobalChange = Symbol('onMouseForceGlobalChange');
+
+ /**
+ * This sensor picks up native force touch events and dictates drag operations
+ * @class ForceTouchSensor
+ * @module ForceTouchSensor
+ * @extends Sensor
+ */
+ class ForceTouchSensor extends _Sensor2.default {
+ /**
+ * ForceTouchSensor constructor.
+ * @constructs ForceTouchSensor
+ * @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
+ * @param {Object} options - Options
+ */
+ constructor(containers = [], options = {}) {
+ super(containers, options);
+
+ /**
+ * Draggable element needs to be remembered to unset the draggable attribute after drag operation has completed
+ * @property mightDrag
+ * @type {Boolean}
+ */
+ this.mightDrag = false;
+
+ this[onMouseForceWillBegin] = this[onMouseForceWillBegin].bind(this);
+ this[onMouseForceDown] = this[onMouseForceDown].bind(this);
+ this[onMouseDown] = this[onMouseDown].bind(this);
+ this[onMouseForceChange] = this[onMouseForceChange].bind(this);
+ this[onMouseMove] = this[onMouseMove].bind(this);
+ this[onMouseUp] = this[onMouseUp].bind(this);
+ }
+
+ /**
+ * Attaches sensors event listeners to the DOM
+ */
+ attach() {
+ for (const container of this.containers) {
+ container.addEventListener('webkitmouseforcewillbegin', this[onMouseForceWillBegin], false);
+ container.addEventListener('webkitmouseforcedown', this[onMouseForceDown], false);
+ container.addEventListener('mousedown', this[onMouseDown], true);
+ container.addEventListener('webkitmouseforcechanged', this[onMouseForceChange], false);
+ }
+
+ document.addEventListener('mousemove', this[onMouseMove]);
+ document.addEventListener('mouseup', this[onMouseUp]);
+ }
+
+ /**
+ * Detaches sensors event listeners to the DOM
+ */
+ detach() {
+ for (const container of this.containers) {
+ container.removeEventListener('webkitmouseforcewillbegin', this[onMouseForceWillBegin], false);
+ container.removeEventListener('webkitmouseforcedown', this[onMouseForceDown], false);
+ container.removeEventListener('mousedown', this[onMouseDown], true);
+ container.removeEventListener('webkitmouseforcechanged', this[onMouseForceChange], false);
+ }
+
+ document.removeEventListener('mousemove', this[onMouseMove]);
+ document.removeEventListener('mouseup', this[onMouseUp]);
+ }
+
+ /**
+ * Mouse force will begin handler
+ * @private
+ * @param {Event} event - Mouse force will begin event
+ */
+ [onMouseForceWillBegin](event) {
+ event.preventDefault();
+ this.mightDrag = true;
+ }
+
+ /**
+ * Mouse force down handler
+ * @private
+ * @param {Event} event - Mouse force down event
+ */
+ [onMouseForceDown](event) {
+ if (this.dragging) {
+ return;
+ }
+
+ const target = document.elementFromPoint(event.clientX, event.clientY);
+ const container = event.currentTarget;
+
+ const dragStartEvent = new _SensorEvent.DragStartSensorEvent({
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container,
+ originalEvent: event
+ });
+
+ this.trigger(container, dragStartEvent);
+
+ this.currentContainer = container;
+ this.dragging = !dragStartEvent.canceled();
+ this.mightDrag = false;
+ }
+
+ /**
+ * Mouse up handler
+ * @private
+ * @param {Event} event - Mouse up event
+ */
+ [onMouseUp](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ const dragStopEvent = new _SensorEvent.DragStopSensorEvent({
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target: null,
+ container: this.currentContainer,
+ originalEvent: event
+ });
+
+ this.trigger(this.currentContainer, dragStopEvent);
+
+ this.currentContainer = null;
+ this.dragging = false;
+ this.mightDrag = false;
+ }
+
+ /**
+ * Mouse down handler
+ * @private
+ * @param {Event} event - Mouse down event
+ */
+ [onMouseDown](event) {
+ if (!this.mightDrag) {
+ return;
+ }
+
+ // Need workaround for real click
+ // Cancel potential drag events
+ event.stopPropagation();
+ event.stopImmediatePropagation();
+ event.preventDefault();
+ }
+
+ /**
+ * Mouse move handler
+ * @private
+ * @param {Event} event - Mouse force will begin event
+ */
+ [onMouseMove](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ const target = document.elementFromPoint(event.clientX, event.clientY);
+
+ const dragMoveEvent = new _SensorEvent.DragMoveSensorEvent({
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container: this.currentContainer,
+ originalEvent: event
+ });
+
+ this.trigger(this.currentContainer, dragMoveEvent);
+ }
+
+ /**
+ * Mouse force change handler
+ * @private
+ * @param {Event} event - Mouse force change event
+ */
+ [onMouseForceChange](event) {
+ if (this.dragging) {
+ return;
+ }
+
+ const target = event.target;
+ const container = event.currentTarget;
+
+ const dragPressureEvent = new _SensorEvent.DragPressureSensorEvent({
+ pressure: event.webkitForce,
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container,
+ originalEvent: event
+ });
+
+ this.trigger(container, dragPressureEvent);
+ }
+
+ /**
+ * Mouse force global change handler
+ * @private
+ * @param {Event} event - Mouse force global change event
+ */
+ [onMouseForceGlobalChange](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ const target = event.target;
+
+ const dragPressureEvent = new _SensorEvent.DragPressureSensorEvent({
+ pressure: event.webkitForce,
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container: this.currentContainer,
+ originalEvent: event
+ });
+
+ this.trigger(this.currentContainer, dragPressureEvent);
+ }
+ }
+ exports.default = ForceTouchSensor;
+
+ /***/ }),
+ /* 14 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _ForceTouchSensor = __webpack_require__(13);
+
+ var _ForceTouchSensor2 = _interopRequireDefault(_ForceTouchSensor);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _ForceTouchSensor2.default;
+
+ /***/ }),
+ /* 15 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _utils = __webpack_require__(2);
+
+ var _Sensor = __webpack_require__(1);
+
+ var _Sensor2 = _interopRequireDefault(_Sensor);
+
+ var _SensorEvent = __webpack_require__(0);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onMouseDown = Symbol('onMouseDown');
+ const onMouseUp = Symbol('onMouseUp');
+ const onDragStart = Symbol('onDragStart');
+ const onDragOver = Symbol('onDragOver');
+ const onDragEnd = Symbol('onDragEnd');
+ const onDrop = Symbol('onDrop');
+ const reset = Symbol('reset');
+
+ /**
+ * This sensor picks up native browser drag events and dictates drag operations
+ * @class DragSensor
+ * @module DragSensor
+ * @extends Sensor
+ */
+ class DragSensor extends _Sensor2.default {
+ /**
+ * DragSensor constructor.
+ * @constructs DragSensor
+ * @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
+ * @param {Object} options - Options
+ */
+ constructor(containers = [], options = {}) {
+ super(containers, options);
+
+ /**
+ * Mouse down timer which will end up setting the draggable attribute, unless canceled
+ * @property mouseDownTimeout
+ * @type {Number}
+ */
+ this.mouseDownTimeout = null;
+
+ /**
+ * Draggable element needs to be remembered to unset the draggable attribute after drag operation has completed
+ * @property draggableElement
+ * @type {HTMLElement}
+ */
+ this.draggableElement = null;
+
+ /**
+ * Native draggable element could be links or images, their draggable state will be disabled during drag operation
+ * @property nativeDraggableElement
+ * @type {HTMLElement}
+ */
+ this.nativeDraggableElement = null;
+
+ this[onMouseDown] = this[onMouseDown].bind(this);
+ this[onMouseUp] = this[onMouseUp].bind(this);
+ this[onDragStart] = this[onDragStart].bind(this);
+ this[onDragOver] = this[onDragOver].bind(this);
+ this[onDragEnd] = this[onDragEnd].bind(this);
+ this[onDrop] = this[onDrop].bind(this);
+ }
+
+ /**
+ * Attaches sensors event listeners to the DOM
+ */
+ attach() {
+ document.addEventListener('mousedown', this[onMouseDown], true);
+ }
+
+ /**
+ * Detaches sensors event listeners to the DOM
+ */
+ detach() {
+ document.removeEventListener('mousedown', this[onMouseDown], true);
+ }
+
+ /**
+ * Drag start handler
+ * @private
+ * @param {Event} event - Drag start event
+ */
+ [onDragStart](event) {
+ // Need for firefox. "text" key is needed for IE
+ event.dataTransfer.setData('text', '');
+ event.dataTransfer.effectAllowed = this.options.type;
+
+ const target = document.elementFromPoint(event.clientX, event.clientY);
+ this.currentContainer = (0, _utils.closest)(event.target, this.containers);
+
+ if (!this.currentContainer) {
+ return;
+ }
+
+ const dragStartEvent = new _SensorEvent.DragStartSensorEvent({
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container: this.currentContainer,
+ originalEvent: event
+ });
+
+ // Workaround
+ setTimeout(() => {
+ this.trigger(this.currentContainer, dragStartEvent);
+
+ if (dragStartEvent.canceled()) {
+ this.dragging = false;
+ } else {
+ this.dragging = true;
+ }
+ }, 0);
+ }
+
+ /**
+ * Drag over handler
+ * @private
+ * @param {Event} event - Drag over event
+ */
+ [onDragOver](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ const target = document.elementFromPoint(event.clientX, event.clientY);
+ const container = this.currentContainer;
+
+ const dragMoveEvent = new _SensorEvent.DragMoveSensorEvent({
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container,
+ originalEvent: event
+ });
+
+ this.trigger(container, dragMoveEvent);
+
+ if (!dragMoveEvent.canceled()) {
+ event.preventDefault();
+ event.dataTransfer.dropEffect = this.options.type;
+ }
+ }
+
+ /**
+ * Drag end handler
+ * @private
+ * @param {Event} event - Drag end event
+ */
+ [onDragEnd](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ document.removeEventListener('mouseup', this[onMouseUp], true);
+
+ const target = document.elementFromPoint(event.clientX, event.clientY);
+ const container = this.currentContainer;
+
+ const dragStopEvent = new _SensorEvent.DragStopSensorEvent({
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container,
+ originalEvent: event
+ });
+
+ this.trigger(container, dragStopEvent);
+
+ this.dragging = false;
+ this.startEvent = null;
+
+ this[reset]();
+ }
+
+ /**
+ * Drop handler
+ * @private
+ * @param {Event} event - Drop event
+ */
+ [onDrop](event) {
+ // eslint-disable-line class-methods-use-this
+ event.preventDefault();
+ }
+
+ /**
+ * Mouse down handler
+ * @private
+ * @param {Event} event - Mouse down event
+ */
+ [onMouseDown](event) {
+ // Firefox bug for inputs within draggables https://bugzilla.mozilla.org/show_bug.cgi?id=739071
+ if (event.target && (event.target.form || event.target.contenteditable)) {
+ return;
+ }
+
+ const nativeDraggableElement = (0, _utils.closest)(event.target, element => element.draggable);
+
+ if (nativeDraggableElement) {
+ nativeDraggableElement.draggable = false;
+ this.nativeDraggableElement = nativeDraggableElement;
+ }
+
+ document.addEventListener('mouseup', this[onMouseUp], true);
+ document.addEventListener('dragstart', this[onDragStart], false);
+ document.addEventListener('dragover', this[onDragOver], false);
+ document.addEventListener('dragend', this[onDragEnd], false);
+ document.addEventListener('drop', this[onDrop], false);
+
+ const target = (0, _utils.closest)(event.target, this.options.draggable);
+
+ if (!target) {
+ return;
+ }
+
+ this.startEvent = event;
+
+ this.mouseDownTimeout = setTimeout(() => {
+ target.draggable = true;
+ this.draggableElement = target;
+ }, this.delay.drag);
+ }
+
+ /**
+ * Mouse up handler
+ * @private
+ * @param {Event} event - Mouse up event
+ */
+ [onMouseUp]() {
+ this[reset]();
+ }
+
+ /**
+ * Mouse up handler
+ * @private
+ * @param {Event} event - Mouse up event
+ */
+ [reset]() {
+ clearTimeout(this.mouseDownTimeout);
+
+ document.removeEventListener('mouseup', this[onMouseUp], true);
+ document.removeEventListener('dragstart', this[onDragStart], false);
+ document.removeEventListener('dragover', this[onDragOver], false);
+ document.removeEventListener('dragend', this[onDragEnd], false);
+ document.removeEventListener('drop', this[onDrop], false);
+
+ if (this.nativeDraggableElement) {
+ this.nativeDraggableElement.draggable = true;
+ this.nativeDraggableElement = null;
+ }
+
+ if (this.draggableElement) {
+ this.draggableElement.draggable = false;
+ this.draggableElement = null;
+ }
+ }
+ }
+ exports.default = DragSensor;
+
+ /***/ }),
+ /* 16 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _DragSensor = __webpack_require__(15);
+
+ var _DragSensor2 = _interopRequireDefault(_DragSensor);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _DragSensor2.default;
+
+ /***/ }),
+ /* 17 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _utils = __webpack_require__(2);
+
+ var _Sensor = __webpack_require__(1);
+
+ var _Sensor2 = _interopRequireDefault(_Sensor);
+
+ var _SensorEvent = __webpack_require__(0);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onTouchStart = Symbol('onTouchStart');
+ const onTouchEnd = Symbol('onTouchEnd');
+ const onTouchMove = Symbol('onTouchMove');
+ const startDrag = Symbol('startDrag');
+ const onDistanceChange = Symbol('onDistanceChange');
+
+ /**
+ * Prevents scrolling when set to true
+ * @var {Boolean} preventScrolling
+ */
+ let preventScrolling = false;
+
+// WebKit requires cancelable `touchmove` events to be added as early as possible
+ window.addEventListener('touchmove', event => {
+ if (!preventScrolling) {
+ return;
+ }
+
+ // Prevent scrolling
+ event.preventDefault();
+ }, { passive: false });
+
+ /**
+ * This sensor picks up native browser touch events and dictates drag operations
+ * @class TouchSensor
+ * @module TouchSensor
+ * @extends Sensor
+ */
+ class TouchSensor extends _Sensor2.default {
+ /**
+ * TouchSensor constructor.
+ * @constructs TouchSensor
+ * @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
+ * @param {Object} options - Options
+ */
+ constructor(containers = [], options = {}) {
+ super(containers, options);
+
+ /**
+ * Closest scrollable container so accidental scroll can cancel long touch
+ * @property currentScrollableParent
+ * @type {HTMLElement}
+ */
+ this.currentScrollableParent = null;
+
+ /**
+ * TimeoutID for managing delay
+ * @property tapTimeout
+ * @type {Number}
+ */
+ this.tapTimeout = null;
+
+ /**
+ * touchMoved indicates if touch has moved during tapTimeout
+ * @property touchMoved
+ * @type {Boolean}
+ */
+ this.touchMoved = false;
+
+ /**
+ * Save pageX coordinates for delay drag
+ * @property {Numbre} pageX
+ * @private
+ */
+ this.pageX = null;
+
+ /**
+ * Save pageY coordinates for delay drag
+ * @property {Numbre} pageY
+ * @private
+ */
+ this.pageY = null;
+
+ this[onTouchStart] = this[onTouchStart].bind(this);
+ this[onTouchEnd] = this[onTouchEnd].bind(this);
+ this[onTouchMove] = this[onTouchMove].bind(this);
+ this[startDrag] = this[startDrag].bind(this);
+ this[onDistanceChange] = this[onDistanceChange].bind(this);
+ }
+
+ /**
+ * Attaches sensors event listeners to the DOM
+ */
+ attach() {
+ document.addEventListener('touchstart', this[onTouchStart]);
+ }
+
+ /**
+ * Detaches sensors event listeners to the DOM
+ */
+ detach() {
+ document.removeEventListener('touchstart', this[onTouchStart]);
+ }
+
+ /**
+ * Touch start handler
+ * @private
+ * @param {Event} event - Touch start event
+ */
+ [onTouchStart](event) {
+ const container = (0, _utils.closest)(event.target, this.containers);
+
+ if (!container) {
+ return;
+ }
+ const { distance = 0 } = this.options;
+ const { delay } = this;
+ const { pageX, pageY } = (0, _utils.touchCoords)(event);
+
+ Object.assign(this, { pageX, pageY });
+ this.onTouchStartAt = Date.now();
+ this.startEvent = event;
+ this.currentContainer = container;
+
+ document.addEventListener('touchend', this[onTouchEnd]);
+ document.addEventListener('touchcancel', this[onTouchEnd]);
+ document.addEventListener('touchmove', this[onDistanceChange]);
+ container.addEventListener('contextmenu', onContextMenu);
+
+ if (distance) {
+ preventScrolling = true;
+ }
+
+ this.tapTimeout = window.setTimeout(() => {
+ this[onDistanceChange]({ touches: [{ pageX: this.pageX, pageY: this.pageY }] });
+ }, delay.touch);
+ }
+
+ /**
+ * Start the drag
+ * @private
+ */
+ [startDrag]() {
+ const startEvent = this.startEvent;
+ const container = this.currentContainer;
+ const touch = (0, _utils.touchCoords)(startEvent);
+
+ const dragStartEvent = new _SensorEvent.DragStartSensorEvent({
+ clientX: touch.pageX,
+ clientY: touch.pageY,
+ target: startEvent.target,
+ container,
+ originalEvent: startEvent
+ });
+
+ this.trigger(this.currentContainer, dragStartEvent);
+
+ this.dragging = !dragStartEvent.canceled();
+
+ if (this.dragging) {
+ document.addEventListener('touchmove', this[onTouchMove]);
+ }
+ preventScrolling = this.dragging;
+ }
+
+ /**
+ * Touch move handler prior to drag start.
+ * @private
+ * @param {Event} event - Touch move event
+ */
+ [onDistanceChange](event) {
+ const { distance } = this.options;
+ const { startEvent, delay } = this;
+ const start = (0, _utils.touchCoords)(startEvent);
+ const current = (0, _utils.touchCoords)(event);
+ const timeElapsed = Date.now() - this.onTouchStartAt;
+ const distanceTravelled = (0, _utils.distance)(start.pageX, start.pageY, current.pageX, current.pageY);
+
+ Object.assign(this, current);
+
+ clearTimeout(this.tapTimeout);
+
+ if (timeElapsed < delay.touch) {
+ // moved during delay
+ document.removeEventListener('touchmove', this[onDistanceChange]);
+ } else if (distanceTravelled >= distance) {
+ document.removeEventListener('touchmove', this[onDistanceChange]);
+ this[startDrag]();
+ }
+ }
+
+ /**
+ * Mouse move handler while dragging
+ * @private
+ * @param {Event} event - Touch move event
+ */
+ [onTouchMove](event) {
+ if (!this.dragging) {
+ return;
+ }
+ const { pageX, pageY } = (0, _utils.touchCoords)(event);
+ const target = document.elementFromPoint(pageX - window.scrollX, pageY - window.scrollY);
+
+ const dragMoveEvent = new _SensorEvent.DragMoveSensorEvent({
+ clientX: pageX,
+ clientY: pageY,
+ target,
+ container: this.currentContainer,
+ originalEvent: event
+ });
+
+ this.trigger(this.currentContainer, dragMoveEvent);
+ }
+
+ /**
+ * Touch end handler
+ * @private
+ * @param {Event} event - Touch end event
+ */
+ [onTouchEnd](event) {
+ clearTimeout(this.tapTimeout);
+ preventScrolling = false;
+
+ document.removeEventListener('touchend', this[onTouchEnd]);
+ document.removeEventListener('touchcancel', this[onTouchEnd]);
+ document.removeEventListener('touchmove', this[onDistanceChange]);
+
+ if (this.currentContainer) {
+ this.currentContainer.removeEventListener('contextmenu', onContextMenu);
+ }
+
+ if (!this.dragging) {
+ return;
+ }
+
+ document.removeEventListener('touchmove', this[onTouchMove]);
+
+ const { pageX, pageY } = (0, _utils.touchCoords)(event);
+ const target = document.elementFromPoint(pageX - window.scrollX, pageY - window.scrollY);
+
+ event.preventDefault();
+
+ const dragStopEvent = new _SensorEvent.DragStopSensorEvent({
+ clientX: pageX,
+ clientY: pageY,
+ target,
+ container: this.currentContainer,
+ originalEvent: event
+ });
+
+ this.trigger(this.currentContainer, dragStopEvent);
+
+ this.currentContainer = null;
+ this.dragging = false;
+ this.startEvent = null;
+ }
+ }
+
+ exports.default = TouchSensor;
+ function onContextMenu(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ /***/ }),
+ /* 18 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _TouchSensor = __webpack_require__(17);
+
+ var _TouchSensor2 = _interopRequireDefault(_TouchSensor);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _TouchSensor2.default;
+
+ /***/ }),
+ /* 19 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.DragPressureSensorEvent = exports.DragStopSensorEvent = exports.DragMoveSensorEvent = exports.DragStartSensorEvent = exports.SensorEvent = undefined;
+
+ var _AbstractEvent = __webpack_require__(3);
+
+ var _AbstractEvent2 = _interopRequireDefault(_AbstractEvent);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ /**
+ * Base sensor event
+ * @class SensorEvent
+ * @module SensorEvent
+ * @extends AbstractEvent
+ */
+ class SensorEvent extends _AbstractEvent2.default {
+ /**
+ * Original browser event that triggered a sensor
+ * @property originalEvent
+ * @type {Event}
+ * @readonly
+ */
+ get originalEvent() {
+ return this.data.originalEvent;
+ }
+
+ /**
+ * Normalized clientX for both touch and mouse events
+ * @property clientX
+ * @type {Number}
+ * @readonly
+ */
+ get clientX() {
+ return this.data.clientX;
+ }
+
+ /**
+ * Normalized clientY for both touch and mouse events
+ * @property clientY
+ * @type {Number}
+ * @readonly
+ */
+ get clientY() {
+ return this.data.clientY;
+ }
+
+ /**
+ * Normalized target for both touch and mouse events
+ * Returns the element that is behind cursor or touch pointer
+ * @property target
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get target() {
+ return this.data.target;
+ }
+
+ /**
+ * Container that initiated the sensor
+ * @property container
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get container() {
+ return this.data.container;
+ }
+
+ /**
+ * Trackpad pressure
+ * @property pressure
+ * @type {Number}
+ * @readonly
+ */
+ get pressure() {
+ return this.data.pressure;
+ }
+ }
+
+ exports.SensorEvent = SensorEvent; /**
+ * Drag start sensor event
+ * @class DragStartSensorEvent
+ * @module DragStartSensorEvent
+ * @extends SensorEvent
+ */
+
+ class DragStartSensorEvent extends SensorEvent {}
+
+ exports.DragStartSensorEvent = DragStartSensorEvent; /**
+ * Drag move sensor event
+ * @class DragMoveSensorEvent
+ * @module DragMoveSensorEvent
+ * @extends SensorEvent
+ */
+
+ DragStartSensorEvent.type = 'drag:start';
+ class DragMoveSensorEvent extends SensorEvent {}
+
+ exports.DragMoveSensorEvent = DragMoveSensorEvent; /**
+ * Drag stop sensor event
+ * @class DragStopSensorEvent
+ * @module DragStopSensorEvent
+ * @extends SensorEvent
+ */
+
+ DragMoveSensorEvent.type = 'drag:move';
+ class DragStopSensorEvent extends SensorEvent {}
+
+ exports.DragStopSensorEvent = DragStopSensorEvent; /**
+ * Drag pressure sensor event
+ * @class DragPressureSensorEvent
+ * @module DragPressureSensorEvent
+ * @extends SensorEvent
+ */
+
+ DragStopSensorEvent.type = 'drag:stop';
+ class DragPressureSensorEvent extends SensorEvent {}
+ exports.DragPressureSensorEvent = DragPressureSensorEvent;
+ DragPressureSensorEvent.type = 'drag:pressure';
+
+ /***/ }),
+ /* 20 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _utils = __webpack_require__(2);
+
+ var _Sensor = __webpack_require__(1);
+
+ var _Sensor2 = _interopRequireDefault(_Sensor);
+
+ var _SensorEvent = __webpack_require__(0);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onContextMenuWhileDragging = Symbol('onContextMenuWhileDragging');
+ const onMouseDown = Symbol('onMouseDown');
+ const onMouseMove = Symbol('onMouseMove');
+ const onMouseUp = Symbol('onMouseUp');
+ const startDrag = Symbol('startDrag');
+ const onDistanceChange = Symbol('onDistanceChange');
+
+ /**
+ * This sensor picks up native browser mouse events and dictates drag operations
+ * @class MouseSensor
+ * @module MouseSensor
+ * @extends Sensor
+ */
+ class MouseSensor extends _Sensor2.default {
+ /**
+ * MouseSensor constructor.
+ * @constructs MouseSensor
+ * @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
+ * @param {Object} options - Options
+ */
+ constructor(containers = [], options = {}) {
+ super(containers, options);
+
+ /**
+ * Mouse down timer which will end up triggering the drag start operation
+ * @property mouseDownTimeout
+ * @type {Number}
+ */
+ this.mouseDownTimeout = null;
+
+ /**
+ * Save pageX coordinates for delay drag
+ * @property {Numbre} pageX
+ * @private
+ */
+ this.pageX = null;
+
+ /**
+ * Save pageY coordinates for delay drag
+ * @property {Numbre} pageY
+ * @private
+ */
+ this.pageY = null;
+
+ this[onContextMenuWhileDragging] = this[onContextMenuWhileDragging].bind(this);
+ this[onMouseDown] = this[onMouseDown].bind(this);
+ this[onMouseMove] = this[onMouseMove].bind(this);
+ this[onMouseUp] = this[onMouseUp].bind(this);
+ this[startDrag] = this[startDrag].bind(this);
+ this[onDistanceChange] = this[onDistanceChange].bind(this);
+ }
+
+ /**
+ * Attaches sensors event listeners to the DOM
+ */
+ attach() {
+ document.addEventListener('mousedown', this[onMouseDown], true);
+ }
+
+ /**
+ * Detaches sensors event listeners to the DOM
+ */
+ detach() {
+ document.removeEventListener('mousedown', this[onMouseDown], true);
+ }
+
+ /**
+ * Mouse down handler
+ * @private
+ * @param {Event} event - Mouse down event
+ */
+ [onMouseDown](event) {
+ if (event.button !== 0 || event.ctrlKey || event.metaKey) {
+ return;
+ }
+ const container = (0, _utils.closest)(event.target, this.containers);
+
+ if (!container) {
+ return;
+ }
+
+ const { delay } = this;
+ const { pageX, pageY } = event;
+
+ Object.assign(this, { pageX, pageY });
+ this.onMouseDownAt = Date.now();
+ this.startEvent = event;
+
+ this.currentContainer = container;
+ document.addEventListener('mouseup', this[onMouseUp]);
+ document.addEventListener('dragstart', preventNativeDragStart);
+ document.addEventListener('mousemove', this[onDistanceChange]);
+
+ this.mouseDownTimeout = window.setTimeout(() => {
+ this[onDistanceChange]({ pageX: this.pageX, pageY: this.pageY });
+ }, delay.mouse);
+ }
+
+ /**
+ * Start the drag
+ * @private
+ */
+ [startDrag]() {
+ const startEvent = this.startEvent;
+ const container = this.currentContainer;
+
+ const dragStartEvent = new _SensorEvent.DragStartSensorEvent({
+ clientX: startEvent.clientX,
+ clientY: startEvent.clientY,
+ target: startEvent.target,
+ container,
+ originalEvent: startEvent
+ });
+
+ this.trigger(this.currentContainer, dragStartEvent);
+
+ this.dragging = !dragStartEvent.canceled();
+
+ if (this.dragging) {
+ document.addEventListener('contextmenu', this[onContextMenuWhileDragging], true);
+ document.addEventListener('mousemove', this[onMouseMove]);
+ }
+ }
+
+ /**
+ * Detect change in distance, starting drag when both
+ * delay and distance requirements are met
+ * @private
+ * @param {Event} event - Mouse move event
+ */
+ [onDistanceChange](event) {
+ const { pageX, pageY } = event;
+ const { distance } = this.options;
+ const { startEvent, delay } = this;
+
+ Object.assign(this, { pageX, pageY });
+
+ if (!this.currentContainer) {
+ return;
+ }
+
+ const timeElapsed = Date.now() - this.onMouseDownAt;
+ const distanceTravelled = (0, _utils.distance)(startEvent.pageX, startEvent.pageY, pageX, pageY) || 0;
+
+ clearTimeout(this.mouseDownTimeout);
+
+ if (timeElapsed < delay.mouse) {
+ // moved during delay
+ document.removeEventListener('mousemove', this[onDistanceChange]);
+ } else if (distanceTravelled >= distance) {
+ document.removeEventListener('mousemove', this[onDistanceChange]);
+ this[startDrag]();
+ }
+ }
+
+ /**
+ * Mouse move handler
+ * @private
+ * @param {Event} event - Mouse move event
+ */
+ [onMouseMove](event) {
+ if (!this.dragging) {
+ return;
+ }
+
+ const target = document.elementFromPoint(event.clientX, event.clientY);
+
+ const dragMoveEvent = new _SensorEvent.DragMoveSensorEvent({
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container: this.currentContainer,
+ originalEvent: event
+ });
+
+ this.trigger(this.currentContainer, dragMoveEvent);
+ }
+
+ /**
+ * Mouse up handler
+ * @private
+ * @param {Event} event - Mouse up event
+ */
+ [onMouseUp](event) {
+ clearTimeout(this.mouseDownTimeout);
+
+ if (event.button !== 0) {
+ return;
+ }
+
+ document.removeEventListener('mouseup', this[onMouseUp]);
+ document.removeEventListener('dragstart', preventNativeDragStart);
+ document.removeEventListener('mousemove', this[onDistanceChange]);
+
+ if (!this.dragging) {
+ return;
+ }
+
+ const target = document.elementFromPoint(event.clientX, event.clientY);
+
+ const dragStopEvent = new _SensorEvent.DragStopSensorEvent({
+ clientX: event.clientX,
+ clientY: event.clientY,
+ target,
+ container: this.currentContainer,
+ originalEvent: event
+ });
+
+ this.trigger(this.currentContainer, dragStopEvent);
+
+ document.removeEventListener('contextmenu', this[onContextMenuWhileDragging], true);
+ document.removeEventListener('mousemove', this[onMouseMove]);
+
+ this.currentContainer = null;
+ this.dragging = false;
+ this.startEvent = null;
+ }
+
+ /**
+ * Context menu handler
+ * @private
+ * @param {Event} event - Context menu event
+ */
+ [onContextMenuWhileDragging](event) {
+ event.preventDefault();
+ }
+ }
+
+ exports.default = MouseSensor;
+ function preventNativeDragStart(event) {
+ event.preventDefault();
+ }
+
+ /***/ }),
+ /* 21 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _MouseSensor = __webpack_require__(20);
+
+ var _MouseSensor2 = _interopRequireDefault(_MouseSensor);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _MouseSensor2.default;
+
+ /***/ }),
+ /* 22 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+ const defaultDealy = {
+ mouse: 0,
+ drag: 0,
+ touch: 100
+ };
+
+ /**
+ * Base sensor class. Extend from this class to create a new or custom sensor
+ * @class Sensor
+ * @module Sensor
+ */
+ class Sensor {
+ /**
+ * Sensor constructor.
+ * @constructs Sensor
+ * @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
+ * @param {Object} options - Options
+ */
+ constructor(containers = [], options = {}) {
+ /**
+ * Current containers
+ * @property containers
+ * @type {HTMLElement[]}
+ */
+ this.containers = [...containers];
+
+ /**
+ * Current options
+ * @property options
+ * @type {Object}
+ */
+ this.options = _extends({}, options);
+
+ /**
+ * Current drag state
+ * @property dragging
+ * @type {Boolean}
+ */
+ this.dragging = false;
+
+ /**
+ * Current container
+ * @property currentContainer
+ * @type {HTMLElement}
+ */
+ this.currentContainer = null;
+
+ /**
+ * The event of the initial sensor down
+ * @property startEvent
+ * @type {Event}
+ */
+ this.startEvent = null;
+
+ /**
+ * The delay of each sensor
+ * @property delay
+ * @type {Object}
+ */
+ this.delay = calcDelay(options.delay);
+ }
+
+ /**
+ * Attaches sensors event listeners to the DOM
+ * @return {Sensor}
+ */
+ attach() {
+ return this;
+ }
+
+ /**
+ * Detaches sensors event listeners to the DOM
+ * @return {Sensor}
+ */
+ detach() {
+ return this;
+ }
+
+ /**
+ * Adds container to this sensor instance
+ * @param {...HTMLElement} containers - Containers you want to add to this sensor
+ * @example draggable.addContainer(document.body)
+ */
+ addContainer(...containers) {
+ this.containers = [...this.containers, ...containers];
+ }
+
+ /**
+ * Removes container from this sensor instance
+ * @param {...HTMLElement} containers - Containers you want to remove from this sensor
+ * @example draggable.removeContainer(document.body)
+ */
+ removeContainer(...containers) {
+ this.containers = this.containers.filter(container => !containers.includes(container));
+ }
+
+ /**
+ * Triggers event on target element
+ * @param {HTMLElement} element - Element to trigger event on
+ * @param {SensorEvent} sensorEvent - Sensor event to trigger
+ */
+ trigger(element, sensorEvent) {
+ const event = document.createEvent('Event');
+ event.detail = sensorEvent;
+ event.initEvent(sensorEvent.type, true, true);
+ element.dispatchEvent(event);
+ this.lastEvent = sensorEvent;
+
+ return sensorEvent;
+ }
+ }
+
+ exports.default = Sensor; /**
+ * Calculate the delay of each sensor through the delay in the options
+ * @param {undefined|Number|Object} optionsDelay - the delay in the options
+ * @return {Object}
+ */
+
+ function calcDelay(optionsDelay) {
+ const delay = {};
+
+ if (optionsDelay === undefined) {
+ return _extends({}, defaultDealy);
+ }
+
+ if (typeof optionsDelay === 'number') {
+ for (const key in defaultDealy) {
+ if (defaultDealy.hasOwnProperty(key)) {
+ delay[key] = optionsDelay;
+ }
+ }
+ return delay;
+ }
+
+ for (const key in defaultDealy) {
+ if (defaultDealy.hasOwnProperty(key)) {
+ if (optionsDelay[key] === undefined) {
+ delay[key] = defaultDealy[key];
+ } else {
+ delay[key] = optionsDelay[key];
+ }
+ }
+ }
+
+ return delay;
+ }
+
+ /***/ }),
+ /* 23 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.default = touchCoords;
+ /**
+ * Returns the first touch event found in touches or changedTouches of a touch events.
+ * @param {TouchEvent} event a touch event
+ * @return {Touch} a touch object
+ */
+ function touchCoords(event = {}) {
+ const { touches, changedTouches } = event;
+ return touches && touches[0] || changedTouches && changedTouches[0];
+ }
+
+ /***/ }),
+ /* 24 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _touchCoords = __webpack_require__(23);
+
+ var _touchCoords2 = _interopRequireDefault(_touchCoords);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _touchCoords2.default;
+
+ /***/ }),
+ /* 25 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.default = distance;
+ /**
+ * Returns the distance between two points
+ * @param {Number} x1 The X position of the first point
+ * @param {Number} y1 The Y position of the first point
+ * @param {Number} x2 The X position of the second point
+ * @param {Number} y2 The Y position of the second point
+ * @return {Number}
+ */
+ function distance(x1, y1, x2, y2) {
+ return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
+ }
+
+ /***/ }),
+ /* 26 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _distance = __webpack_require__(25);
+
+ var _distance2 = _interopRequireDefault(_distance);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _distance2.default;
+
+ /***/ }),
+ /* 27 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.default = requestNextAnimationFrame;
+ function requestNextAnimationFrame(callback) {
+ return requestAnimationFrame(() => {
+ requestAnimationFrame(callback);
+ });
+ }
+
+ /***/ }),
+ /* 28 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _requestNextAnimationFrame = __webpack_require__(27);
+
+ var _requestNextAnimationFrame2 = _interopRequireDefault(_requestNextAnimationFrame);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _requestNextAnimationFrame2.default;
+
+ /***/ }),
+ /* 29 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.default = closest;
+ const matchFunction = Element.prototype.matches || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector;
+
+ /**
+ * Get the closest parent element of a given element that matches the given
+ * selector string or matching function
+ *
+ * @param {Element} element The child element to find a parent of
+ * @param {String|Function} selector The string or function to use to match
+ * the parent element
+ * @return {Element|null}
+ */
+ function closest(element, value) {
+ if (!element) {
+ return null;
+ }
+
+ const selector = value;
+ const callback = value;
+ const nodeList = value;
+ const singleElement = value;
+
+ const isSelector = Boolean(typeof value === 'string');
+ const isFunction = Boolean(typeof value === 'function');
+ const isNodeList = Boolean(value instanceof NodeList || value instanceof Array);
+ const isElement = Boolean(value instanceof HTMLElement);
+
+ function conditionFn(currentElement) {
+ if (!currentElement) {
+ return currentElement;
+ } else if (isSelector) {
+ return matchFunction.call(currentElement, selector);
+ } else if (isNodeList) {
+ return [...nodeList].includes(currentElement);
+ } else if (isElement) {
+ return singleElement === currentElement;
+ } else if (isFunction) {
+ return callback(currentElement);
+ } else {
+ return null;
+ }
+ }
+
+ let current = element;
+
+ do {
+ current = current.correspondingUseElement || current.correspondingElement || current;
+
+ if (conditionFn(current)) {
+ return current;
+ }
+
+ current = current.parentNode;
+ } while (current && current !== document.body && current !== document);
+
+ return null;
+ }
+
+ /***/ }),
+ /* 30 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _closest = __webpack_require__(29);
+
+ var _closest2 = _interopRequireDefault(_closest);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _closest2.default;
+
+ /***/ }),
+ /* 31 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.defaultOptions = exports.scroll = exports.onDragStop = exports.onDragMove = exports.onDragStart = undefined;
+
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+ var _AbstractPlugin = __webpack_require__(4);
+
+ var _AbstractPlugin2 = _interopRequireDefault(_AbstractPlugin);
+
+ var _utils = __webpack_require__(2);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onDragStart = exports.onDragStart = Symbol('onDragStart');
+ const onDragMove = exports.onDragMove = Symbol('onDragMove');
+ const onDragStop = exports.onDragStop = Symbol('onDragStop');
+ const scroll = exports.scroll = Symbol('scroll');
+
+ /**
+ * Scrollable default options
+ * @property {Object} defaultOptions
+ * @property {Number} defaultOptions.speed
+ * @property {Number} defaultOptions.sensitivity
+ * @property {HTMLElement[]} defaultOptions.scrollableElements
+ * @type {Object}
+ */
+ const defaultOptions = exports.defaultOptions = {
+ speed: 6,
+ sensitivity: 50,
+ scrollableElements: []
+ };
+
+ /**
+ * Scrollable plugin which scrolls the closest scrollable parent
+ * @class Scrollable
+ * @module Scrollable
+ * @extends AbstractPlugin
+ */
+ class Scrollable extends _AbstractPlugin2.default {
+ /**
+ * Scrollable constructor.
+ * @constructs Scrollable
+ * @param {Draggable} draggable - Draggable instance
+ */
+ constructor(draggable) {
+ super(draggable);
+
+ /**
+ * Scrollable options
+ * @property {Object} options
+ * @property {Number} options.speed
+ * @property {Number} options.sensitivity
+ * @property {HTMLElement[]} options.scrollableElements
+ * @type {Object}
+ */
+ this.options = _extends({}, defaultOptions, this.getOptions());
+
+ /**
+ * Keeps current mouse position
+ * @property {Object} currentMousePosition
+ * @property {Number} currentMousePosition.clientX
+ * @property {Number} currentMousePosition.clientY
+ * @type {Object|null}
+ */
+ this.currentMousePosition = null;
+
+ /**
+ * Scroll animation frame
+ * @property scrollAnimationFrame
+ * @type {Number|null}
+ */
+ this.scrollAnimationFrame = null;
+
+ /**
+ * Closest scrollable element
+ * @property scrollableElement
+ * @type {HTMLElement|null}
+ */
+ this.scrollableElement = null;
+
+ /**
+ * Animation frame looking for the closest scrollable element
+ * @property findScrollableElementFrame
+ * @type {Number|null}
+ */
+ this.findScrollableElementFrame = null;
+
+ this[onDragStart] = this[onDragStart].bind(this);
+ this[onDragMove] = this[onDragMove].bind(this);
+ this[onDragStop] = this[onDragStop].bind(this);
+ this[scroll] = this[scroll].bind(this);
+ }
+
+ /**
+ * Attaches plugins event listeners
+ */
+ attach() {
+ this.draggable.on('drag:start', this[onDragStart]).on('drag:move', this[onDragMove]).on('drag:stop', this[onDragStop]);
+ }
+
+ /**
+ * Detaches plugins event listeners
+ */
+ detach() {
+ this.draggable.off('drag:start', this[onDragStart]).off('drag:move', this[onDragMove]).off('drag:stop', this[onDragStop]);
+ }
+
+ /**
+ * Returns options passed through draggable
+ * @return {Object}
+ */
+ getOptions() {
+ return this.draggable.options.scrollable || {};
+ }
+
+ /**
+ * Returns closest scrollable elements by element
+ * @param {HTMLElement} target
+ * @return {HTMLElement}
+ */
+ getScrollableElement(target) {
+ if (this.hasDefinedScrollableElements()) {
+ return (0, _utils.closest)(target, this.options.scrollableElements) || document.documentElement;
+ } else {
+ return closestScrollableElement(target);
+ }
+ }
+
+ /**
+ * Returns true if at least one scrollable element have been defined via options
+ * @param {HTMLElement} target
+ * @return {Boolean}
+ */
+ hasDefinedScrollableElements() {
+ return Boolean(this.options.scrollableElements.length !== 0);
+ }
+
+ /**
+ * Drag start handler. Finds closest scrollable parent in separate frame
+ * @param {DragStartEvent} dragEvent
+ * @private
+ */
+ [onDragStart](dragEvent) {
+ this.findScrollableElementFrame = requestAnimationFrame(() => {
+ this.scrollableElement = this.getScrollableElement(dragEvent.source);
+ });
+ }
+
+ /**
+ * Drag move handler. Remembers mouse position and initiates scrolling
+ * @param {DragMoveEvent} dragEvent
+ * @private
+ */
+ [onDragMove](dragEvent) {
+ this.findScrollableElementFrame = requestAnimationFrame(() => {
+ this.scrollableElement = this.getScrollableElement(dragEvent.sensorEvent.target);
+ });
+
+ if (!this.scrollableElement) {
+ return;
+ }
+
+ const sensorEvent = dragEvent.sensorEvent;
+ const scrollOffset = { x: 0, y: 0 };
+
+ if ('ontouchstart' in window) {
+ scrollOffset.y = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
+ scrollOffset.x = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
+ }
+
+ this.currentMousePosition = {
+ clientX: sensorEvent.clientX - scrollOffset.x,
+ clientY: sensorEvent.clientY - scrollOffset.y
+ };
+
+ this.scrollAnimationFrame = requestAnimationFrame(this[scroll]);
+ }
+
+ /**
+ * Drag stop handler. Cancels scroll animations and resets state
+ * @private
+ */
+ [onDragStop]() {
+ cancelAnimationFrame(this.scrollAnimationFrame);
+ cancelAnimationFrame(this.findScrollableElementFrame);
+
+ this.scrollableElement = null;
+ this.scrollAnimationFrame = null;
+ this.findScrollableElementFrame = null;
+ this.currentMousePosition = null;
+ }
+
+ /**
+ * Scroll function that does the heavylifting
+ * @private
+ */
+ [scroll]() {
+ if (!this.scrollableElement || !this.currentMousePosition) {
+ return;
+ }
+
+ cancelAnimationFrame(this.scrollAnimationFrame);
+
+ const { speed, sensitivity } = this.options;
+
+ const rect = this.scrollableElement.getBoundingClientRect();
+ const bottomCutOff = rect.bottom > window.innerHeight;
+ const topCutOff = rect.top < 0;
+ const cutOff = topCutOff || bottomCutOff;
+
+ const documentScrollingElement = getDocumentScrollingElement();
+ const scrollableElement = this.scrollableElement;
+ const clientX = this.currentMousePosition.clientX;
+ const clientY = this.currentMousePosition.clientY;
+
+ if (scrollableElement !== document.body && scrollableElement !== document.documentElement && !cutOff) {
+ const { offsetHeight, offsetWidth } = scrollableElement;
+
+ if (rect.top + offsetHeight - clientY < sensitivity) {
+ scrollableElement.scrollTop += speed;
+ } else if (clientY - rect.top < sensitivity) {
+ scrollableElement.scrollTop -= speed;
+ }
+
+ if (rect.left + offsetWidth - clientX < sensitivity) {
+ scrollableElement.scrollLeft += speed;
+ } else if (clientX - rect.left < sensitivity) {
+ scrollableElement.scrollLeft -= speed;
+ }
+ } else {
+ const { innerHeight, innerWidth } = window;
+
+ if (clientY < sensitivity) {
+ documentScrollingElement.scrollTop -= speed;
+ } else if (innerHeight - clientY < sensitivity) {
+ documentScrollingElement.scrollTop += speed;
+ }
+
+ if (clientX < sensitivity) {
+ documentScrollingElement.scrollLeft -= speed;
+ } else if (innerWidth - clientX < sensitivity) {
+ documentScrollingElement.scrollLeft += speed;
+ }
+ }
+
+ this.scrollAnimationFrame = requestAnimationFrame(this[scroll]);
+ }
+ }
+
+ exports.default = Scrollable; /**
+ * Returns true if the passed element has overflow
+ * @param {HTMLElement} element
+ * @return {Boolean}
+ * @private
+ */
+
+ function hasOverflow(element) {
+ const overflowRegex = /(auto|scroll)/;
+ const computedStyles = getComputedStyle(element, null);
+
+ const overflow = computedStyles.getPropertyValue('overflow') + computedStyles.getPropertyValue('overflow-y') + computedStyles.getPropertyValue('overflow-x');
+
+ return overflowRegex.test(overflow);
+ }
+
+ /**
+ * Returns true if the passed element is statically positioned
+ * @param {HTMLElement} element
+ * @return {Boolean}
+ * @private
+ */
+ function isStaticallyPositioned(element) {
+ const position = getComputedStyle(element).getPropertyValue('position');
+ return position === 'static';
+ }
+
+ /**
+ * Finds closest scrollable element
+ * @param {HTMLElement} element
+ * @return {HTMLElement}
+ * @private
+ */
+ function closestScrollableElement(element) {
+ if (!element) {
+ return getDocumentScrollingElement();
+ }
+
+ const position = getComputedStyle(element).getPropertyValue('position');
+ const excludeStaticParents = position === 'absolute';
+
+ const scrollableElement = (0, _utils.closest)(element, parent => {
+ if (excludeStaticParents && isStaticallyPositioned(parent)) {
+ return false;
+ }
+ return hasOverflow(parent);
+ });
+
+ if (position === 'fixed' || !scrollableElement) {
+ return getDocumentScrollingElement();
+ } else {
+ return scrollableElement;
+ }
+ }
+
+ /**
+ * Returns element that scrolls document
+ * @return {HTMLElement}
+ * @private
+ */
+ function getDocumentScrollingElement() {
+ return document.scrollingElement || document.documentElement;
+ }
+
+ /***/ }),
+ /* 32 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.defaultOptions = undefined;
+
+ var _Scrollable = __webpack_require__(31);
+
+ var _Scrollable2 = _interopRequireDefault(_Scrollable);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _Scrollable2.default;
+ exports.defaultOptions = _Scrollable.defaultOptions;
+
+ /***/ }),
+ /* 33 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.MirrorDestroyEvent = exports.MirrorMoveEvent = exports.MirrorAttachedEvent = exports.MirrorCreatedEvent = exports.MirrorCreateEvent = exports.MirrorEvent = undefined;
+
+ var _AbstractEvent = __webpack_require__(3);
+
+ var _AbstractEvent2 = _interopRequireDefault(_AbstractEvent);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ /**
+ * Base mirror event
+ * @class MirrorEvent
+ * @module MirrorEvent
+ * @extends AbstractEvent
+ */
+ class MirrorEvent extends _AbstractEvent2.default {
+ /**
+ * Draggables source element
+ * @property source
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get source() {
+ return this.data.source;
+ }
+
+ /**
+ * Draggables original source element
+ * @property originalSource
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get originalSource() {
+ return this.data.originalSource;
+ }
+
+ /**
+ * Draggables source container element
+ * @property sourceContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get sourceContainer() {
+ return this.data.sourceContainer;
+ }
+
+ /**
+ * Sensor event
+ * @property sensorEvent
+ * @type {SensorEvent}
+ * @readonly
+ */
+ get sensorEvent() {
+ return this.data.sensorEvent;
+ }
+
+ /**
+ * Drag event
+ * @property dragEvent
+ * @type {DragEvent}
+ * @readonly
+ */
+ get dragEvent() {
+ return this.data.dragEvent;
+ }
+
+ /**
+ * Original event that triggered sensor event
+ * @property originalEvent
+ * @type {Event}
+ * @readonly
+ */
+ get originalEvent() {
+ if (this.sensorEvent) {
+ return this.sensorEvent.originalEvent;
+ }
+
+ return null;
+ }
+ }
+
+ exports.MirrorEvent = MirrorEvent; /**
+ * Mirror create event
+ * @class MirrorCreateEvent
+ * @module MirrorCreateEvent
+ * @extends MirrorEvent
+ */
+
+ class MirrorCreateEvent extends MirrorEvent {}
+
+ exports.MirrorCreateEvent = MirrorCreateEvent; /**
+ * Mirror created event
+ * @class MirrorCreatedEvent
+ * @module MirrorCreatedEvent
+ * @extends MirrorEvent
+ */
+
+ MirrorCreateEvent.type = 'mirror:create';
+ class MirrorCreatedEvent extends MirrorEvent {
+
+ /**
+ * Draggables mirror element
+ * @property mirror
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get mirror() {
+ return this.data.mirror;
+ }
+ }
+
+ exports.MirrorCreatedEvent = MirrorCreatedEvent; /**
+ * Mirror attached event
+ * @class MirrorAttachedEvent
+ * @module MirrorAttachedEvent
+ * @extends MirrorEvent
+ */
+
+ MirrorCreatedEvent.type = 'mirror:created';
+ class MirrorAttachedEvent extends MirrorEvent {
+
+ /**
+ * Draggables mirror element
+ * @property mirror
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get mirror() {
+ return this.data.mirror;
+ }
+ }
+
+ exports.MirrorAttachedEvent = MirrorAttachedEvent; /**
+ * Mirror move event
+ * @class MirrorMoveEvent
+ * @module MirrorMoveEvent
+ * @extends MirrorEvent
+ */
+
+ MirrorAttachedEvent.type = 'mirror:attached';
+ class MirrorMoveEvent extends MirrorEvent {
+
+ /**
+ * Draggables mirror element
+ * @property mirror
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get mirror() {
+ return this.data.mirror;
+ }
+
+ /**
+ * Sensor has exceeded mirror's threshold on x axis
+ * @type {Boolean}
+ * @readonly
+ */
+ get passedThreshX() {
+ return this.data.passedThreshX;
+ }
+
+ /**
+ * Sensor has exceeded mirror's threshold on y axis
+ * @type {Boolean}
+ * @readonly
+ */
+ get passedThreshY() {
+ return this.data.passedThreshY;
+ }
+ }
+
+ exports.MirrorMoveEvent = MirrorMoveEvent; /**
+ * Mirror destroy event
+ * @class MirrorDestroyEvent
+ * @module MirrorDestroyEvent
+ * @extends MirrorEvent
+ */
+
+ MirrorMoveEvent.type = 'mirror:move';
+ MirrorMoveEvent.cancelable = true;
+ class MirrorDestroyEvent extends MirrorEvent {
+
+ /**
+ * Draggables mirror element
+ * @property mirror
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get mirror() {
+ return this.data.mirror;
+ }
+ }
+ exports.MirrorDestroyEvent = MirrorDestroyEvent;
+ MirrorDestroyEvent.type = 'mirror:destroy';
+ MirrorDestroyEvent.cancelable = true;
+
+ /***/ }),
+ /* 34 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _MirrorEvent = __webpack_require__(33);
+
+ Object.keys(_MirrorEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _MirrorEvent[key];
+ }
+ });
+ });
+
+ /***/ }),
+ /* 35 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.defaultOptions = exports.getAppendableContainer = exports.onScroll = exports.onMirrorMove = exports.onMirrorCreated = exports.onDragStop = exports.onDragMove = exports.onDragStart = undefined;
+
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+ var _AbstractPlugin = __webpack_require__(4);
+
+ var _AbstractPlugin2 = _interopRequireDefault(_AbstractPlugin);
+
+ var _MirrorEvent = __webpack_require__(34);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+ const onDragStart = exports.onDragStart = Symbol('onDragStart');
+ const onDragMove = exports.onDragMove = Symbol('onDragMove');
+ const onDragStop = exports.onDragStop = Symbol('onDragStop');
+ const onMirrorCreated = exports.onMirrorCreated = Symbol('onMirrorCreated');
+ const onMirrorMove = exports.onMirrorMove = Symbol('onMirrorMove');
+ const onScroll = exports.onScroll = Symbol('onScroll');
+ const getAppendableContainer = exports.getAppendableContainer = Symbol('getAppendableContainer');
+
+ /**
+ * Mirror default options
+ * @property {Object} defaultOptions
+ * @property {Boolean} defaultOptions.constrainDimensions
+ * @property {Boolean} defaultOptions.xAxis
+ * @property {Boolean} defaultOptions.yAxis
+ * @property {null} defaultOptions.cursorOffsetX
+ * @property {null} defaultOptions.cursorOffsetY
+ * @type {Object}
+ */
+ const defaultOptions = exports.defaultOptions = {
+ constrainDimensions: false,
+ xAxis: true,
+ yAxis: true,
+ cursorOffsetX: null,
+ cursorOffsetY: null,
+ thresholdX: null,
+ thresholdY: null
+ };
+
+ /**
+ * Mirror plugin which controls the mirror positioning while dragging
+ * @class Mirror
+ * @module Mirror
+ * @extends AbstractPlugin
+ */
+ class Mirror extends _AbstractPlugin2.default {
+ /**
+ * Mirror constructor.
+ * @constructs Mirror
+ * @param {Draggable} draggable - Draggable instance
+ */
+ constructor(draggable) {
+ super(draggable);
+
+ /**
+ * Mirror options
+ * @property {Object} options
+ * @property {Boolean} options.constrainDimensions
+ * @property {Boolean} options.xAxis
+ * @property {Boolean} options.yAxis
+ * @property {Number|null} options.cursorOffsetX
+ * @property {Number|null} options.cursorOffsetY
+ * @property {String|HTMLElement|Function} options.appendTo
+ * @type {Object}
+ */
+ this.options = _extends({}, defaultOptions, this.getOptions());
+
+ /**
+ * Scroll offset for touch devices because the mirror is positioned fixed
+ * @property {Object} scrollOffset
+ * @property {Number} scrollOffset.x
+ * @property {Number} scrollOffset.y
+ */
+ this.scrollOffset = { x: 0, y: 0 };
+
+ /**
+ * Initial scroll offset for touch devices because the mirror is positioned fixed
+ * @property {Object} scrollOffset
+ * @property {Number} scrollOffset.x
+ * @property {Number} scrollOffset.y
+ */
+ this.initialScrollOffset = {
+ x: window.scrollX,
+ y: window.scrollY
+ };
+
+ this[onDragStart] = this[onDragStart].bind(this);
+ this[onDragMove] = this[onDragMove].bind(this);
+ this[onDragStop] = this[onDragStop].bind(this);
+ this[onMirrorCreated] = this[onMirrorCreated].bind(this);
+ this[onMirrorMove] = this[onMirrorMove].bind(this);
+ this[onScroll] = this[onScroll].bind(this);
+ }
+
+ /**
+ * Attaches plugins event listeners
+ */
+ attach() {
+ this.draggable.on('drag:start', this[onDragStart]).on('drag:move', this[onDragMove]).on('drag:stop', this[onDragStop]).on('mirror:created', this[onMirrorCreated]).on('mirror:move', this[onMirrorMove]);
+ }
+
+ /**
+ * Detaches plugins event listeners
+ */
+ detach() {
+ this.draggable.off('drag:start', this[onDragStart]).off('drag:move', this[onDragMove]).off('drag:stop', this[onDragStop]).off('mirror:created', this[onMirrorCreated]).off('mirror:move', this[onMirrorMove]);
+ }
+
+ /**
+ * Returns options passed through draggable
+ * @return {Object}
+ */
+ getOptions() {
+ return this.draggable.options.mirror || {};
+ }
+
+ [onDragStart](dragEvent) {
+ if (dragEvent.canceled()) {
+ return;
+ }
+
+ if ('ontouchstart' in window) {
+ document.addEventListener('scroll', this[onScroll], true);
+ }
+
+ this.initialScrollOffset = {
+ x: window.scrollX,
+ y: window.scrollY
+ };
+
+ const { source, originalSource, sourceContainer, sensorEvent } = dragEvent;
+
+ // Last sensor position of mirror move
+ this.lastMirrorMovedClient = {
+ x: sensorEvent.clientX,
+ y: sensorEvent.clientY
+ };
+
+ const mirrorCreateEvent = new _MirrorEvent.MirrorCreateEvent({
+ source,
+ originalSource,
+ sourceContainer,
+ sensorEvent,
+ dragEvent
+ });
+
+ this.draggable.trigger(mirrorCreateEvent);
+
+ if (isNativeDragEvent(sensorEvent) || mirrorCreateEvent.canceled()) {
+ return;
+ }
+
+ const appendableContainer = this[getAppendableContainer](source) || sourceContainer;
+ this.mirror = source.cloneNode(true);
+
+ const mirrorCreatedEvent = new _MirrorEvent.MirrorCreatedEvent({
+ source,
+ originalSource,
+ sourceContainer,
+ sensorEvent,
+ dragEvent,
+ mirror: this.mirror
+ });
+
+ const mirrorAttachedEvent = new _MirrorEvent.MirrorAttachedEvent({
+ source,
+ originalSource,
+ sourceContainer,
+ sensorEvent,
+ dragEvent,
+ mirror: this.mirror
+ });
+
+ this.draggable.trigger(mirrorCreatedEvent);
+ appendableContainer.appendChild(this.mirror);
+ this.draggable.trigger(mirrorAttachedEvent);
+ }
+
+ [onDragMove](dragEvent) {
+ if (!this.mirror || dragEvent.canceled()) {
+ return;
+ }
+
+ const { source, originalSource, sourceContainer, sensorEvent } = dragEvent;
+
+ let passedThreshX = true;
+ let passedThreshY = true;
+
+ if (this.options.thresholdX || this.options.thresholdY) {
+ const { x: lastX, y: lastY } = this.lastMirrorMovedClient;
+
+ if (Math.abs(lastX - sensorEvent.clientX) < this.options.thresholdX) {
+ passedThreshX = false;
+ } else {
+ this.lastMirrorMovedClient.x = sensorEvent.clientX;
+ }
+
+ if (Math.abs(lastY - sensorEvent.clientY) < this.options.thresholdY) {
+ passedThreshY = false;
+ } else {
+ this.lastMirrorMovedClient.y = sensorEvent.clientY;
+ }
+
+ if (!passedThreshX && !passedThreshY) {
+ return;
+ }
+ }
+
+ const mirrorMoveEvent = new _MirrorEvent.MirrorMoveEvent({
+ source,
+ originalSource,
+ sourceContainer,
+ sensorEvent,
+ dragEvent,
+ mirror: this.mirror,
+ passedThreshX,
+ passedThreshY
+ });
+
+ this.draggable.trigger(mirrorMoveEvent);
+ }
+
+ [onDragStop](dragEvent) {
+ if ('ontouchstart' in window) {
+ document.removeEventListener('scroll', this[onScroll], true);
+ }
+
+ this.initialScrollOffset = { x: 0, y: 0 };
+ this.scrollOffset = { x: 0, y: 0 };
+
+ if (!this.mirror) {
+ return;
+ }
+
+ const { source, sourceContainer, sensorEvent } = dragEvent;
+
+ const mirrorDestroyEvent = new _MirrorEvent.MirrorDestroyEvent({
+ source,
+ mirror: this.mirror,
+ sourceContainer,
+ sensorEvent,
+ dragEvent
+ });
+
+ this.draggable.trigger(mirrorDestroyEvent);
+
+ if (!mirrorDestroyEvent.canceled()) {
+ this.mirror.parentNode.removeChild(this.mirror);
+ }
+ }
+
+ [onScroll]() {
+ this.scrollOffset = {
+ x: window.scrollX - this.initialScrollOffset.x,
+ y: window.scrollY - this.initialScrollOffset.y
+ };
+ }
+
+ /**
+ * Mirror created handler
+ * @param {MirrorCreatedEvent} mirrorEvent
+ * @return {Promise}
+ * @private
+ */
+ [onMirrorCreated]({ mirror, source, sensorEvent }) {
+ const mirrorClasses = this.draggable.getClassNamesFor('mirror');
+
+ const setState = (_ref) => {
+ let { mirrorOffset, initialX, initialY } = _ref,
+ args = _objectWithoutProperties(_ref, ['mirrorOffset', 'initialX', 'initialY']);
+
+ this.mirrorOffset = mirrorOffset;
+ this.initialX = initialX;
+ this.initialY = initialY;
+ this.lastMovedX = initialX;
+ this.lastMovedY = initialY;
+ return _extends({ mirrorOffset, initialX, initialY }, args);
+ };
+
+ mirror.style.display = 'none';
+
+ const initialState = {
+ mirror,
+ source,
+ sensorEvent,
+ mirrorClasses,
+ scrollOffset: this.scrollOffset,
+ options: this.options,
+ passedThreshX: true,
+ passedThreshY: true
+ };
+
+ return Promise.resolve(initialState)
+ // Fix reflow here
+ .then(computeMirrorDimensions).then(calculateMirrorOffset).then(resetMirror).then(addMirrorClasses).then(positionMirror({ initial: true })).then(removeMirrorID).then(setState);
+ }
+
+ /**
+ * Mirror move handler
+ * @param {MirrorMoveEvent} mirrorEvent
+ * @return {Promise|null}
+ * @private
+ */
+ [onMirrorMove](mirrorEvent) {
+ if (mirrorEvent.canceled()) {
+ return null;
+ }
+
+ const setState = (_ref2) => {
+ let { lastMovedX, lastMovedY } = _ref2,
+ args = _objectWithoutProperties(_ref2, ['lastMovedX', 'lastMovedY']);
+
+ this.lastMovedX = lastMovedX;
+ this.lastMovedY = lastMovedY;
+
+ return _extends({ lastMovedX, lastMovedY }, args);
+ };
+
+ const initialState = {
+ mirror: mirrorEvent.mirror,
+ sensorEvent: mirrorEvent.sensorEvent,
+ mirrorOffset: this.mirrorOffset,
+ options: this.options,
+ initialX: this.initialX,
+ initialY: this.initialY,
+ scrollOffset: this.scrollOffset,
+ passedThreshX: mirrorEvent.passedThreshX,
+ passedThreshY: mirrorEvent.passedThreshY,
+ lastMovedX: this.lastMovedX,
+ lastMovedY: this.lastMovedY
+ };
+
+ return Promise.resolve(initialState).then(positionMirror({ raf: true })).then(setState);
+ }
+
+ /**
+ * Returns appendable container for mirror based on the appendTo option
+ * @private
+ * @param {Object} options
+ * @param {HTMLElement} options.source - Current source
+ * @return {HTMLElement}
+ */
+ [getAppendableContainer](source) {
+ const appendTo = this.options.appendTo;
+
+ if (typeof appendTo === 'string') {
+ return document.querySelector(appendTo);
+ } else if (appendTo instanceof HTMLElement) {
+ return appendTo;
+ } else if (typeof appendTo === 'function') {
+ return appendTo(source);
+ } else {
+ return source.parentNode;
+ }
+ }
+ }
+
+ exports.default = Mirror; /**
+ * Computes mirror dimensions based on the source element
+ * Adds sourceRect to state
+ * @param {Object} state
+ * @param {HTMLElement} state.source
+ * @return {Promise}
+ * @private
+ */
+
+ function computeMirrorDimensions(_ref3) {
+ let { source } = _ref3,
+ args = _objectWithoutProperties(_ref3, ['source']);
+
+ return withPromise(resolve => {
+ const sourceRect = source.getBoundingClientRect();
+ resolve(_extends({ source, sourceRect }, args));
+ });
+ }
+
+ /**
+ * Calculates mirror offset
+ * Adds mirrorOffset to state
+ * @param {Object} state
+ * @param {SensorEvent} state.sensorEvent
+ * @param {DOMRect} state.sourceRect
+ * @return {Promise}
+ * @private
+ */
+ function calculateMirrorOffset(_ref4) {
+ let { sensorEvent, sourceRect, options } = _ref4,
+ args = _objectWithoutProperties(_ref4, ['sensorEvent', 'sourceRect', 'options']);
+
+ return withPromise(resolve => {
+ const top = options.cursorOffsetY === null ? sensorEvent.clientY - sourceRect.top : options.cursorOffsetY;
+ const left = options.cursorOffsetX === null ? sensorEvent.clientX - sourceRect.left : options.cursorOffsetX;
+
+ const mirrorOffset = { top, left };
+
+ resolve(_extends({ sensorEvent, sourceRect, mirrorOffset, options }, args));
+ });
+ }
+
+ /**
+ * Applys mirror styles
+ * @param {Object} state
+ * @param {HTMLElement} state.mirror
+ * @param {HTMLElement} state.source
+ * @param {Object} state.options
+ * @return {Promise}
+ * @private
+ */
+ function resetMirror(_ref5) {
+ let { mirror, source, options } = _ref5,
+ args = _objectWithoutProperties(_ref5, ['mirror', 'source', 'options']);
+
+ return withPromise(resolve => {
+ let offsetHeight;
+ let offsetWidth;
+
+ if (options.constrainDimensions) {
+ const computedSourceStyles = getComputedStyle(source);
+ offsetHeight = computedSourceStyles.getPropertyValue('height');
+ offsetWidth = computedSourceStyles.getPropertyValue('width');
+ }
+
+ mirror.style.display = null;
+ mirror.style.position = 'fixed';
+ mirror.style.pointerEvents = 'none';
+ mirror.style.top = 0;
+ mirror.style.left = 0;
+ mirror.style.margin = 0;
+
+ if (options.constrainDimensions) {
+ mirror.style.height = offsetHeight;
+ mirror.style.width = offsetWidth;
+ }
+
+ resolve(_extends({ mirror, source, options }, args));
+ });
+ }
+
+ /**
+ * Applys mirror class on mirror element
+ * @param {Object} state
+ * @param {HTMLElement} state.mirror
+ * @param {String[]} state.mirrorClasses
+ * @return {Promise}
+ * @private
+ */
+ function addMirrorClasses(_ref6) {
+ let { mirror, mirrorClasses } = _ref6,
+ args = _objectWithoutProperties(_ref6, ['mirror', 'mirrorClasses']);
+
+ return withPromise(resolve => {
+ mirror.classList.add(...mirrorClasses);
+ resolve(_extends({ mirror, mirrorClasses }, args));
+ });
+ }
+
+ /**
+ * Removes source ID from cloned mirror element
+ * @param {Object} state
+ * @param {HTMLElement} state.mirror
+ * @return {Promise}
+ * @private
+ */
+ function removeMirrorID(_ref7) {
+ let { mirror } = _ref7,
+ args = _objectWithoutProperties(_ref7, ['mirror']);
+
+ return withPromise(resolve => {
+ mirror.removeAttribute('id');
+ delete mirror.id;
+ resolve(_extends({ mirror }, args));
+ });
+ }
+
+ /**
+ * Positions mirror with translate3d
+ * @param {Object} state
+ * @param {HTMLElement} state.mirror
+ * @param {SensorEvent} state.sensorEvent
+ * @param {Object} state.mirrorOffset
+ * @param {Number} state.initialY
+ * @param {Number} state.initialX
+ * @param {Object} state.options
+ * @return {Promise}
+ * @private
+ */
+ function positionMirror({ withFrame = false, initial = false } = {}) {
+ return (_ref8) => {
+ let {
+ mirror,
+ sensorEvent,
+ mirrorOffset,
+ initialY,
+ initialX,
+ scrollOffset,
+ options,
+ passedThreshX,
+ passedThreshY,
+ lastMovedX,
+ lastMovedY
+ } = _ref8,
+ args = _objectWithoutProperties(_ref8, ['mirror', 'sensorEvent', 'mirrorOffset', 'initialY', 'initialX', 'scrollOffset', 'options', 'passedThreshX', 'passedThreshY', 'lastMovedX', 'lastMovedY']);
+
+ return withPromise(resolve => {
+ const result = _extends({
+ mirror,
+ sensorEvent,
+ mirrorOffset,
+ options
+ }, args);
+
+ if (mirrorOffset) {
+ const x = passedThreshX ? Math.round((sensorEvent.clientX - mirrorOffset.left - scrollOffset.x) / (options.thresholdX || 1)) * (options.thresholdX || 1) : Math.round(lastMovedX);
+ const y = passedThreshY ? Math.round((sensorEvent.clientY - mirrorOffset.top - scrollOffset.y) / (options.thresholdY || 1)) * (options.thresholdY || 1) : Math.round(lastMovedY);
+
+ if (options.xAxis && options.yAxis || initial) {
+ mirror.style.transform = `translate3d(${x}px, ${y}px, 0)`;
+ } else if (options.xAxis && !options.yAxis) {
+ mirror.style.transform = `translate3d(${x}px, ${initialY}px, 0)`;
+ } else if (options.yAxis && !options.xAxis) {
+ mirror.style.transform = `translate3d(${initialX}px, ${y}px, 0)`;
+ }
+
+ if (initial) {
+ result.initialX = x;
+ result.initialY = y;
+ }
+
+ result.lastMovedX = x;
+ result.lastMovedY = y;
+ }
+
+ resolve(result);
+ }, { frame: withFrame });
+ };
+ }
+
+ /**
+ * Wraps functions in promise with potential animation frame option
+ * @param {Function} callback
+ * @param {Object} options
+ * @param {Boolean} options.raf
+ * @return {Promise}
+ * @private
+ */
+ function withPromise(callback, { raf = false } = {}) {
+ return new Promise((resolve, reject) => {
+ if (raf) {
+ requestAnimationFrame(() => {
+ callback(resolve, reject);
+ });
+ } else {
+ callback(resolve, reject);
+ }
+ });
+ }
+
+ /**
+ * Returns true if the sensor event was triggered by a native browser drag event
+ * @param {SensorEvent} sensorEvent
+ */
+ function isNativeDragEvent(sensorEvent) {
+ return (/^drag/.test(sensorEvent.originalEvent.type)
+ );
+ }
+
+ /***/ }),
+ /* 36 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.defaultOptions = undefined;
+
+ var _Mirror = __webpack_require__(35);
+
+ var _Mirror2 = _interopRequireDefault(_Mirror);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _Mirror2.default;
+ exports.defaultOptions = _Mirror.defaultOptions;
+
+ /***/ }),
+ /* 37 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+ var _AbstractPlugin = __webpack_require__(4);
+
+ var _AbstractPlugin2 = _interopRequireDefault(_AbstractPlugin);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onInitialize = Symbol('onInitialize');
+ const onDestroy = Symbol('onDestroy');
+
+ /**
+ * Focusable default options
+ * @property {Object} defaultOptions
+ * @type {Object}
+ */
+ const defaultOptions = {};
+
+ /**
+ * Focusable plugin
+ * @class Focusable
+ * @module Focusable
+ * @extends AbstractPlugin
+ */
+ class Focusable extends _AbstractPlugin2.default {
+ /**
+ * Focusable constructor.
+ * @constructs Focusable
+ * @param {Draggable} draggable - Draggable instance
+ */
+ constructor(draggable) {
+ super(draggable);
+
+ /**
+ * Focusable options
+ * @property {Object} options
+ * @type {Object}
+ */
+ this.options = _extends({}, defaultOptions, this.getOptions());
+
+ this[onInitialize] = this[onInitialize].bind(this);
+ this[onDestroy] = this[onDestroy].bind(this);
+ }
+
+ /**
+ * Attaches listeners to draggable
+ */
+ attach() {
+ this.draggable.on('draggable:initialize', this[onInitialize]).on('draggable:destroy', this[onDestroy]);
+ }
+
+ /**
+ * Detaches listeners from draggable
+ */
+ detach() {
+ this.draggable.off('draggable:initialize', this[onInitialize]).off('draggable:destroy', this[onDestroy]);
+
+ // Remove modified elements when detach
+ this[onDestroy]();
+ }
+
+ /**
+ * Returns options passed through draggable
+ * @return {Object}
+ */
+ getOptions() {
+ return this.draggable.options.focusable || {};
+ }
+
+ /**
+ * Returns draggable containers and elements
+ * @return {HTMLElement[]}
+ */
+ getElements() {
+ return [...this.draggable.containers, ...this.draggable.getDraggableElements()];
+ }
+
+ /**
+ * Intialize handler
+ * @private
+ */
+ [onInitialize]() {
+ // Can wait until the next best frame is available
+ requestAnimationFrame(() => {
+ this.getElements().forEach(element => decorateElement(element));
+ });
+ }
+
+ /**
+ * Destroy handler
+ * @private
+ */
+ [onDestroy]() {
+ // Can wait until the next best frame is available
+ requestAnimationFrame(() => {
+ this.getElements().forEach(element => stripElement(element));
+ });
+ }
+ }
+
+ exports.default = Focusable; /**
+ * Keeps track of all the elements that are missing tabindex attributes
+ * so they can be reset when draggable gets destroyed
+ * @const {HTMLElement[]} elementsWithMissingTabIndex
+ */
+
+ const elementsWithMissingTabIndex = [];
+
+ /**
+ * Decorates element with tabindex attributes
+ * @param {HTMLElement} element
+ * @return {Object}
+ * @private
+ */
+ function decorateElement(element) {
+ const hasMissingTabIndex = Boolean(!element.getAttribute('tabindex') && element.tabIndex === -1);
+
+ if (hasMissingTabIndex) {
+ elementsWithMissingTabIndex.push(element);
+ element.tabIndex = 0;
+ }
+ }
+
+ /**
+ * Removes elements tabindex attributes
+ * @param {HTMLElement} element
+ * @private
+ */
+ function stripElement(element) {
+ const tabIndexElementPosition = elementsWithMissingTabIndex.indexOf(element);
+
+ if (tabIndexElementPosition !== -1) {
+ element.tabIndex = -1;
+ elementsWithMissingTabIndex.splice(tabIndexElementPosition, 1);
+ }
+ }
+
+ /***/ }),
+ /* 38 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _Focusable = __webpack_require__(37);
+
+ var _Focusable2 = _interopRequireDefault(_Focusable);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _Focusable2.default;
+
+ /***/ }),
+ /* 39 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ /**
+ * All draggable plugins inherit from this class.
+ * @abstract
+ * @class AbstractPlugin
+ * @module AbstractPlugin
+ */
+ class AbstractPlugin {
+ /**
+ * AbstractPlugin constructor.
+ * @constructs AbstractPlugin
+ * @param {Draggable} draggable - Draggable instance
+ */
+ constructor(draggable) {
+ /**
+ * Draggable instance
+ * @property draggable
+ * @type {Draggable}
+ */
+ this.draggable = draggable;
+ }
+
+ /**
+ * Override to add listeners
+ * @abstract
+ */
+ attach() {
+ throw new Error('Not Implemented');
+ }
+
+ /**
+ * Override to remove listeners
+ * @abstract
+ */
+ detach() {
+ throw new Error('Not Implemented');
+ }
+ }
+ exports.default = AbstractPlugin;
+
+ /***/ }),
+ /* 40 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.defaultOptions = undefined;
+
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+ var _AbstractPlugin = __webpack_require__(4);
+
+ var _AbstractPlugin2 = _interopRequireDefault(_AbstractPlugin);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onInitialize = Symbol('onInitialize');
+ const onDestroy = Symbol('onDestroy');
+ const announceEvent = Symbol('announceEvent');
+ const announceMessage = Symbol('announceMessage');
+
+ const ARIA_RELEVANT = 'aria-relevant';
+ const ARIA_ATOMIC = 'aria-atomic';
+ const ARIA_LIVE = 'aria-live';
+ const ROLE = 'role';
+
+ /**
+ * Announcement default options
+ * @property {Object} defaultOptions
+ * @property {Number} defaultOptions.expire
+ * @type {Object}
+ */
+ const defaultOptions = exports.defaultOptions = {
+ expire: 7000
+ };
+
+ /**
+ * Announcement plugin
+ * @class Announcement
+ * @module Announcement
+ * @extends AbstractPlugin
+ */
+ class Announcement extends _AbstractPlugin2.default {
+ /**
+ * Announcement constructor.
+ * @constructs Announcement
+ * @param {Draggable} draggable - Draggable instance
+ */
+ constructor(draggable) {
+ super(draggable);
+
+ /**
+ * Plugin options
+ * @property options
+ * @type {Object}
+ */
+ this.options = _extends({}, defaultOptions, this.getOptions());
+
+ /**
+ * Original draggable trigger method. Hack until we have onAll or on('all')
+ * @property originalTriggerMethod
+ * @type {Function}
+ */
+ this.originalTriggerMethod = this.draggable.trigger;
+
+ this[onInitialize] = this[onInitialize].bind(this);
+ this[onDestroy] = this[onDestroy].bind(this);
+ }
+
+ /**
+ * Attaches listeners to draggable
+ */
+ attach() {
+ this.draggable.on('draggable:initialize', this[onInitialize]);
+ }
+
+ /**
+ * Detaches listeners from draggable
+ */
+ detach() {
+ this.draggable.off('draggable:destroy', this[onDestroy]);
+ }
+
+ /**
+ * Returns passed in options
+ */
+ getOptions() {
+ return this.draggable.options.announcements || {};
+ }
+
+ /**
+ * Announces event
+ * @private
+ * @param {AbstractEvent} event
+ */
+ [announceEvent](event) {
+ const message = this.options[event.type];
+
+ if (message && typeof message === 'string') {
+ this[announceMessage](message);
+ }
+
+ if (message && typeof message === 'function') {
+ this[announceMessage](message(event));
+ }
+ }
+
+ /**
+ * Announces message to screen reader
+ * @private
+ * @param {String} message
+ */
+ [announceMessage](message) {
+ announce(message, { expire: this.options.expire });
+ }
+
+ /**
+ * Initialize hander
+ * @private
+ */
+ [onInitialize]() {
+ // Hack until there is an api for listening for all events
+ this.draggable.trigger = event => {
+ try {
+ this[announceEvent](event);
+ } finally {
+ // Ensure that original trigger is called
+ this.originalTriggerMethod.call(this.draggable, event);
+ }
+ };
+ }
+
+ /**
+ * Destroy hander
+ * @private
+ */
+ [onDestroy]() {
+ this.draggable.trigger = this.originalTriggerMethod;
+ }
+ }
+
+ exports.default = Announcement; /**
+ * @const {HTMLElement} liveRegion
+ */
+
+ const liveRegion = createRegion();
+
+ /**
+ * Announces message via live region
+ * @param {String} message
+ * @param {Object} options
+ * @param {Number} options.expire
+ */
+ function announce(message, { expire }) {
+ const element = document.createElement('div');
+
+ element.textContent = message;
+ liveRegion.appendChild(element);
+
+ return setTimeout(() => {
+ liveRegion.removeChild(element);
+ }, expire);
+ }
+
+ /**
+ * Creates region element
+ * @return {HTMLElement}
+ */
+ function createRegion() {
+ const element = document.createElement('div');
+
+ element.setAttribute('id', 'draggable-live-region');
+ element.setAttribute(ARIA_RELEVANT, 'additions');
+ element.setAttribute(ARIA_ATOMIC, 'true');
+ element.setAttribute(ARIA_LIVE, 'assertive');
+ element.setAttribute(ROLE, 'log');
+
+ element.style.position = 'fixed';
+ element.style.width = '1px';
+ element.style.height = '1px';
+ element.style.top = '-1px';
+ element.style.overflow = 'hidden';
+
+ return element;
+ }
+
+// Append live region element as early as possible
+ document.addEventListener('DOMContentLoaded', () => {
+ document.body.appendChild(liveRegion);
+ });
+
+ /***/ }),
+ /* 41 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.defaultOptions = undefined;
+
+ var _Announcement = __webpack_require__(40);
+
+ var _Announcement2 = _interopRequireDefault(_Announcement);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _Announcement2.default;
+ exports.defaultOptions = _Announcement.defaultOptions;
+
+ /***/ }),
+ /* 42 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.DraggableDestroyEvent = exports.DraggableInitializedEvent = exports.DraggableEvent = undefined;
+
+ var _AbstractEvent = __webpack_require__(3);
+
+ var _AbstractEvent2 = _interopRequireDefault(_AbstractEvent);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ /**
+ * Base draggable event
+ * @class DraggableEvent
+ * @module DraggableEvent
+ * @extends AbstractEvent
+ */
+ class DraggableEvent extends _AbstractEvent2.default {
+
+ /**
+ * Draggable instance
+ * @property draggable
+ * @type {Draggable}
+ * @readonly
+ */
+ get draggable() {
+ return this.data.draggable;
+ }
+ }
+
+ exports.DraggableEvent = DraggableEvent; /**
+ * Draggable initialized event
+ * @class DraggableInitializedEvent
+ * @module DraggableInitializedEvent
+ * @extends DraggableEvent
+ */
+
+ DraggableEvent.type = 'draggable';
+ class DraggableInitializedEvent extends DraggableEvent {}
+
+ exports.DraggableInitializedEvent = DraggableInitializedEvent; /**
+ * Draggable destory event
+ * @class DraggableInitializedEvent
+ * @module DraggableDestroyEvent
+ * @extends DraggableDestroyEvent
+ */
+
+ DraggableInitializedEvent.type = 'draggable:initialize';
+ class DraggableDestroyEvent extends DraggableEvent {}
+ exports.DraggableDestroyEvent = DraggableDestroyEvent;
+ DraggableDestroyEvent.type = 'draggable:destroy';
+
+ /***/ }),
+ /* 43 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.DragStoppedEvent = exports.DragStopEvent = exports.DragPressureEvent = exports.DragOutContainerEvent = exports.DragOverContainerEvent = exports.DragOutEvent = exports.DragOverEvent = exports.DragMoveEvent = exports.DragStartEvent = exports.DragEvent = undefined;
+
+ var _AbstractEvent = __webpack_require__(3);
+
+ var _AbstractEvent2 = _interopRequireDefault(_AbstractEvent);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ /**
+ * Base drag event
+ * @class DragEvent
+ * @module DragEvent
+ * @extends AbstractEvent
+ */
+ class DragEvent extends _AbstractEvent2.default {
+
+ /**
+ * Draggables source element
+ * @property source
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get source() {
+ return this.data.source;
+ }
+
+ /**
+ * Draggables original source element
+ * @property originalSource
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get originalSource() {
+ return this.data.originalSource;
+ }
+
+ /**
+ * Draggables mirror element
+ * @property mirror
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get mirror() {
+ return this.data.mirror;
+ }
+
+ /**
+ * Draggables source container element
+ * @property sourceContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get sourceContainer() {
+ return this.data.sourceContainer;
+ }
+
+ /**
+ * Sensor event
+ * @property sensorEvent
+ * @type {SensorEvent}
+ * @readonly
+ */
+ get sensorEvent() {
+ return this.data.sensorEvent;
+ }
+
+ /**
+ * Original event that triggered sensor event
+ * @property originalEvent
+ * @type {Event}
+ * @readonly
+ */
+ get originalEvent() {
+ if (this.sensorEvent) {
+ return this.sensorEvent.originalEvent;
+ }
+
+ return null;
+ }
+ }
+
+ exports.DragEvent = DragEvent; /**
+ * Drag start event
+ * @class DragStartEvent
+ * @module DragStartEvent
+ * @extends DragEvent
+ */
+
+ DragEvent.type = 'drag';
+ class DragStartEvent extends DragEvent {}
+
+ exports.DragStartEvent = DragStartEvent; /**
+ * Drag move event
+ * @class DragMoveEvent
+ * @module DragMoveEvent
+ * @extends DragEvent
+ */
+
+ DragStartEvent.type = 'drag:start';
+ DragStartEvent.cancelable = true;
+ class DragMoveEvent extends DragEvent {}
+
+ exports.DragMoveEvent = DragMoveEvent; /**
+ * Drag over event
+ * @class DragOverEvent
+ * @module DragOverEvent
+ * @extends DragEvent
+ */
+
+ DragMoveEvent.type = 'drag:move';
+ class DragOverEvent extends DragEvent {
+
+ /**
+ * Draggable container you are over
+ * @property overContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get overContainer() {
+ return this.data.overContainer;
+ }
+
+ /**
+ * Draggable element you are over
+ * @property over
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get over() {
+ return this.data.over;
+ }
+ }
+
+ exports.DragOverEvent = DragOverEvent; /**
+ * Drag out event
+ * @class DragOutEvent
+ * @module DragOutEvent
+ * @extends DragEvent
+ */
+
+ DragOverEvent.type = 'drag:over';
+ DragOverEvent.cancelable = true;
+ class DragOutEvent extends DragEvent {
+
+ /**
+ * Draggable container you are over
+ * @property overContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get overContainer() {
+ return this.data.overContainer;
+ }
+
+ /**
+ * Draggable element you left
+ * @property over
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get over() {
+ return this.data.over;
+ }
+ }
+
+ exports.DragOutEvent = DragOutEvent; /**
+ * Drag over container event
+ * @class DragOverContainerEvent
+ * @module DragOverContainerEvent
+ * @extends DragEvent
+ */
+
+ DragOutEvent.type = 'drag:out';
+ class DragOverContainerEvent extends DragEvent {
+
+ /**
+ * Draggable container you are over
+ * @property overContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get overContainer() {
+ return this.data.overContainer;
+ }
+ }
+
+ exports.DragOverContainerEvent = DragOverContainerEvent; /**
+ * Drag out container event
+ * @class DragOutContainerEvent
+ * @module DragOutContainerEvent
+ * @extends DragEvent
+ */
+
+ DragOverContainerEvent.type = 'drag:over:container';
+ class DragOutContainerEvent extends DragEvent {
+
+ /**
+ * Draggable container you left
+ * @property overContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get overContainer() {
+ return this.data.overContainer;
+ }
+ }
+
+ exports.DragOutContainerEvent = DragOutContainerEvent; /**
+ * Drag pressure event
+ * @class DragPressureEvent
+ * @module DragPressureEvent
+ * @extends DragEvent
+ */
+
+ DragOutContainerEvent.type = 'drag:out:container';
+ class DragPressureEvent extends DragEvent {
+
+ /**
+ * Pressure applied on draggable element
+ * @property pressure
+ * @type {Number}
+ * @readonly
+ */
+ get pressure() {
+ return this.data.pressure;
+ }
+ }
+
+ exports.DragPressureEvent = DragPressureEvent; /**
+ * Drag stop event
+ * @class DragStopEvent
+ * @module DragStopEvent
+ * @extends DragEvent
+ */
+
+ DragPressureEvent.type = 'drag:pressure';
+ class DragStopEvent extends DragEvent {}
+
+ exports.DragStopEvent = DragStopEvent; /**
+ * Drag stopped event
+ * @class DragStoppedEvent
+ * @module DragStoppedEvent
+ * @extends DragEvent
+ */
+
+ DragStopEvent.type = 'drag:stop';
+ class DragStoppedEvent extends DragEvent {}
+ exports.DragStoppedEvent = DragStoppedEvent;
+ DragStoppedEvent.type = 'drag:stopped';
+
+ /***/ }),
+ /* 44 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _DragEvent = __webpack_require__(8);
+
+ Object.keys(_DragEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _DragEvent[key];
+ }
+ });
+ });
+
+ var _DraggableEvent = __webpack_require__(7);
+
+ Object.keys(_DraggableEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _DraggableEvent[key];
+ }
+ });
+ });
+
+ var _Plugins = __webpack_require__(6);
+
+ Object.keys(_Plugins).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _Plugins[key];
+ }
+ });
+ });
+
+ var _Sensors = __webpack_require__(5);
+
+ Object.keys(_Sensors).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _Sensors[key];
+ }
+ });
+ });
+
+ var _Draggable = __webpack_require__(12);
+
+ var _Draggable2 = _interopRequireDefault(_Draggable);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _Draggable2.default;
+
+ /***/ }),
+ /* 45 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+ var _Draggable = __webpack_require__(44);
+
+ var _Draggable2 = _interopRequireDefault(_Draggable);
+
+ var _SortableEvent = __webpack_require__(9);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ const onDragStart = Symbol('onDragStart');
+ const onDragOverContainer = Symbol('onDragOverContainer');
+ const onDragOver = Symbol('onDragOver');
+ const onDragStop = Symbol('onDragStop');
+
+ /**
+ * Returns announcement message when a Draggable element has been sorted with another Draggable element
+ * or moved into a new container
+ * @param {SortableSortedEvent} sortableEvent
+ * @return {String}
+ */
+ function onSortableSortedDefaultAnnouncement({ dragEvent }) {
+ const sourceText = dragEvent.source.textContent.trim() || dragEvent.source.id || 'sortable element';
+
+ if (dragEvent.over) {
+ const overText = dragEvent.over.textContent.trim() || dragEvent.over.id || 'sortable element';
+ const isFollowing = dragEvent.source.compareDocumentPosition(dragEvent.over) & Node.DOCUMENT_POSITION_FOLLOWING;
+
+ if (isFollowing) {
+ return `Placed ${sourceText} after ${overText}`;
+ } else {
+ return `Placed ${sourceText} before ${overText}`;
+ }
+ } else {
+ // need to figure out how to compute container name
+ return `Placed ${sourceText} into a different container`;
+ }
+ }
+
+ /**
+ * @const {Object} defaultAnnouncements
+ * @const {Function} defaultAnnouncements['sortable:sorted']
+ */
+ const defaultAnnouncements = {
+ 'sortable:sorted': onSortableSortedDefaultAnnouncement
+ };
+
+ /**
+ * Sortable is built on top of Draggable and allows sorting of draggable elements. Sortable will keep
+ * track of the original index and emits the new index as you drag over draggable elements.
+ * @class Sortable
+ * @module Sortable
+ * @extends Draggable
+ */
+ class Sortable extends _Draggable2.default {
+ /**
+ * Sortable constructor.
+ * @constructs Sortable
+ * @param {HTMLElement[]|NodeList|HTMLElement} containers - Sortable containers
+ * @param {Object} options - Options for Sortable
+ */
+ constructor(containers = [], options = {}) {
+ super(containers, _extends({}, options, {
+ announcements: _extends({}, defaultAnnouncements, options.announcements || {})
+ }));
+
+ /**
+ * start index of source on drag start
+ * @property startIndex
+ * @type {Number}
+ */
+ this.startIndex = null;
+
+ /**
+ * start container on drag start
+ * @property startContainer
+ * @type {HTMLElement}
+ * @default null
+ */
+ this.startContainer = null;
+
+ this[onDragStart] = this[onDragStart].bind(this);
+ this[onDragOverContainer] = this[onDragOverContainer].bind(this);
+ this[onDragOver] = this[onDragOver].bind(this);
+ this[onDragStop] = this[onDragStop].bind(this);
+
+ this.on('drag:start', this[onDragStart]).on('drag:over:container', this[onDragOverContainer]).on('drag:over', this[onDragOver]).on('drag:stop', this[onDragStop]);
+ }
+
+ /**
+ * Destroys Sortable instance.
+ */
+ destroy() {
+ super.destroy();
+
+ this.off('drag:start', this[onDragStart]).off('drag:over:container', this[onDragOverContainer]).off('drag:over', this[onDragOver]).off('drag:stop', this[onDragStop]);
+ }
+
+ /**
+ * Returns true index of element within its container during drag operation, i.e. excluding mirror and original source
+ * @param {HTMLElement} element - An element
+ * @return {Number}
+ */
+ index(element) {
+ return this.getSortableElementsForContainer(element.parentNode).indexOf(element);
+ }
+
+ /**
+ * Returns sortable elements for a given container, excluding the mirror and
+ * original source element if present
+ * @param {HTMLElement} container
+ * @return {HTMLElement[]}
+ */
+ getSortableElementsForContainer(container) {
+ const allSortableElements = container.querySelectorAll(this.options.draggable);
+
+ return [...allSortableElements].filter(childElement => {
+ return childElement !== this.originalSource && childElement !== this.mirror && childElement.parentNode === container;
+ });
+ }
+
+ /**
+ * Drag start handler
+ * @private
+ * @param {DragStartEvent} event - Drag start event
+ */
+ [onDragStart](event) {
+ this.startContainer = event.source.parentNode;
+ this.startIndex = this.index(event.source);
+
+ const sortableStartEvent = new _SortableEvent.SortableStartEvent({
+ dragEvent: event,
+ startIndex: this.startIndex,
+ startContainer: this.startContainer
+ });
+
+ this.trigger(sortableStartEvent);
+
+ if (sortableStartEvent.canceled()) {
+ event.cancel();
+ }
+ }
+
+ /**
+ * Drag over container handler
+ * @private
+ * @param {DragOverContainerEvent} event - Drag over container event
+ */
+ [onDragOverContainer](event) {
+ if (event.canceled()) {
+ return;
+ }
+
+ const { source, over, overContainer } = event;
+ const oldIndex = this.index(source);
+
+ const sortableSortEvent = new _SortableEvent.SortableSortEvent({
+ dragEvent: event,
+ currentIndex: oldIndex,
+ source,
+ over
+ });
+
+ this.trigger(sortableSortEvent);
+
+ if (sortableSortEvent.canceled()) {
+ return;
+ }
+
+ const children = this.getSortableElementsForContainer(overContainer);
+ const moves = move({ source, over, overContainer, children });
+
+ if (!moves) {
+ return;
+ }
+
+ const { oldContainer, newContainer } = moves;
+ const newIndex = this.index(event.source);
+
+ const sortableSortedEvent = new _SortableEvent.SortableSortedEvent({
+ dragEvent: event,
+ oldIndex,
+ newIndex,
+ oldContainer,
+ newContainer
+ });
+
+ this.trigger(sortableSortedEvent);
+ }
+
+ /**
+ * Drag over handler
+ * @private
+ * @param {DragOverEvent} event - Drag over event
+ */
+ [onDragOver](event) {
+ if (event.over === event.originalSource || event.over === event.source) {
+ return;
+ }
+
+ const { source, over, overContainer } = event;
+ const oldIndex = this.index(source);
+
+ const sortableSortEvent = new _SortableEvent.SortableSortEvent({
+ dragEvent: event,
+ currentIndex: oldIndex,
+ source,
+ over
+ });
+
+ this.trigger(sortableSortEvent);
+
+ if (sortableSortEvent.canceled()) {
+ return;
+ }
+
+ const children = this.getDraggableElementsForContainer(overContainer);
+ const moves = move({ source, over, overContainer, children });
+
+ if (!moves) {
+ return;
+ }
+
+ const { oldContainer, newContainer } = moves;
+ const newIndex = this.index(source);
+
+ const sortableSortedEvent = new _SortableEvent.SortableSortedEvent({
+ dragEvent: event,
+ oldIndex,
+ newIndex,
+ oldContainer,
+ newContainer
+ });
+
+ this.trigger(sortableSortedEvent);
+ }
+
+ /**
+ * Drag stop handler
+ * @private
+ * @param {DragStopEvent} event - Drag stop event
+ */
+ [onDragStop](event) {
+ const sortableStopEvent = new _SortableEvent.SortableStopEvent({
+ dragEvent: event,
+ oldIndex: this.startIndex,
+ newIndex: this.index(event.source),
+ oldContainer: this.startContainer,
+ newContainer: event.source.parentNode
+ });
+
+ this.trigger(sortableStopEvent);
+
+ this.startIndex = null;
+ this.startContainer = null;
+ }
+ }
+
+ exports.default = Sortable;
+ function index(element) {
+ return Array.prototype.indexOf.call(element.parentNode.children, element);
+ }
+
+ function move({ source, over, overContainer, children }) {
+ const emptyOverContainer = !children.length;
+ const differentContainer = source.parentNode !== overContainer;
+ const sameContainer = over && source.parentNode === over.parentNode;
+
+ if (emptyOverContainer) {
+ return moveInsideEmptyContainer(source, overContainer);
+ } else if (sameContainer) {
+ return moveWithinContainer(source, over);
+ } else if (differentContainer) {
+ return moveOutsideContainer(source, over, overContainer);
+ } else {
+ return null;
+ }
+ }
+
+ function moveInsideEmptyContainer(source, overContainer) {
+ const oldContainer = source.parentNode;
+
+ overContainer.appendChild(source);
+
+ return { oldContainer, newContainer: overContainer };
+ }
+
+ function moveWithinContainer(source, over) {
+ const oldIndex = index(source);
+ const newIndex = index(over);
+
+ if (oldIndex < newIndex) {
+ source.parentNode.insertBefore(source, over.nextElementSibling);
+ } else {
+ source.parentNode.insertBefore(source, over);
+ }
+
+ return { oldContainer: source.parentNode, newContainer: source.parentNode };
+ }
+
+ function moveOutsideContainer(source, over, overContainer) {
+ const oldContainer = source.parentNode;
+
+ if (over) {
+ over.parentNode.insertBefore(source, over);
+ } else {
+ // need to figure out proper position
+ overContainer.appendChild(source);
+ }
+
+ return { oldContainer, newContainer: source.parentNode };
+ }
+
+ /***/ }),
+ /* 46 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+ const canceled = Symbol('canceled');
+
+ /**
+ * All events fired by draggable inherit this class. You can call `cancel()` to
+ * cancel a specific event or you can check if an event has been canceled by
+ * calling `canceled()`.
+ * @abstract
+ * @class AbstractEvent
+ * @module AbstractEvent
+ */
+ class AbstractEvent {
+
+ /**
+ * AbstractEvent constructor.
+ * @constructs AbstractEvent
+ * @param {object} data - Event data
+ */
+
+ /**
+ * Event type
+ * @static
+ * @abstract
+ * @property type
+ * @type {String}
+ */
+ constructor(data) {
+ this[canceled] = false;
+ this.data = data;
+ }
+
+ /**
+ * Read-only type
+ * @abstract
+ * @return {String}
+ */
+
+
+ /**
+ * Event cancelable
+ * @static
+ * @abstract
+ * @property cancelable
+ * @type {Boolean}
+ */
+ get type() {
+ return this.constructor.type;
+ }
+
+ /**
+ * Read-only cancelable
+ * @abstract
+ * @return {Boolean}
+ */
+ get cancelable() {
+ return this.constructor.cancelable;
+ }
+
+ /**
+ * Cancels the event instance
+ * @abstract
+ */
+ cancel() {
+ this[canceled] = true;
+ }
+
+ /**
+ * Check if event has been canceled
+ * @abstract
+ * @return {Boolean}
+ */
+ canceled() {
+ return Boolean(this[canceled]);
+ }
+
+ /**
+ * Returns new event instance with existing event data.
+ * This method allows for overriding of event data.
+ * @param {Object} data
+ * @return {AbstractEvent}
+ */
+ clone(data) {
+ return new this.constructor(_extends({}, this.data, data));
+ }
+ }
+ exports.default = AbstractEvent;
+ AbstractEvent.type = 'event';
+ AbstractEvent.cancelable = false;
+
+ /***/ }),
+ /* 47 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.SortableStopEvent = exports.SortableSortedEvent = exports.SortableSortEvent = exports.SortableStartEvent = exports.SortableEvent = undefined;
+
+ var _AbstractEvent = __webpack_require__(3);
+
+ var _AbstractEvent2 = _interopRequireDefault(_AbstractEvent);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ /**
+ * Base sortable event
+ * @class SortableEvent
+ * @module SortableEvent
+ * @extends AbstractEvent
+ */
+ class SortableEvent extends _AbstractEvent2.default {
+
+ /**
+ * Original drag event that triggered this sortable event
+ * @property dragEvent
+ * @type {DragEvent}
+ * @readonly
+ */
+ get dragEvent() {
+ return this.data.dragEvent;
+ }
+ }
+
+ exports.SortableEvent = SortableEvent; /**
+ * Sortable start event
+ * @class SortableStartEvent
+ * @module SortableStartEvent
+ * @extends SortableEvent
+ */
+
+ SortableEvent.type = 'sortable';
+ class SortableStartEvent extends SortableEvent {
+
+ /**
+ * Start index of source on sortable start
+ * @property startIndex
+ * @type {Number}
+ * @readonly
+ */
+ get startIndex() {
+ return this.data.startIndex;
+ }
+
+ /**
+ * Start container on sortable start
+ * @property startContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get startContainer() {
+ return this.data.startContainer;
+ }
+ }
+
+ exports.SortableStartEvent = SortableStartEvent; /**
+ * Sortable sort event
+ * @class SortableSortEvent
+ * @module SortableSortEvent
+ * @extends SortableEvent
+ */
+
+ SortableStartEvent.type = 'sortable:start';
+ SortableStartEvent.cancelable = true;
+ class SortableSortEvent extends SortableEvent {
+
+ /**
+ * Index of current draggable element
+ * @property currentIndex
+ * @type {Number}
+ * @readonly
+ */
+ get currentIndex() {
+ return this.data.currentIndex;
+ }
+
+ /**
+ * Draggable element you are hovering over
+ * @property over
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get over() {
+ return this.data.over;
+ }
+
+ /**
+ * Draggable container element you are hovering over
+ * @property overContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get overContainer() {
+ return this.data.dragEvent.overContainer;
+ }
+ }
+
+ exports.SortableSortEvent = SortableSortEvent; /**
+ * Sortable sorted event
+ * @class SortableSortedEvent
+ * @module SortableSortedEvent
+ * @extends SortableEvent
+ */
+
+ SortableSortEvent.type = 'sortable:sort';
+ SortableSortEvent.cancelable = true;
+ class SortableSortedEvent extends SortableEvent {
+
+ /**
+ * Index of last sorted event
+ * @property oldIndex
+ * @type {Number}
+ * @readonly
+ */
+ get oldIndex() {
+ return this.data.oldIndex;
+ }
+
+ /**
+ * New index of this sorted event
+ * @property newIndex
+ * @type {Number}
+ * @readonly
+ */
+ get newIndex() {
+ return this.data.newIndex;
+ }
+
+ /**
+ * Old container of draggable element
+ * @property oldContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get oldContainer() {
+ return this.data.oldContainer;
+ }
+
+ /**
+ * New container of draggable element
+ * @property newContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get newContainer() {
+ return this.data.newContainer;
+ }
+ }
+
+ exports.SortableSortedEvent = SortableSortedEvent; /**
+ * Sortable stop event
+ * @class SortableStopEvent
+ * @module SortableStopEvent
+ * @extends SortableEvent
+ */
+
+ SortableSortedEvent.type = 'sortable:sorted';
+ class SortableStopEvent extends SortableEvent {
+
+ /**
+ * Original index on sortable start
+ * @property oldIndex
+ * @type {Number}
+ * @readonly
+ */
+ get oldIndex() {
+ return this.data.oldIndex;
+ }
+
+ /**
+ * New index of draggable element
+ * @property newIndex
+ * @type {Number}
+ * @readonly
+ */
+ get newIndex() {
+ return this.data.newIndex;
+ }
+
+ /**
+ * Original container of draggable element
+ * @property oldContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get oldContainer() {
+ return this.data.oldContainer;
+ }
+
+ /**
+ * New container of draggable element
+ * @property newContainer
+ * @type {HTMLElement}
+ * @readonly
+ */
+ get newContainer() {
+ return this.data.newContainer;
+ }
+ }
+ exports.SortableStopEvent = SortableStopEvent;
+ SortableStopEvent.type = 'sortable:stop';
+
+ /***/ }),
+ /* 48 */
+ /***/ (function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+
+ var _SortableEvent = __webpack_require__(9);
+
+ Object.keys(_SortableEvent).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function () {
+ return _SortableEvent[key];
+ }
+ });
+ });
+
+ var _Sortable = __webpack_require__(45);
+
+ var _Sortable2 = _interopRequireDefault(_Sortable);
+
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+ exports.default = _Sortable2.default;
+
+ /***/ })
+ /******/ ]);
+});
\ No newline at end of file