diff --git a/dist/vis.js b/dist/vis.js index 68278fdc..ccae4d35 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -3144,7 +3144,7 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; - var Emitter = __webpack_require__(43); + var Emitter = __webpack_require__(69); var DataSet = __webpack_require__(3); var DataView = __webpack_require__(4); var util = __webpack_require__(1); @@ -6307,23 +6307,23 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; - var Emitter = __webpack_require__(43); + var Emitter = __webpack_require__(69); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); var DataView = __webpack_require__(4); var Range = __webpack_require__(17); - var Core = __webpack_require__(44); + var Core = __webpack_require__(43); var TimeAxis = __webpack_require__(35); var CurrentTime = __webpack_require__(26); var CustomTime = __webpack_require__(27); var ItemSet = __webpack_require__(32); - var ConfigurationSystem = __webpack_require__(45); - var Validator = __webpack_require__(46)['default']; - var printStyle = __webpack_require__(46).printStyle; - var allOptions = __webpack_require__(47).allOptions; - var configureOptions = __webpack_require__(47).configureOptions; + var ConfigurationSystem = __webpack_require__(44); + var Validator = __webpack_require__(45)['default']; + var printStyle = __webpack_require__(45).printStyle; + var allOptions = __webpack_require__(46).allOptions; + var configureOptions = __webpack_require__(46).configureOptions; /** * Create a timeline visualization @@ -6746,23 +6746,23 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; - var Emitter = __webpack_require__(43); + var Emitter = __webpack_require__(69); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); var DataView = __webpack_require__(4); var Range = __webpack_require__(17); - var Core = __webpack_require__(44); + var Core = __webpack_require__(43); var TimeAxis = __webpack_require__(35); var CurrentTime = __webpack_require__(26); var CustomTime = __webpack_require__(27); var LineGraph = __webpack_require__(34); - var ConfigurationSystem = __webpack_require__(45); - var Validator = __webpack_require__(46)['default']; - var printStyle = __webpack_require__(46).printStyle; - var allOptions = __webpack_require__(48).allOptions; - var configureOptions = __webpack_require__(48).configureOptions; + var ConfigurationSystem = __webpack_require__(44); + var Validator = __webpack_require__(45)['default']; + var printStyle = __webpack_require__(45).printStyle; + var allOptions = __webpack_require__(47).allOptions; + var configureOptions = __webpack_require__(47).configureOptions; /** * Create a timeline visualization @@ -7756,7 +7756,7 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(49); + var hammerUtil = __webpack_require__(48); var moment = __webpack_require__(40); var Component = __webpack_require__(25); var DateUtil = __webpack_require__(15); @@ -10504,7 +10504,7 @@ return /******/ (function(modules) { // webpackBootstrap var util = __webpack_require__(1); var Component = __webpack_require__(25); var moment = __webpack_require__(40); - var locales = __webpack_require__(50); + var locales = __webpack_require__(49); /** * A current time bar @@ -10681,7 +10681,7 @@ return /******/ (function(modules) { // webpackBootstrap var util = __webpack_require__(1); var Component = __webpack_require__(25); var moment = __webpack_require__(40); - var locales = __webpack_require__(50); + var locales = __webpack_require__(49); /** * A custom time bar @@ -11522,9 +11522,9 @@ return /******/ (function(modules) { // webpackBootstrap var util = __webpack_require__(1); var DOMutil = __webpack_require__(2); - var Line = __webpack_require__(51); - var Bar = __webpack_require__(52); - var Points = __webpack_require__(53); + var Line = __webpack_require__(50); + var Bar = __webpack_require__(51); + var Points = __webpack_require__(52); /** * /** @@ -13165,17 +13165,19 @@ return /******/ (function(modules) { // webpackBootstrap ItemSet.prototype._onUpdate = function (ids) { var me = this; - ids.forEach(function (id) { + ids.forEach((function (id) { var itemData = me.itemsData.get(id, me.itemOptions); var item = me.items[id]; var type = me._getType(itemData); var constructor = ItemSet.types[type]; + var selected; if (item) { // update item if (!constructor || !(item instanceof constructor)) { // item type has changed, delete the item and recreate it + selected = item.selected; // preserve selection of this item me._removeItem(item); item = null; } else { @@ -13189,6 +13191,10 @@ return /******/ (function(modules) { // webpackBootstrap item = new constructor(itemData, me.conversion, me.options); item.id = id; // TODO: not so nice setting id afterwards me._addItem(item); + if (selected) { + this.selection.push(id); + item.select(); + } } else if (type == 'rangeoverflow') { // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + '.vis-item.vis-range .vis-item-content {overflow: visible;}'); @@ -13196,7 +13202,7 @@ return /******/ (function(modules) { // webpackBootstrap throw new TypeError('Unknown item type "' + type + '"'); } } - }); + }).bind(this)); this._order(); this.stackDirty = true; // force re-stacking of all items next redraw @@ -14189,8 +14195,8 @@ return /******/ (function(modules) { // webpackBootstrap var DataAxis = __webpack_require__(28); var GraphGroup = __webpack_require__(29); var Legend = __webpack_require__(33); - var BarFunctions = __webpack_require__(52); - var LineFunctions = __webpack_require__(51); + var BarFunctions = __webpack_require__(51); + var LineFunctions = __webpack_require__(50); var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items @@ -15599,68 +15605,68 @@ return /******/ (function(modules) { // webpackBootstrap function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - var _Groups = __webpack_require__(54); + var _modulesGroups = __webpack_require__(53); - var _Groups2 = _interopRequireDefault(_Groups); + var _modulesGroups2 = _interopRequireDefault(_modulesGroups); - var _NodesHandler = __webpack_require__(55); + var _modulesNodesHandler = __webpack_require__(54); - var _NodesHandler2 = _interopRequireDefault(_NodesHandler); + var _modulesNodesHandler2 = _interopRequireDefault(_modulesNodesHandler); - var _EdgesHandler = __webpack_require__(56); + var _modulesEdgesHandler = __webpack_require__(55); - var _EdgesHandler2 = _interopRequireDefault(_EdgesHandler); + var _modulesEdgesHandler2 = _interopRequireDefault(_modulesEdgesHandler); - var _PhysicsEngine = __webpack_require__(57); + var _modulesPhysicsEngine = __webpack_require__(56); - var _PhysicsEngine2 = _interopRequireDefault(_PhysicsEngine); + var _modulesPhysicsEngine2 = _interopRequireDefault(_modulesPhysicsEngine); - var _ClusterEngine = __webpack_require__(58); + var _modulesClustering = __webpack_require__(57); - var _ClusterEngine2 = _interopRequireDefault(_ClusterEngine); + var _modulesClustering2 = _interopRequireDefault(_modulesClustering); - var _CanvasRenderer = __webpack_require__(59); + var _modulesCanvasRenderer = __webpack_require__(58); - var _CanvasRenderer2 = _interopRequireDefault(_CanvasRenderer); + var _modulesCanvasRenderer2 = _interopRequireDefault(_modulesCanvasRenderer); - var _Canvas = __webpack_require__(60); + var _modulesCanvas = __webpack_require__(59); - var _Canvas2 = _interopRequireDefault(_Canvas); + var _modulesCanvas2 = _interopRequireDefault(_modulesCanvas); - var _View = __webpack_require__(61); + var _modulesView = __webpack_require__(60); - var _View2 = _interopRequireDefault(_View); + var _modulesView2 = _interopRequireDefault(_modulesView); - var _InteractionHandler = __webpack_require__(62); + var _modulesInteractionHandler = __webpack_require__(61); - var _InteractionHandler2 = _interopRequireDefault(_InteractionHandler); + var _modulesInteractionHandler2 = _interopRequireDefault(_modulesInteractionHandler); - var _SelectionHandler = __webpack_require__(63); + var _modulesSelectionHandler = __webpack_require__(62); - var _SelectionHandler2 = _interopRequireDefault(_SelectionHandler); + var _modulesSelectionHandler2 = _interopRequireDefault(_modulesSelectionHandler); - var _LayoutEngine = __webpack_require__(64); + var _modulesLayoutEngine = __webpack_require__(63); - var _LayoutEngine2 = _interopRequireDefault(_LayoutEngine); + var _modulesLayoutEngine2 = _interopRequireDefault(_modulesLayoutEngine); - var _ManipulationSystem = __webpack_require__(65); + var _modulesManipulationSystem = __webpack_require__(64); - var _ManipulationSystem2 = _interopRequireDefault(_ManipulationSystem); + var _modulesManipulationSystem2 = _interopRequireDefault(_modulesManipulationSystem); - var _ConfigurationSystem = __webpack_require__(45); + var _sharedConfigurationSystem = __webpack_require__(44); - var _ConfigurationSystem2 = _interopRequireDefault(_ConfigurationSystem); + var _sharedConfigurationSystem2 = _interopRequireDefault(_sharedConfigurationSystem); - var _Validator = __webpack_require__(46); + var _sharedValidator = __webpack_require__(45); - var _Validator2 = _interopRequireDefault(_Validator); + var _sharedValidator2 = _interopRequireDefault(_sharedValidator); - var _allOptions$configureOptions = __webpack_require__(66); + var _optionsJs = __webpack_require__(65); // Load custom shapes into CanvasRenderingContext2D - __webpack_require__(67); + __webpack_require__(66); - var Emitter = __webpack_require__(43); + var Emitter = __webpack_require__(69); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); @@ -15668,8 +15674,8 @@ return /******/ (function(modules) { // webpackBootstrap var dotparser = __webpack_require__(38); var gephiParser = __webpack_require__(39); var Images = __webpack_require__(37); - var Activator = __webpack_require__(68); - var locales = __webpack_require__(69); + var Activator = __webpack_require__(67); + var locales = __webpack_require__(68); /** * @constructor Network @@ -15747,25 +15753,25 @@ return /******/ (function(modules) { // webpackBootstrap this.images = new Images(function () { return _this.body.emitter.emit('_requestRedraw'); }); // object with images - this.groups = new _Groups2['default'](); // object with groups - this.canvas = new _Canvas2['default'](this.body); // DOM handler - this.selectionHandler = new _SelectionHandler2['default'](this.body, this.canvas); // Selection handler - this.interactionHandler = new _InteractionHandler2['default'](this.body, this.canvas, this.selectionHandler); // Interaction handler handles all the hammer bindings (that are bound by canvas), key - this.view = new _View2['default'](this.body, this.canvas); // camera handler, does animations and zooms - this.renderer = new _CanvasRenderer2['default'](this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into - this.physics = new _PhysicsEngine2['default'](this.body); // physics engine, does all the simulations - this.layoutEngine = new _LayoutEngine2['default'](this.body); // layout engine for inital layout and hierarchical layout - this.clustering = new _ClusterEngine2['default'](this.body); // clustering api - this.manipulation = new _ManipulationSystem2['default'](this.body, this.canvas, this.selectionHandler); // data manipulation system - - this.nodesHandler = new _NodesHandler2['default'](this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options - this.edgesHandler = new _EdgesHandler2['default'](this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options + this.groups = new _modulesGroups2['default'](); // object with groups + this.canvas = new _modulesCanvas2['default'](this.body); // DOM handler + this.selectionHandler = new _modulesSelectionHandler2['default'](this.body, this.canvas); // Selection handler + this.interactionHandler = new _modulesInteractionHandler2['default'](this.body, this.canvas, this.selectionHandler); // Interaction handler handles all the hammer bindings (that are bound by canvas), key + this.view = new _modulesView2['default'](this.body, this.canvas); // camera handler, does animations and zooms + this.renderer = new _modulesCanvasRenderer2['default'](this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into + this.physics = new _modulesPhysicsEngine2['default'](this.body); // physics engine, does all the simulations + this.layoutEngine = new _modulesLayoutEngine2['default'](this.body); // layout engine for inital layout and hierarchical layout + this.clustering = new _modulesClustering2['default'](this.body); // clustering api + this.manipulation = new _modulesManipulationSystem2['default'](this.body, this.canvas, this.selectionHandler); // data manipulation system + + this.nodesHandler = new _modulesNodesHandler2['default'](this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options + this.edgesHandler = new _modulesEdgesHandler2['default'](this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options // create the DOM elements this.canvas._create(); // setup configuration system - this.configurationSystem = new _ConfigurationSystem2['default'](this, this.body.container, _allOptions$configureOptions.configureOptions, this.canvas.pixelRatio); + this.configurationSystem = new _sharedConfigurationSystem2['default'](this, this.body.container, _optionsJs.configureOptions, this.canvas.pixelRatio); // apply options this.setOptions(options); @@ -15784,9 +15790,9 @@ return /******/ (function(modules) { // webpackBootstrap Network.prototype.setOptions = function (options) { if (options !== undefined) { - var errorFound = _Validator2['default'].validate(options, _allOptions$configureOptions.allOptions); + var errorFound = _sharedValidator2['default'].validate(options, _optionsJs.allOptions); if (errorFound === true) { - console.log('%cErrors have been found in the supplied options object.', _Validator.printStyle); + console.log('%cErrors have been found in the supplied options object.', _sharedValidator.printStyle); } // copy the global fields over @@ -15898,7 +15904,7 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.on('_dataChanged', function () { // update shortcut lists _this2._updateVisibleIndices(); - _this2.physics.updatePhysicsIndices(); + _this2.physics.updatePhysicsData(); // call the dataUpdated event because the only difference between the two is the updating of the indices _this2.body.emitter.emit('_dataUpdated'); @@ -17149,7 +17155,7 @@ return /******/ (function(modules) { // webpackBootstrap // use this instance. Else, load via commonjs. 'use strict'; - module.exports = typeof window !== 'undefined' && window['moment'] || __webpack_require__(70); + module.exports = typeof window !== 'undefined' && window['moment'] || __webpack_require__(72); /***/ }, /* 41 */ @@ -17160,8 +17166,8 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; if (typeof window !== 'undefined') { - var propagating = __webpack_require__(71); - var Hammer = window['Hammer'] || __webpack_require__(72); + var propagating = __webpack_require__(70); + var Hammer = window['Hammer'] || __webpack_require__(71); module.exports = propagating(Hammer, { preventDefault: 'mouse' }); @@ -17391,188 +17397,18 @@ return /******/ (function(modules) { // webpackBootstrap /* 43 */ /***/ function(module, exports, __webpack_require__) { - - /** - * Expose `Emitter`. - */ - - module.exports = Emitter; - - /** - * Initialize a new `Emitter`. - * - * @api public - */ - - function Emitter(obj) { - if (obj) return mixin(obj); - }; - - /** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ - - function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; - } - - /** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.on = - Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks[event] = this._callbacks[event] || []) - .push(fn); - return this; - }; - - /** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; - - function on() { - self.off(event, on); - fn.apply(this, arguments); - } - - on.fn = fn; - this.on(event, on); - return this; - }; - - /** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.off = - Emitter.prototype.removeListener = - Emitter.prototype.removeAllListeners = - Emitter.prototype.removeEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } - - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; - - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; - } - - // remove specific handler - var cb; - for (var i = 0; i < callbacks.length; i++) { - cb = callbacks[i]; - if (cb === fn || cb.fn === fn) { - callbacks.splice(i, 1); - break; - } - } - return this; - }; - - /** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ - - Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks[event]; - - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } - - return this; - }; - - /** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ - - Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks[event] || []; - }; - - /** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ - - Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; - }; - - -/***/ }, -/* 44 */ -/***/ function(module, exports, __webpack_require__) { - 'use strict'; - var Emitter = __webpack_require__(43); + var Emitter = __webpack_require__(69); var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(49); + var hammerUtil = __webpack_require__(48); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); var DataView = __webpack_require__(4); var Range = __webpack_require__(17); var ItemSet = __webpack_require__(32); var TimeAxis = __webpack_require__(35); - var Activator = __webpack_require__(68); + var Activator = __webpack_require__(67); var DateUtil = __webpack_require__(15); var CustomTime = __webpack_require__(27); @@ -18525,7 +18361,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Core; /***/ }, -/* 45 */ +/* 44 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -18540,9 +18376,9 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _ColorPicker = __webpack_require__(73); + var _networkModulesComponentsColorPicker = __webpack_require__(73); - var _ColorPicker2 = _interopRequireDefault(_ColorPicker); + var _networkModulesComponentsColorPicker2 = _interopRequireDefault(_networkModulesComponentsColorPicker); var util = __webpack_require__(1); @@ -18583,7 +18419,7 @@ return /******/ (function(modules) { // webpackBootstrap this.configureOptions = configureOptions; this.moduleOptions = {}; this.domElements = []; - this.colorPicker = new _ColorPicker2['default'](pixelRatio); + this.colorPicker = new _networkModulesComponentsColorPicker2['default'](pixelRatio); this.wrapper; } @@ -19182,7 +19018,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 46 */ +/* 45 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -19445,9 +19281,9 @@ return /******/ (function(modules) { // webpackBootstrap // http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript /* Copyright (c) 2011 Andrei Mackenzie - 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. + 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. */ value: function levenshteinDistance(a, b) { if (a.length === 0) return b.length; @@ -19495,7 +19331,7 @@ return /******/ (function(modules) { // webpackBootstrap // item is a function, which is allowed /***/ }, -/* 47 */ +/* 46 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -19712,7 +19548,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.configureOptions = configureOptions; /***/ }, -/* 48 */ +/* 47 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -19983,7 +19819,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.configureOptions = configureOptions; /***/ }, -/* 49 */ +/* 48 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -20055,7 +19891,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.offRelease = exports.offTouch; /***/ }, -/* 50 */ +/* 49 */ /***/ function(module, exports, __webpack_require__) { // English @@ -20077,13 +19913,13 @@ return /******/ (function(modules) { // webpackBootstrap exports['nl_BE'] = exports['nl']; /***/ }, -/* 51 */ +/* 50 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var DOMutil = __webpack_require__(2); - var Points = __webpack_require__(53); + var Points = __webpack_require__(52); function Line(groupId, options) { this.groupId = groupId; @@ -20372,13 +20208,13 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Line; /***/ }, -/* 52 */ +/* 51 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var DOMutil = __webpack_require__(2); - var Points = __webpack_require__(53); + var Points = __webpack_require__(52); function Bargraph(groupId, options) { this.groupId = groupId; @@ -20620,7 +20456,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Bargraph; /***/ }, -/* 53 */ +/* 52 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -20667,7 +20503,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Points; /***/ }, -/* 54 */ +/* 53 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -20809,7 +20645,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 55 */ +/* 54 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -20824,13 +20660,13 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _Node = __webpack_require__(74); + var _componentsNode = __webpack_require__(87); - var _Node2 = _interopRequireDefault(_Node); + var _componentsNode2 = _interopRequireDefault(_componentsNode); - var _Label = __webpack_require__(75); + var _componentsSharedLabel = __webpack_require__(75); - var _Label2 = _interopRequireDefault(_Label); + var _componentsSharedLabel2 = _interopRequireDefault(_componentsSharedLabel); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); @@ -20962,7 +20798,7 @@ return /******/ (function(modules) { // webpackBootstrap key: 'setOptions', value: function setOptions(options) { if (options !== undefined) { - _Node2['default'].parseOptions(this.options, options); + _componentsNode2['default'].parseOptions(this.options, options); // update the shape in all nodes if (options.shape !== undefined) { @@ -20975,7 +20811,7 @@ return /******/ (function(modules) { // webpackBootstrap // update the shape size in all nodes if (options.font !== undefined) { - _Label2['default'].parseOptions(this.options.font, options); + _componentsSharedLabel2['default'].parseOptions(this.options.font, options); for (var nodeId in this.body.nodes) { if (this.body.nodes.hasOwnProperty(nodeId)) { this.body.nodes[nodeId].updateLabelModule(); @@ -21139,7 +20975,7 @@ return /******/ (function(modules) { // webpackBootstrap * @param constructorClass */ value: function create(properties) { - var constructorClass = arguments[1] === undefined ? _Node2['default'] : arguments[1]; + var constructorClass = arguments[1] === undefined ? _componentsNode2['default'] : arguments[1]; return new constructorClass(properties, this.body, this.images, this.groups, this.options); } @@ -21282,7 +21118,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 56 */ +/* 55 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -21297,13 +21133,13 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _Edge = __webpack_require__(76); + var _componentsEdge = __webpack_require__(74); - var _Edge2 = _interopRequireDefault(_Edge); + var _componentsEdge2 = _interopRequireDefault(_componentsEdge); - var _Label = __webpack_require__(75); + var _componentsSharedLabel = __webpack_require__(75); - var _Label2 = _interopRequireDefault(_Label); + var _componentsSharedLabel2 = _interopRequireDefault(_componentsSharedLabel); var util = __webpack_require__(1); var DataSet = __webpack_require__(3); @@ -21462,7 +21298,7 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { if (options !== undefined) { // use the parser from the Edge class to fill in all shorthand notations - _Edge2['default'].parseOptions(this.options, options); + _componentsEdge2['default'].parseOptions(this.options, options); // hanlde multiple input cases for color if (options.color !== undefined) { @@ -21482,7 +21318,7 @@ return /******/ (function(modules) { // webpackBootstrap // update fonts in all edges if (options.font !== undefined) { // use the parser from the Label class to fill in all shorthand notations - _Label2['default'].parseOptions(this.options, options); + _componentsSharedLabel2['default'].parseOptions(this.options, options); for (var edgeId in this.body.edges) { if (this.body.edges.hasOwnProperty(edgeId)) { this.body.edges[edgeId].updateLabelModule(); @@ -21656,7 +21492,7 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: 'create', value: function create(properties) { - return new _Edge2['default'](properties, this.body, this.options); + return new _componentsEdge2['default'](properties, this.body, this.options); } }, { key: 'markAllEdgesAsDirty', @@ -21701,7 +21537,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 57 */ +/* 56 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -21716,37 +21552,37 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _BarnesHutSolver = __webpack_require__(77); + var _componentsPhysicsBarnesHutSolver = __webpack_require__(77); - var _BarnesHutSolver2 = _interopRequireDefault(_BarnesHutSolver); + var _componentsPhysicsBarnesHutSolver2 = _interopRequireDefault(_componentsPhysicsBarnesHutSolver); - var _Repulsion = __webpack_require__(78); + var _componentsPhysicsRepulsionSolver = __webpack_require__(78); - var _Repulsion2 = _interopRequireDefault(_Repulsion); + var _componentsPhysicsRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsRepulsionSolver); - var _HierarchicalRepulsion = __webpack_require__(79); + var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(79); - var _HierarchicalRepulsion2 = _interopRequireDefault(_HierarchicalRepulsion); + var _componentsPhysicsHierarchicalRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalRepulsionSolver); - var _SpringSolver = __webpack_require__(80); + var _componentsPhysicsSpringSolver = __webpack_require__(80); - var _SpringSolver2 = _interopRequireDefault(_SpringSolver); + var _componentsPhysicsSpringSolver2 = _interopRequireDefault(_componentsPhysicsSpringSolver); - var _HierarchicalSpringSolver = __webpack_require__(81); + var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(81); - var _HierarchicalSpringSolver2 = _interopRequireDefault(_HierarchicalSpringSolver); + var _componentsPhysicsHierarchicalSpringSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalSpringSolver); - var _CentralGravitySolver = __webpack_require__(82); + var _componentsPhysicsCentralGravitySolver = __webpack_require__(82); - var _CentralGravitySolver2 = _interopRequireDefault(_CentralGravitySolver); + var _componentsPhysicsCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsCentralGravitySolver); - var _ForceAtlas2BasedRepulsionSolver = __webpack_require__(83); + var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(83); - var _ForceAtlas2BasedRepulsionSolver2 = _interopRequireDefault(_ForceAtlas2BasedRepulsionSolver); + var _componentsPhysicsFA2BasedRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedRepulsionSolver); - var _ForceAtlas2BasedCentralGravitySolver = __webpack_require__(84); + var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(84); - var _ForceAtlas2BasedCentralGravitySolver2 = _interopRequireDefault(_ForceAtlas2BasedCentralGravitySolver); + var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver); var util = __webpack_require__(1); @@ -21777,7 +21613,8 @@ return /******/ (function(modules) { // webpackBootstrap centralGravity: 0.3, springLength: 95, springConstant: 0.04, - damping: 0.09 + damping: 0.09, + avoidOverlap: 0 }, forceAtlas2Based: { theta: 0.5, @@ -21785,14 +21622,16 @@ return /******/ (function(modules) { // webpackBootstrap centralGravity: 0.01, springConstant: 0.08, springLength: 100, - damping: 0.4 + damping: 0.4, + avoidOverlap: 0 }, repulsion: { centralGravity: 0.2, springLength: 200, springConstant: 0.05, nodeDistance: 100, - damping: 0.09 + damping: 0.09, + avoidOverlap: 0 }, hierarchicalRepulsion: { centralGravity: 0, @@ -21872,25 +21711,25 @@ return /******/ (function(modules) { // webpackBootstrap var options; if (this.options.solver === 'forceAtlas2Based') { options = this.options.forceAtlas2Based; - this.nodesSolver = new _ForceAtlas2BasedRepulsionSolver2['default'](this.body, this.physicsBody, options); - this.edgesSolver = new _SpringSolver2['default'](this.body, this.physicsBody, options); - this.gravitySolver = new _ForceAtlas2BasedCentralGravitySolver2['default'](this.body, this.physicsBody, options); + this.nodesSolver = new _componentsPhysicsFA2BasedRepulsionSolver2['default'](this.body, this.physicsBody, options); + this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); + this.gravitySolver = new _componentsPhysicsFA2BasedCentralGravitySolver2['default'](this.body, this.physicsBody, options); } else if (this.options.solver === 'repulsion') { options = this.options.repulsion; - this.nodesSolver = new _Repulsion2['default'](this.body, this.physicsBody, options); - this.edgesSolver = new _SpringSolver2['default'](this.body, this.physicsBody, options); - this.gravitySolver = new _CentralGravitySolver2['default'](this.body, this.physicsBody, options); + this.nodesSolver = new _componentsPhysicsRepulsionSolver2['default'](this.body, this.physicsBody, options); + this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); + this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); } else if (this.options.solver === 'hierarchicalRepulsion') { options = this.options.hierarchicalRepulsion; - this.nodesSolver = new _HierarchicalRepulsion2['default'](this.body, this.physicsBody, options); - this.edgesSolver = new _HierarchicalSpringSolver2['default'](this.body, this.physicsBody, options); - this.gravitySolver = new _CentralGravitySolver2['default'](this.body, this.physicsBody, options); + this.nodesSolver = new _componentsPhysicsHierarchicalRepulsionSolver2['default'](this.body, this.physicsBody, options); + this.edgesSolver = new _componentsPhysicsHierarchicalSpringSolver2['default'](this.body, this.physicsBody, options); + this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); } else { // barnesHut options = this.options.barnesHut; - this.nodesSolver = new _BarnesHutSolver2['default'](this.body, this.physicsBody, options); - this.edgesSolver = new _SpringSolver2['default'](this.body, this.physicsBody, options); - this.gravitySolver = new _CentralGravitySolver2['default'](this.body, this.physicsBody, options); + this.nodesSolver = new _componentsPhysicsBarnesHutSolver2['default'](this.body, this.physicsBody, options); + this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options); + this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options); } this.modelOptions = options; @@ -21921,6 +21760,10 @@ return /******/ (function(modules) { // webpackBootstrap value: function startSimulation() { if (this.physicsEnabled === true) { this.stabilized = false; + + // this sets the width of all nodes initially which could be required for the avoidOverlap + this.body.emitter.emit('_resizeNodes'); + if (this.viewFunction === undefined) { this.viewFunction = this.simulationStep.bind(this); this.body.emitter.on('initRedraw', this.viewFunction); @@ -22026,14 +21869,14 @@ return /******/ (function(modules) { // webpackBootstrap } } }, { - key: 'updatePhysicsIndices', + key: 'updatePhysicsData', /** * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. * * @private */ - value: function updatePhysicsIndices() { + value: function updatePhysicsData() { this.physicsBody.forces = {}; this.physicsBody.physicsNodeIndices = []; this.physicsBody.physicsEdgeIndices = []; @@ -22244,6 +22087,9 @@ return /******/ (function(modules) { // webpackBootstrap iterations = this.options.stabilization.iterations; } + // this sets the width of all nodes initially which could be required for the avoidOverlap + this.body.emitter.emit('_resizeNodes'); + // stop the render loop this.stopSimulation(); @@ -22307,7 +22153,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 58 */ +/* 57 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -22322,9 +22168,9 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var _Cluster = __webpack_require__(85); + var _componentsNodesCluster = __webpack_require__(76); - var _Cluster2 = _interopRequireDefault(_Cluster); + var _componentsNodesCluster2 = _interopRequireDefault(_componentsNodesCluster); var util = __webpack_require__(1); @@ -22686,7 +22532,7 @@ return /******/ (function(modules) { // webpackBootstrap clusterNodeProperties.id = clusterId; // create the clusterNode - var clusterNode = this.body.functions.createNode(clusterNodeProperties, _Cluster2["default"]); + var clusterNode = this.body.functions.createNode(clusterNodeProperties, _componentsNodesCluster2["default"]); clusterNode.isCluster = true; clusterNode.containedNodes = childNodesObj; clusterNode.containedEdges = childEdgesObj; @@ -22959,7 +22805,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 59 */ +/* 58 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -23016,6 +22862,9 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.on('dragEnd', function () { return _this.dragging = false; }); + this.body.emitter.on('_resizeNodes', function () { + return _this._resizeNodes(); + }); this.body.emitter.on('_redraw', function () { if (_this.renderingActive === false) { _this._redraw(); @@ -23177,6 +23026,41 @@ return /******/ (function(modules) { // webpackBootstrap ctx.clearRect(0, 0, w, h); } } + }, { + key: '_resizeNodes', + + /** + * Redraw all nodes + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @param {Boolean} [alwaysShow] + * @private + */ + value: function _resizeNodes() { + var ctx = this.canvas.frame.canvas.getContext('2d'); + if (this.pixelRatio === undefined) { + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); + } + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + ctx.save(); + ctx.translate(this.body.view.translation.x, this.body.view.translation.y); + ctx.scale(this.body.view.scale, this.body.view.scale); + + var nodes = this.body.nodes; + var node = undefined; + + // resize all nodes + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + node = nodes[nodeId]; + node.resize(ctx); + node.updateBoundingBox(ctx); + } + } + + // restore original scaling and translation + ctx.restore(); + } }, { key: '_drawNodes', @@ -23299,7 +23183,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 60 */ +/* 59 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -23313,7 +23197,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(49); + var hammerUtil = __webpack_require__(48); var util = __webpack_require__(1); @@ -23667,7 +23551,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 61 */ +/* 60 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -23826,7 +23710,7 @@ return /******/ (function(modules) { // webpackBootstrap var factor = Math.min(this.canvas.frame.canvas.clientWidth / 600, this.canvas.frame.canvas.clientHeight / 600); zoomLevel *= factor; } else { - this.body.emitter.emit("_redraw", true); + this.body.emitter.emit("_resizeNodes"); range = this._getRange(options.nodes); var xDistance = Math.abs(range.maxX - range.minX) * 1.1; var yDistance = Math.abs(range.maxY - range.minY) * 1.1; @@ -24069,7 +23953,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 62 */ +/* 61 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -24084,13 +23968,13 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _NavigationHandler = __webpack_require__(86); + var _componentsNavigationHandler = __webpack_require__(85); - var _NavigationHandler2 = _interopRequireDefault(_NavigationHandler); + var _componentsNavigationHandler2 = _interopRequireDefault(_componentsNavigationHandler); - var _Popup = __webpack_require__(87); + var _componentsPopup = __webpack_require__(86); - var _Popup2 = _interopRequireDefault(_Popup); + var _componentsPopup2 = _interopRequireDefault(_componentsPopup); var util = __webpack_require__(1); @@ -24101,7 +23985,7 @@ return /******/ (function(modules) { // webpackBootstrap this.body = body; this.canvas = canvas; this.selectionHandler = selectionHandler; - this.navigationHandler = new _NavigationHandler2['default'](body, canvas); + this.navigationHandler = new _componentsNavigationHandler2['default'](body, canvas); // bind the events from hammer to functions in this object this.body.eventListeners.onTap = this.onTap.bind(this); @@ -24707,7 +24591,7 @@ return /******/ (function(modules) { // webpackBootstrap // show popup message window if (this.popupObj.id !== previousPopupObjId) { if (this.popup === undefined) { - this.popup = new _Popup2['default'](this.canvas.frame); + this.popup = new _componentsPopup2['default'](this.canvas.frame); } this.popup.popupTargetType = popupType; @@ -24775,7 +24659,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 63 */ +/* 62 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -24788,8 +24672,8 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Node = __webpack_require__(74); - var Edge = __webpack_require__(76); + var Node = __webpack_require__(87); + var Edge = __webpack_require__(74); var util = __webpack_require__(1); var SelectionHandler = (function () { @@ -25495,7 +25379,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports["default"]; /***/ }, -/* 64 */ +/* 63 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -25647,7 +25531,6 @@ return /******/ (function(modules) { // webpackBootstrap if (!node.isFixed() && (node.x === undefined || node.y === undefined)) { var radius = 10 * 0.1 * nodesArray.length + 10; var angle = 2 * Math.PI * this.seededRandom(); - if (node.options.fixed.x === false) { node.x = radius * Math.cos(angle); } @@ -25992,7 +25875,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 65 */ +/* 64 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -26007,7 +25890,7 @@ return /******/ (function(modules) { // webpackBootstrap var util = __webpack_require__(1); var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(49); + var hammerUtil = __webpack_require__(48); /** * clears the toolbar div element of children @@ -27181,7 +27064,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 66 */ +/* 65 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -27410,6 +27293,7 @@ return /******/ (function(modules) { // webpackBootstrap springLength: { number: number }, springConstant: { number: number }, damping: { number: number }, + avoidOverlap: { number: number }, __type__: { object: object } }, forceAtlas2Based: { @@ -27418,6 +27302,7 @@ return /******/ (function(modules) { // webpackBootstrap springLength: { number: number }, springConstant: { number: number }, damping: { number: number }, + avoidOverlap: { number: number }, __type__: { object: object } }, repulsion: { @@ -27426,6 +27311,7 @@ return /******/ (function(modules) { // webpackBootstrap springConstant: { number: number }, nodeDistance: { number: number }, damping: { number: number }, + avoidOverlap: { number: number }, __type__: { object: object } }, hierarchicalRepulsion: { @@ -27615,7 +27501,8 @@ return /******/ (function(modules) { // webpackBootstrap centralGravity: [0.3, 0, 10, 0.05], springLength: [95, 0, 500, 5], springConstant: [0.04, 0, 5, 0.005], - damping: [0.09, 0, 1, 0.01] + damping: [0.09, 0, 1, 0.01], + avoidOverlap: [0, 0, 1, 0.01] }, forceAtlas2Based: { //theta: [0.5, 0.1, 1, 0.05], @@ -27623,14 +27510,16 @@ return /******/ (function(modules) { // webpackBootstrap centralGravity: [0.01, 0, 1, 0.005], springLength: [95, 0, 500, 5], springConstant: [0.08, 0, 5, 0.005], - damping: [0.4, 0, 1, 0.01] + damping: [0.4, 0, 1, 0.01], + avoidOverlap: [0, 0, 1, 0.01] }, repulsion: { centralGravity: [0.2, 0, 10, 0.05], springLength: [200, 0, 500, 5], springConstant: [0.05, 0, 5, 0.005], nodeDistance: [100, 0, 500, 5], - damping: [0.09, 0, 1, 0.01] + damping: [0.09, 0, 1, 0.01], + avoidOverlap: [0, 0, 1, 0.01] }, hierarchicalRepulsion: { centralGravity: [0.2, 0, 10, 0.05], @@ -27653,7 +27542,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.configureOptions = configureOptions; /***/ }, -/* 67 */ +/* 66 */ /***/ function(module, exports, __webpack_require__) { /** @@ -27937,13 +27826,13 @@ return /******/ (function(modules) { // webpackBootstrap } /***/ }, -/* 68 */ +/* 67 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var keycharm = __webpack_require__(88); - var Emitter = __webpack_require__(43); + var Emitter = __webpack_require__(69); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); @@ -28090,7 +27979,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Activator; /***/ }, -/* 69 */ +/* 68 */ /***/ function(module, exports, __webpack_require__) { // English @@ -28134,7192 +28023,6543 @@ return /******/ (function(modules) { // webpackBootstrap exports['nl_BE'] = exports['nl']; /***/ }, -/* 70 */ +/* 69 */ /***/ function(module, exports, __webpack_require__) { - /* WEBPACK VAR INJECTION */(function(module) {//! moment.js - //! version : 2.10.2 - //! authors : Tim Wood, Iskren Chernev, Moment.js contributors - //! license : MIT - //! momentjs.com - - (function (global, factory) { - true ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - global.moment = factory() - }(this, function () { 'use strict'; + + /** + * Expose `Emitter`. + */ - var hookCallback; + module.exports = Emitter; - function utils_hooks__hooks () { - return hookCallback.apply(null, arguments); - } + /** + * Initialize a new `Emitter`. + * + * @api public + */ - // This is done to register the method called with moment() - // without creating circular dependencies. - function setHookCallback (callback) { - hookCallback = callback; - } + function Emitter(obj) { + if (obj) return mixin(obj); + }; - function defaultParsingFlags() { - // We need to deep clone this object. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso : false - }; - } + /** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ - function isArray(input) { - return Object.prototype.toString.call(input) === '[object Array]'; - } + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; + } - function isDate(input) { - return Object.prototype.toString.call(input) === '[object Date]' || input instanceof Date; - } + /** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); - } - return res; - } + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; + }; - function hasOwnProp(a, b) { - return Object.prototype.hasOwnProperty.call(a, b); - } + /** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } + function on() { + self.off(event, on); + fn.apply(this, arguments); + } - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } + on.fn = fn; + this.on(event, on); + return this; + }; - return a; - } + /** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ - function create_utc__createUTC (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, true).utc(); - } + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; - function valid__isValid(m) { - if (m._isValid == null) { - m._isValid = !isNaN(m._d.getTime()) && - m._pf.overflow < 0 && - !m._pf.empty && - !m._pf.invalidMonth && - !m._pf.nullInput && - !m._pf.invalidFormat && - !m._pf.userInvalidated; + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } - if (m._strict) { - m._isValid = m._isValid && - m._pf.charsLeftOver === 0 && - m._pf.unusedTokens.length === 0 && - m._pf.bigHour === undefined; - } - } - return m._isValid; - } + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; - function valid__createInvalid (flags) { - var m = create_utc__createUTC(NaN); - if (flags != null) { - extend(m._pf, flags); - } - else { - m._pf.userInvalidated = true; - } + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; + } - return m; + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; } + } + return this; + }; - var momentProperties = utils_hooks__hooks.momentProperties = []; - - function copyConfig(to, from) { - var i, prop, val; - - if (typeof from._isAMomentObject !== 'undefined') { - to._isAMomentObject = from._isAMomentObject; - } - if (typeof from._i !== 'undefined') { - to._i = from._i; - } - if (typeof from._f !== 'undefined') { - to._f = from._f; - } - if (typeof from._l !== 'undefined') { - to._l = from._l; - } - if (typeof from._strict !== 'undefined') { - to._strict = from._strict; - } - if (typeof from._tzm !== 'undefined') { - to._tzm = from._tzm; - } - if (typeof from._isUTC !== 'undefined') { - to._isUTC = from._isUTC; - } - if (typeof from._offset !== 'undefined') { - to._offset = from._offset; - } - if (typeof from._pf !== 'undefined') { - to._pf = from._pf; - } - if (typeof from._locale !== 'undefined') { - to._locale = from._locale; - } + /** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ - if (momentProperties.length > 0) { - for (i in momentProperties) { - prop = momentProperties[i]; - val = from[prop]; - if (typeof val !== 'undefined') { - to[prop] = val; - } - } - } + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks[event]; - return to; + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); } + } - var updateInProgress = false; + return this; + }; - // Moment prototype object - function Moment(config) { - copyConfig(this, config); - this._d = new Date(+config._d); - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - utils_hooks__hooks.updateOffset(this); - updateInProgress = false; - } - } + /** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ - function isMoment (obj) { - return obj instanceof Moment || (obj != null && hasOwnProp(obj, '_isAMomentObject')); - } + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; + }; - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; + /** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - if (coercedNumber >= 0) { - value = Math.floor(coercedNumber); - } else { - value = Math.ceil(coercedNumber); - } - } + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; - return value; - } - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { - function Locale() { - } + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;'use strict'; - var locales = {}; - var globalLocale; + (function (factory) { + if (true) { + // AMD. Register as an anonymous module. + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + window.propagating = factory(); + } + }(function () { + var _firstTarget = null; // singleton, will contain the target element where the touch event started + var _processing = false; // singleton, true when a touch event is being handled - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; + /** + * Extend an Hammer.js instance with event propagation. + * + * Features: + * - Events emitted by hammer will propagate in order from child to parent + * elements. + * - Events are extended with a function `event.stopPropagation()` to stop + * propagation to parent elements. + * - An option `preventDefault` to stop all default browser behavior. + * + * Usage: + * var hammer = propagatingHammer(new Hammer(element)); + * var hammer = propagatingHammer(new Hammer(element), {preventDefault: true}); + * + * @param {Hammer.Manager} hammer An hammer instance. + * @param {Object} [options] Available options: + * - `preventDefault: true | 'mouse' | 'touch' | 'pen'`. + * Enforce preventing the default browser behavior. + * Cannot be set to `false`. + * @return {Hammer.Manager} Returns the same hammer instance with extended + * functionality + */ + return function propagating(hammer, options) { + if (options && options.preventDefault === false) { + throw new Error('Only supports preventDefault == true'); } + var _options = options || { + preventDefault: false + }; - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; + if (hammer.Manager) { + // This looks like the Hammer constructor. + // Overload the constructors with our own. + var Hammer = hammer; - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return null; - } + var PropagatingHammer = function(element, options) { + return propagating(new Hammer(element, options), _options); + }; + Hammer.extend(PropagatingHammer, Hammer); + PropagatingHammer.Manager = function (element, options) { + return propagating(new Hammer.Manager(element, options), _options); + }; - function loadLocale(name) { - var oldLocale = null; - // TODO: Find a better way to register and load all the locales in Node - if (!locales[name] && typeof module !== 'undefined' && - module && module.exports) { - try { - oldLocale = globalLocale._abbr; - !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); - // because defineLocale currently also sets the global locale, we - // want to undo that for lazy loaded locales - locale_locales__getSetGlobalLocale(oldLocale); - } catch (e) { } - } - return locales[name]; + return PropagatingHammer; } - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - function locale_locales__getSetGlobalLocale (key, values) { - var data; - if (key) { - if (typeof values === 'undefined') { - data = locale_locales__getLocale(key); - } - else { - data = defineLocale(key, values); - } + // attach to DOM element + var element = hammer.element; + element.hammer = hammer; - if (data) { - // moment.duration._locale = moment._locale = data; - globalLocale = data; - } - } + // move the original functions that we will wrap + hammer._on = hammer.on; + hammer._off = hammer.off; + hammer._emit = hammer.emit; + hammer._destroy = hammer.destroy; - return globalLocale._abbr; - } + /** @type {Object.>} */ + hammer._handlers = {}; - function defineLocale (name, values) { - if (values !== null) { - values.abbr = name; - if (!locales[name]) { - locales[name] = new Locale(); - } - locales[name].set(values); + // register an event to catch the start of a gesture and store the + // target in a singleton + hammer._on('hammer.input', function (event) { + if (_options.preventDefault === true || (_options.preventDefault === event.pointerType)) { + event.preventDefault(); + } + if (event.isFirst) { + _firstTarget = event.target; + _processing = true; + } + if (event.isFinal) { + _processing = false; + } + }); - // backwards compat for now: also set the locale - locale_locales__getSetGlobalLocale(name); + /** + * Register a handler for one or multiple events + * @param {String} events A space separated string with events + * @param {function} handler A callback function, called as handler(event) + * @returns {Hammer.Manager} Returns the hammer instance + */ + hammer.on = function (events, handler) { + // register the handler + split(events).forEach(function (event) { + var _handlers = hammer._handlers[event]; + if (!_handlers) { + hammer._handlers[event] = _handlers = []; - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; + // register the static, propagated handler + hammer._on(event, propagatedHandler); } - } + _handlers.push(handler); + }); - // returns locale data - function locale_locales__getLocale (key) { - var locale; + return hammer; + }; - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } + /** + * Unregister a handler for one or multiple events + * @param {String} events A space separated string with events + * @param {function} [handler] Optional. The registered handler. If not + * provided, all handlers for given events + * are removed. + * @returns {Hammer.Manager} Returns the hammer instance + */ + hammer.off = function (events, handler) { + // unregister the handler + split(events).forEach(function (event) { + var _handlers = hammer._handlers[event]; + if (_handlers) { + _handlers = handler ? _handlers.filter(function (h) { + return h !== handler; + }) : []; - if (!key) { - return globalLocale; + if (_handlers.length > 0) { + hammer._handlers[event] = _handlers; + } + else { + // remove static, propagated handler + hammer._off(event, propagatedHandler); + delete hammer._handlers[event]; + } } + }); - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; - } + return hammer; + }; - return chooseLocale(key); - } + /** + * Emit to the event listeners + * @param {string} eventType + * @param {Event} event + */ + hammer.emit = function(eventType, event) { + if (!_processing) { + _firstTarget = event.target; + } + hammer._emit(eventType, event); + }; - var aliases = {}; + hammer.destroy = function () { + // Detach from DOM element + var element = hammer.element; + delete element.hammer; - function addUnitAlias (unit, shorthand) { - var lowerCase = unit.toLowerCase(); - aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; - } + // clear all handlers + hammer._handlers = {}; - function normalizeUnits(units) { - return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; + // call original hammer destroy + hammer._destroy(); + }; + + // split a string with space separated words + function split(events) { + return events.match(/[^ ]+/g); } - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; + /** + * A static event handler, applying event propagation. + * @param {Object} event + */ + function propagatedHandler(event) { + // let only a single hammer instance handle this event + if (event.type !== 'hammer.input') { + // it is possible that the same srcEvent is used with multiple hammer events, + // we keep track on which events are handled in an object _handled + if (!event.srcEvent._handled) { + event.srcEvent._handled = {}; + } - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } + if (event.srcEvent._handled[event.type]) { + return; } + else { + event.srcEvent._handled[event.type] = true; + } + } - return normalizedInput; - } + // attach a stopPropagation function to the event + var stopped = false; + event.stopPropagation = function () { + stopped = true; + }; - function makeGetSet (unit, keepTime) { - return function (value) { - if (value != null) { - get_set__set(this, unit, value); - utils_hooks__hooks.updateOffset(this, keepTime); - return this; - } else { - return get_set__get(this, unit); - } - }; - } + // attach firstTarget property to the event + event.firstTarget = _firstTarget; - function get_set__get (mom, unit) { - return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); - } + // propagate over all elements (until stopped) + var elem = _firstTarget; + while (elem && !stopped) { + var _handlers = elem.hammer && elem.hammer._handlers[event.type]; + if (_handlers) { + for (var i = 0; i < _handlers.length && !stopped; i++) { + _handlers[i](event); + } + } - function get_set__set (mom, unit, value) { - return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); + elem = elem.parentNode; + } } - // MOMENTS - - function getSet (units, value) { - var unit; - if (typeof units === 'object') { - for (unit in units) { - this.set(unit, units[unit]); - } - } else { - units = normalizeUnits(units); - if (typeof this[units] === 'function') { - return this[units](value); - } - } - return this; - } + return hammer; + }; + })); - function zeroFill(number, targetLength, forceSign) { - var output = '' + Math.abs(number), - sign = number >= 0; - while (output.length < targetLength) { - output = '0' + output; - } - return (sign ? (forceSign ? '+' : '') : '-') + output; - } +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { - var formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g; + var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.4 - 2014-09-28 + * http://hammerjs.github.io/ + * + * Copyright (c) 2014 Jorik Tangelder; + * Licensed under the MIT license */ + (function(window, document, exportName, undefined) { + 'use strict'; - var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; + var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o']; + var TEST_ELEMENT = document.createElement('div'); - var formatFunctions = {}; + var TYPE_FUNCTION = 'function'; - var formatTokenFunctions = {}; + var round = Math.round; + var abs = Math.abs; + var now = Date.now; - // token: 'M' - // padded: ['MM', 2] - // ordinal: 'Mo' - // callback: function () { this.month() + 1 } - function addFormatToken (token, padded, ordinal, callback) { - var func = callback; - if (typeof callback === 'string') { - func = function () { - return this[callback](); - }; - } - if (token) { - formatTokenFunctions[token] = func; - } - if (padded) { - formatTokenFunctions[padded[0]] = function () { - return zeroFill(func.apply(this, arguments), padded[1], padded[2]); - }; - } - if (ordinal) { - formatTokenFunctions[ordinal] = function () { - return this.localeData().ordinal(func.apply(this, arguments), token); - }; - } - } + /** + * set a timeout with a given scope + * @param {Function} fn + * @param {Number} timeout + * @param {Object} context + * @returns {number} + */ + function setTimeoutContext(fn, timeout, context) { + return setTimeout(bindFn(fn, context), timeout); + } - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); + /** + * if the argument is an array, we want to execute the fn on each entry + * if it aint an array we don't want to do a thing. + * this is used by all the methods that accept a single and array argument. + * @param {*|Array} arg + * @param {String} fn + * @param {Object} [context] + * @returns {Boolean} + */ + function invokeArrayArg(arg, fn, context) { + if (Array.isArray(arg)) { + each(arg, context[fn], context); + return true; } + return false; + } - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; - - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } + /** + * walk objects and arrays + * @param {Object} obj + * @param {Function} iterator + * @param {Object} context + */ + function each(obj, iterator, context) { + var i; - return function (mom) { - var output = ''; - for (i = 0; i < length; i++) { - output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; - } - return output; - }; + if (!obj) { + return; } - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); + if (obj.forEach) { + obj.forEach(iterator, context); + } else if (obj.length !== undefined) { + i = 0; + while (i < obj.length) { + iterator.call(context, obj[i], i, obj); + i++; } - - format = expandFormat(format, m.localeData()); - - if (!formatFunctions[format]) { - formatFunctions[format] = makeFormatFunction(format); + } else { + for (i in obj) { + obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); } - - return formatFunctions[format](m); } + } - function expandFormat(format, locale) { - var i = 5; - - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } - - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; + /** + * extend object. + * means that properties in dest will be overwritten by the ones in src. + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge] + * @returns {Object} dest + */ + function extend(dest, src, merge) { + var keys = Object.keys(src); + var i = 0; + while (i < keys.length) { + if (!merge || (merge && dest[keys[i]] === undefined)) { + dest[keys[i]] = src[keys[i]]; } - - return format; + i++; } + return dest; + } - var match1 = /\d/; // 0 - 9 - var match2 = /\d\d/; // 00 - 99 - var match3 = /\d{3}/; // 000 - 999 - var match4 = /\d{4}/; // 0000 - 9999 - var match6 = /[+-]?\d{6}/; // -999999 - 999999 - var match1to2 = /\d\d?/; // 0 - 99 - var match1to3 = /\d{1,3}/; // 0 - 999 - var match1to4 = /\d{1,4}/; // 0 - 9999 - var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 - - var matchUnsigned = /\d+/; // 0 - inf - var matchSigned = /[+-]?\d+/; // -inf - inf - - var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z - - var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 + /** + * merge the values from src in the dest. + * means that properties that exist in dest will not be overwritten by src + * @param {Object} dest + * @param {Object} src + * @returns {Object} dest + */ + function merge(dest, src) { + return extend(dest, src, true); + } - // any word (or two) characters or numbers including two/three word month in arabic. - var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; + /** + * simple class inheritance + * @param {Function} child + * @param {Function} base + * @param {Object} [properties] + */ + function inherit(child, base, properties) { + var baseP = base.prototype, + childP; - var regexes = {}; + childP = child.prototype = Object.create(baseP); + childP.constructor = child; + childP._super = baseP; - function addRegexToken (token, regex, strictRegex) { - regexes[token] = typeof regex === 'function' ? regex : function (isStrict) { - return (isStrict && strictRegex) ? strictRegex : regex; - }; + if (properties) { + extend(childP, properties); } + } - function getParseRegexForToken (token, config) { - if (!hasOwnProp(regexes, token)) { - return new RegExp(unescapeFormat(token)); - } + /** + * simple function bind + * @param {Function} fn + * @param {Object} context + * @returns {Function} + */ + function bindFn(fn, context) { + return function boundFn() { + return fn.apply(context, arguments); + }; + } - return regexes[token](config._strict, config._locale); + /** + * let a boolean value also be a function that must return a boolean + * this first item in args will be used as the context + * @param {Boolean|Function} val + * @param {Array} [args] + * @returns {Boolean} + */ + function boolOrFn(val, args) { + if (typeof val == TYPE_FUNCTION) { + return val.apply(args ? args[0] || undefined : undefined, args); } + return val; + } - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function unescapeFormat(s) { - return s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - }).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } + /** + * use the val2 when val1 is undefined + * @param {*} val1 + * @param {*} val2 + * @returns {*} + */ + function ifUndefined(val1, val2) { + return (val1 === undefined) ? val2 : val1; + } - var tokens = {}; + /** + * addEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ + function addEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.addEventListener(type, handler, false); + }); + } - function addParseToken (token, callback) { - var i, func = callback; - if (typeof token === 'string') { - token = [token]; - } - if (typeof callback === 'number') { - func = function (input, array) { - array[callback] = toInt(input); - }; - } - for (i = 0; i < token.length; i++) { - tokens[token[i]] = func; + /** + * removeEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ + function removeEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.removeEventListener(type, handler, false); + }); + } + + /** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ + function hasParent(node, parent) { + while (node) { + if (node == parent) { + return true; } + node = node.parentNode; } + return false; + } - function addWeekParseToken (token, callback) { - addParseToken(token, function (input, array, config, token) { - config._w = config._w || {}; - callback(input, config._w, config, token); - }); - } + /** + * small indexOf wrapper + * @param {String} str + * @param {String} find + * @returns {Boolean} found + */ + function inStr(str, find) { + return str.indexOf(find) > -1; + } - function addTimeToArrayFromToken(token, input, config) { - if (input != null && hasOwnProp(tokens, token)) { - tokens[token](input, config._a, config, token); + /** + * split string on whitespace + * @param {String} str + * @returns {Array} words + */ + function splitStr(str) { + return str.trim().split(/\s+/g); + } + + /** + * find if a array contains the object using indexOf or a simple polyFill + * @param {Array} src + * @param {String} find + * @param {String} [findByKey] + * @return {Boolean|Number} false when not found, or the index + */ + function inArray(src, find, findByKey) { + if (src.indexOf && !findByKey) { + return src.indexOf(find); + } else { + var i = 0; + while (i < src.length) { + if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { + return i; + } + i++; } + return -1; } + } - var YEAR = 0; - var MONTH = 1; - var DATE = 2; - var HOUR = 3; - var MINUTE = 4; - var SECOND = 5; - var MILLISECOND = 6; - - function daysInMonth(year, month) { - return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); - } - - // FORMATTING - - addFormatToken('M', ['MM', 2], 'Mo', function () { - return this.month() + 1; - }); - - addFormatToken('MMM', 0, 0, function (format) { - return this.localeData().monthsShort(this, format); - }); - - addFormatToken('MMMM', 0, 0, function (format) { - return this.localeData().months(this, format); - }); - - // ALIASES - - addUnitAlias('month', 'M'); - - // PARSING + /** + * convert array-like objects to real arrays + * @param {Object} obj + * @returns {Array} + */ + function toArray(obj) { + return Array.prototype.slice.call(obj, 0); + } - addRegexToken('M', match1to2); - addRegexToken('MM', match1to2, match2); - addRegexToken('MMM', matchWord); - addRegexToken('MMMM', matchWord); + /** + * unique array with objects based on a key (like 'id') or just by the array's value + * @param {Array} src [{id:1},{id:2},{id:1}] + * @param {String} [key] + * @param {Boolean} [sort=False] + * @returns {Array} [{id:1},{id:2}] + */ + function uniqueArray(src, key, sort) { + var results = []; + var values = []; + var i = 0; - addParseToken(['M', 'MM'], function (input, array) { - array[MONTH] = toInt(input) - 1; - }); + while (i < src.length) { + var val = key ? src[i][key] : src[i]; + if (inArray(values, val) < 0) { + results.push(src[i]); + } + values[i] = val; + i++; + } - addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { - var month = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (month != null) { - array[MONTH] = month; + if (sort) { + if (!key) { + results = results.sort(); } else { - config._pf.invalidMonth = input; + results = results.sort(function sortUniqueArray(a, b) { + return a[key] > b[key]; + }); } - }); - - // LOCALES - - var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); - function localeMonths (m) { - return this._months[m.month()]; } - var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); - function localeMonthsShort (m) { - return this._monthsShort[m.month()]; - } + return results; + } - function localeMonthsParse (monthName, format, strict) { - var i, mom, regex; + /** + * get the prefixed property + * @param {Object} obj + * @param {String} property + * @returns {String|Undefined} prefixed + */ + function prefixed(obj, property) { + var prefix, prop; + var camelProp = property[0].toUpperCase() + property.slice(1); - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } + var i = 0; + while (i < VENDOR_PREFIXES.length) { + prefix = VENDOR_PREFIXES[i]; + prop = (prefix) ? prefix + camelProp : property; - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = create_utc__createUTC([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } + if (prop in obj) { + return prop; } + i++; } + return undefined; + } - // MOMENTS + /** + * get a unique id + * @returns {number} uniqueId + */ + var _uniqueId = 1; + function uniqueId() { + return _uniqueId++; + } - function setMonth (mom, value) { - var dayOfMonth; + /** + * get the window object of an element + * @param {HTMLElement} element + * @returns {DocumentView|Window} + */ + function getWindowForElement(element) { + var doc = element.ownerDocument; + return (doc.defaultView || doc.parentWindow); + } - // TODO: Move this out of here! - if (typeof value === 'string') { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (typeof value !== 'number') { - return mom; - } - } + var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; - dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; - } + var SUPPORT_TOUCH = ('ontouchstart' in window); + var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; + var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); - function getSetMonth (value) { - if (value != null) { - setMonth(this, value); - utils_hooks__hooks.updateOffset(this, true); - return this; - } else { - return get_set__get(this, 'Month'); - } - } + var INPUT_TYPE_TOUCH = 'touch'; + var INPUT_TYPE_PEN = 'pen'; + var INPUT_TYPE_MOUSE = 'mouse'; + var INPUT_TYPE_KINECT = 'kinect'; - function getDaysInMonth () { - return daysInMonth(this.year(), this.month()); - } + var COMPUTE_INTERVAL = 25; - function checkOverflow (m) { - var overflow; - var a = m._a; + var INPUT_START = 1; + var INPUT_MOVE = 2; + var INPUT_END = 4; + var INPUT_CANCEL = 8; - if (a && m._pf.overflow === -2) { - overflow = - a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : - a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : - a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : - a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : - a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : - a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : - -1; + var DIRECTION_NONE = 1; + var DIRECTION_LEFT = 2; + var DIRECTION_RIGHT = 4; + var DIRECTION_UP = 8; + var DIRECTION_DOWN = 16; - if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } + var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; + var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; + var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; - m._pf.overflow = overflow; - } + var PROPS_XY = ['x', 'y']; + var PROPS_CLIENT_XY = ['clientX', 'clientY']; - return m; - } + /** + * create new input type manager + * @param {Manager} manager + * @param {Function} callback + * @returns {Input} + * @constructor + */ + function Input(manager, callback) { + var self = this; + this.manager = manager; + this.callback = callback; + this.element = manager.element; + this.target = manager.options.inputTarget; - function warn(msg) { - if (utils_hooks__hooks.suppressDeprecationWarnings === false && typeof console !== 'undefined' && console.warn) { - console.warn('Deprecation warning: ' + msg); + // smaller wrapper around the handler, for the scope and the enabled state of the manager, + // so when disabled the input events are completely bypassed. + this.domHandler = function(ev) { + if (boolOrFn(manager.options.enable, [manager])) { + self.handler(ev); } - } + }; - function deprecate(msg, fn) { - var firstTime = true; - return extend(function () { - if (firstTime) { - warn(msg); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } + this.init(); - var deprecations = {}; + } - function deprecateSimple(name, msg) { - if (!deprecations[name]) { - warn(msg); - deprecations[name] = true; - } - } + Input.prototype = { + /** + * should handle the inputEvent data and trigger the callback + * @virtual + */ + handler: function() { }, - utils_hooks__hooks.suppressDeprecationWarnings = false; + /** + * bind the events + */ + init: function() { + this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + }, - var from_string__isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; + /** + * unbind the events + */ + destroy: function() { + this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + } + }; - var isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], - ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], - ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], - ['GGGG-[W]WW', /\d{4}-W\d{2}/], - ['YYYY-DDD', /\d{4}-\d{3}/] - ]; + /** + * create new input type manager + * called by the Manager constructor + * @param {Hammer} manager + * @returns {Input} + */ + function createInputInstance(manager) { + var Type; + var inputClass = manager.options.inputClass; - // iso time formats and regexes - var isoTimes = [ - ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], - ['HH:mm', /(T| )\d\d:\d\d/], - ['HH', /(T| )\d\d/] - ]; + if (inputClass) { + Type = inputClass; + } else if (SUPPORT_POINTER_EVENTS) { + Type = PointerEventInput; + } else if (SUPPORT_ONLY_TOUCH) { + Type = TouchInput; + } else if (!SUPPORT_TOUCH) { + Type = MouseInput; + } else { + Type = TouchMouseInput; + } + return new (Type)(manager, inputHandler); + } - var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; + /** + * handle input events + * @param {Manager} manager + * @param {String} eventType + * @param {Object} input + */ + function inputHandler(manager, eventType, input) { + var pointersLen = input.pointers.length; + var changedPointersLen = input.changedPointers.length; + var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); + var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); - // date from iso format - function configFromISO(config) { - var i, l, - string = config._i, - match = from_string__isoRegex.exec(string); + input.isFirst = !!isFirst; + input.isFinal = !!isFinal; - if (match) { - config._pf.iso = true; - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(string)) { - // match[5] should be 'T' or undefined - config._f = isoDates[i][0] + (match[6] || ' '); - break; - } - } - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(string)) { - config._f += isoTimes[i][0]; - break; - } - } - if (string.match(matchOffset)) { - config._f += 'Z'; - } - configFromStringAndFormat(config); - } else { - config._isValid = false; - } + if (isFirst) { + manager.session = {}; } - // date from iso format or fallback - function configFromString(config) { - var matched = aspNetJsonRegex.exec(config._i); + // source event is the normalized value of the domEvents + // like 'touchstart, mouseup, pointerdown' + input.eventType = eventType; - if (matched !== null) { - config._d = new Date(+matched[1]); - return; - } + // compute scale, rotation etc + computeInputData(manager, input); - configFromISO(config); - if (config._isValid === false) { - delete config._isValid; - utils_hooks__hooks.createFromInputFallback(config); - } - } + // emit secret event + manager.emit('hammer.input', input); - utils_hooks__hooks.createFromInputFallback = deprecate( - 'moment construction falls back to js Date. This is ' + - 'discouraged and will be removed in upcoming major ' + - 'release. Please refer to ' + - 'https://github.com/moment/moment/issues/1407 for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); + manager.recognize(input); + manager.session.prevInput = input; + } - function createDate (y, m, d, h, M, s, ms) { - //can't just apply() to create a date: - //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var date = new Date(y, m, d, h, M, s, ms); + /** + * extend the data with some usable properties like scale, rotate, velocity etc + * @param {Object} manager + * @param {Object} input + */ + function computeInputData(manager, input) { + var session = manager.session; + var pointers = input.pointers; + var pointersLength = pointers.length; - //the date constructor doesn't accept years < 1970 - if (y < 1970) { - date.setFullYear(y); - } - return date; + // store the first input to calculate the distance and direction + if (!session.firstInput) { + session.firstInput = simpleCloneInputData(input); } - function createUTCDate (y) { - var date = new Date(Date.UTC.apply(null, arguments)); - if (y < 1970) { - date.setUTCFullYear(y); - } - return date; + // to compute scale and rotation we need to store the multiple touches + if (pointersLength > 1 && !session.firstMultiple) { + session.firstMultiple = simpleCloneInputData(input); + } else if (pointersLength === 1) { + session.firstMultiple = false; } - addFormatToken(0, ['YY', 2], 0, function () { - return this.year() % 100; - }); + var firstInput = session.firstInput; + var firstMultiple = session.firstMultiple; + var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; - addFormatToken(0, ['YYYY', 4], 0, 'year'); - addFormatToken(0, ['YYYYY', 5], 0, 'year'); - addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); + var center = input.center = getCenter(pointers); + input.timeStamp = now(); + input.deltaTime = input.timeStamp - firstInput.timeStamp; - // ALIASES + input.angle = getAngle(offsetCenter, center); + input.distance = getDistance(offsetCenter, center); - addUnitAlias('year', 'y'); + computeDeltaXY(session, input); + input.offsetDirection = getDirection(input.deltaX, input.deltaY); - // PARSING + input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; + input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; - addRegexToken('Y', matchSigned); - addRegexToken('YY', match1to2, match2); - addRegexToken('YYYY', match1to4, match4); - addRegexToken('YYYYY', match1to6, match6); - addRegexToken('YYYYYY', match1to6, match6); + computeIntervalInputData(session, input); - addParseToken(['YYYY', 'YYYYY', 'YYYYYY'], YEAR); - addParseToken('YY', function (input, array) { - array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input); - }); + // find the correct target + var target = manager.element; + if (hasParent(input.srcEvent.target, target)) { + target = input.srcEvent.target; + } + input.target = target; + } - // HELPERS + function computeDeltaXY(session, input) { + var center = input.center; + var offset = session.offsetDelta || {}; + var prevDelta = session.prevDelta || {}; + var prevInput = session.prevInput || {}; - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } + if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { + prevDelta = session.prevDelta = { + x: prevInput.deltaX || 0, + y: prevInput.deltaY || 0 + }; - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + offset = session.offsetDelta = { + x: center.x, + y: center.y + }; } - // HOOKS + input.deltaX = prevDelta.x + (center.x - offset.x); + input.deltaY = prevDelta.y + (center.y - offset.y); + } - utils_hooks__hooks.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; + /** + * velocity is calculated every x ms + * @param {Object} session + * @param {Object} input + */ + function computeIntervalInputData(session, input) { + var last = session.lastInterval || input, + deltaTime = input.timeStamp - last.timeStamp, + velocity, velocityX, velocityY, direction; - // MOMENTS + if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { + var deltaX = last.deltaX - input.deltaX; + var deltaY = last.deltaY - input.deltaY; - var getSetYear = makeGetSet('FullYear', false); + var v = getVelocity(deltaTime, deltaX, deltaY); + velocityX = v.x; + velocityY = v.y; + velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; + direction = getDirection(deltaX, deltaY); - function getIsLeapYear () { - return isLeapYear(this.year()); + session.lastInterval = input; + } else { + // use latest velocity info if it doesn't overtake a minimum period + velocity = last.velocity; + velocityX = last.velocityX; + velocityY = last.velocityY; + direction = last.direction; } - addFormatToken('w', ['ww', 2], 'wo', 'week'); - addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); - - // ALIASES - - addUnitAlias('week', 'w'); - addUnitAlias('isoWeek', 'W'); - - // PARSING - - addRegexToken('w', match1to2); - addRegexToken('ww', match1to2, match2); - addRegexToken('W', match1to2); - addRegexToken('WW', match1to2, match2); - - addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { - week[token.substr(0, 1)] = toInt(input); - }); - - // HELPERS - - // firstDayOfWeek 0 = sun, 6 = sat - // the day of the week that starts the week - // (usually sunday or monday) - // firstDayOfWeekOfYear 0 = sun, 6 = sat - // the first week is the week that contains the first - // of this day of the week - // (eg. ISO weeks use thursday (4)) - function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { - var end = firstDayOfWeekOfYear - firstDayOfWeek, - daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), - adjustedMoment; + input.velocity = velocity; + input.velocityX = velocityX; + input.velocityY = velocityY; + input.direction = direction; + } + /** + * create a simple clone from the input used for storage of firstInput and firstMultiple + * @param {Object} input + * @returns {Object} clonedInputData + */ + function simpleCloneInputData(input) { + // make a simple copy of the pointers because we will get a reference if we don't + // we only need clientXY for the calculations + var pointers = []; + var i = 0; + while (i < input.pointers.length) { + pointers[i] = { + clientX: round(input.pointers[i].clientX), + clientY: round(input.pointers[i].clientY) + }; + i++; + } - if (daysToDayOfWeek > end) { - daysToDayOfWeek -= 7; - } + return { + timeStamp: now(), + pointers: pointers, + center: getCenter(pointers), + deltaX: input.deltaX, + deltaY: input.deltaY + }; + } - if (daysToDayOfWeek < end - 7) { - daysToDayOfWeek += 7; - } + /** + * get the center of all the pointers + * @param {Array} pointers + * @return {Object} center contains `x` and `y` properties + */ + function getCenter(pointers) { + var pointersLength = pointers.length; - adjustedMoment = local__createLocal(mom).add(daysToDayOfWeek, 'd'); + // no need to loop when only one touch + if (pointersLength === 1) { return { - week: Math.ceil(adjustedMoment.dayOfYear() / 7), - year: adjustedMoment.year() + x: round(pointers[0].clientX), + y: round(pointers[0].clientY) }; } - // LOCALES - - function localeWeek (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; + var x = 0, y = 0, i = 0; + while (i < pointersLength) { + x += pointers[i].clientX; + y += pointers[i].clientY; + i++; } - var defaultLocaleWeek = { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. + return { + x: round(x / pointersLength), + y: round(y / pointersLength) }; + } - function localeFirstDayOfWeek () { - return this._week.dow; - } + /** + * calculate the velocity between two points. unit is in px per ms. + * @param {Number} deltaTime + * @param {Number} x + * @param {Number} y + * @return {Object} velocity `x` and `y` + */ + function getVelocity(deltaTime, x, y) { + return { + x: x / deltaTime || 0, + y: y / deltaTime || 0 + }; + } - function localeFirstDayOfYear () { - return this._week.doy; + /** + * get the direction between two points + * @param {Number} x + * @param {Number} y + * @return {Number} direction + */ + function getDirection(x, y) { + if (x === y) { + return DIRECTION_NONE; } - // MOMENTS - - function getSetWeek (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); + if (abs(x) >= abs(y)) { + return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; } + return y > 0 ? DIRECTION_UP : DIRECTION_DOWN; + } - function getSetISOWeek (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); + /** + * calculate the absolute distance between two points + * @param {Object} p1 {x, y} + * @param {Object} p2 {x, y} + * @param {Array} [props] containing x and y keys + * @return {Number} distance + */ + function getDistance(p1, p2, props) { + if (!props) { + props = PROPS_XY; } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; - addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); + return Math.sqrt((x * x) + (y * y)); + } - // ALIASES + /** + * calculate the angle between two coordinates + * @param {Object} p1 + * @param {Object} p2 + * @param {Array} [props] containing x and y keys + * @return {Number} angle + */ + function getAngle(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + return Math.atan2(y, x) * 180 / Math.PI; + } - addUnitAlias('dayOfYear', 'DDD'); + /** + * calculate the rotation degrees between two pointersets + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} rotation + */ + function getRotation(start, end) { + return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY); + } - // PARSING + /** + * calculate the scale factor between two pointersets + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} scale + */ + function getScale(start, end) { + return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); + } - addRegexToken('DDD', match1to3); - addRegexToken('DDDD', match3); - addParseToken(['DDD', 'DDDD'], function (input, array, config) { - config._dayOfYear = toInt(input); - }); + var MOUSE_INPUT_MAP = { + mousedown: INPUT_START, + mousemove: INPUT_MOVE, + mouseup: INPUT_END + }; - // HELPERS + var MOUSE_ELEMENT_EVENTS = 'mousedown'; + var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; - //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { - var d = createUTCDate(year, 0, 1).getUTCDay(); - var daysToAdd; - var dayOfYear; + /** + * Mouse events input + * @constructor + * @extends Input + */ + function MouseInput() { + this.evEl = MOUSE_ELEMENT_EVENTS; + this.evWin = MOUSE_WINDOW_EVENTS; - d = d === 0 ? 7 : d; - weekday = weekday != null ? weekday : firstDayOfWeek; - daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); - dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; + this.allow = true; // used by Input.TouchMouse to disable mouse events + this.pressed = false; // mousedown state - return { - year : dayOfYear > 0 ? year : year - 1, - dayOfYear : dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear - }; - } + Input.apply(this, arguments); + } - // MOMENTS + inherit(MouseInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function MEhandler(ev) { + var eventType = MOUSE_INPUT_MAP[ev.type]; - function getSetDayOfYear (input) { - var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - } + // on start we want to have the left mouse button down + if (eventType & INPUT_START && ev.button === 0) { + this.pressed = true; + } - // Pick the first defined of two or three arguments. - function defaults(a, b, c) { - if (a != null) { - return a; + if (eventType & INPUT_MOVE && ev.which !== 1) { + eventType = INPUT_END; } - if (b != null) { - return b; + + // mouse must be down, and mouse events are allowed (see the TouchMouse input) + if (!this.pressed || !this.allow) { + return; } - return c; - } - function currentDateArray(config) { - var now = new Date(); - if (config._useUTC) { - return [now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()]; + if (eventType & INPUT_END) { + this.pressed = false; } - return [now.getFullYear(), now.getMonth(), now.getDate()]; + + this.callback(this.manager, eventType, { + pointers: [ev], + changedPointers: [ev], + pointerType: INPUT_TYPE_MOUSE, + srcEvent: ev + }); } + }); - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function configFromArray (config) { - var i, date, input = [], currentDate, yearToUse; + var POINTER_INPUT_MAP = { + pointerdown: INPUT_START, + pointermove: INPUT_MOVE, + pointerup: INPUT_END, + pointercancel: INPUT_CANCEL, + pointerout: INPUT_CANCEL + }; - if (config._d) { - return; - } + // in IE10 the pointer types is defined as an enum + var IE10_POINTER_TYPE_ENUM = { + 2: INPUT_TYPE_TOUCH, + 3: INPUT_TYPE_PEN, + 4: INPUT_TYPE_MOUSE, + 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 + }; - currentDate = currentDateArray(config); + var POINTER_ELEMENT_EVENTS = 'pointerdown'; + var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } + // IE10 has prefixed support, and case-sensitive + if (window.MSPointerEvent) { + POINTER_ELEMENT_EVENTS = 'MSPointerDown'; + POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; + } - //if the day of the year is set, figure out what it is - if (config._dayOfYear) { - yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); + /** + * Pointer events input + * @constructor + * @extends Input + */ + function PointerEventInput() { + this.evEl = POINTER_ELEMENT_EVENTS; + this.evWin = POINTER_WINDOW_EVENTS; - if (config._dayOfYear > daysInYear(yearToUse)) { - config._pf._overflowDayOfYear = true; - } + Input.apply(this, arguments); - date = createUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } + this.store = (this.manager.session.pointerEvents = []); + } - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } + inherit(PointerEventInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function PEhandler(ev) { + var store = this.store; + var removePointer = false; - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } + var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); + var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; + var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; + var isTouch = (pointerType == INPUT_TYPE_TOUCH); + + // get index of the event in the store + var storeIndex = inArray(store, ev.pointerId, 'pointerId'); + + // start and mouse must be down + if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { + if (storeIndex < 0) { + store.push(ev); + storeIndex = store.length - 1; + } + } else if (eventType & (INPUT_END | INPUT_CANCEL)) { + removePointer = true; } - config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + // it not found, so the pointer hasn't been down (so it's probably a hover) + if (storeIndex < 0) { + return; } - if (config._nextDay) { - config._a[HOUR] = 24; + // update the event in the store + store[storeIndex] = ev; + + this.callback(this.manager, eventType, { + pointers: store, + changedPointers: [ev], + pointerType: pointerType, + srcEvent: ev + }); + + if (removePointer) { + // remove from the store + store.splice(storeIndex, 1); } } + }); - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp; + var SINGLE_TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL + }; - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; + var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; + var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year); - week = defaults(w.W, 1); - weekday = defaults(w.E, 1); - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; + /** + * Touch events input + * @constructor + * @extends Input + */ + function SingleTouchInput() { + this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; + this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; + this.started = false; - weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year); - week = defaults(w.w, 1); + Input.apply(this, arguments); + } - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < dow) { - ++week; - } - } else if (w.e != null) { - // local weekday -- counting starts from begining of week - weekday = w.e + dow; - } else { - // default to begining of week - weekday = dow; - } + inherit(SingleTouchInput, Input, { + handler: function TEhandler(ev) { + var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; + + // should we handle the touch events? + if (type === INPUT_START) { + this.started = true; } - temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } + if (!this.started) { + return; + } - utils_hooks__hooks.ISO_8601 = function () {}; + var touches = normalizeSingleTouches.call(this, ev, type); - // date from string and format string - function configFromStringAndFormat(config) { - // TODO: Move this to another part of the creation flow to prevent circular deps - if (config._f === utils_hooks__hooks.ISO_8601) { - configFromISO(config); - return; + // when done, reset the started state + if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { + this.started = false; } - config._a = []; - config._pf.empty = true; + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } + }); - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; + /** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ + function normalizeSingleTouches(ev, type) { + var all = toArray(ev.touches); + var changed = toArray(ev.changedTouches); - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + if (type & (INPUT_END | INPUT_CANCEL)) { + all = uniqueArray(all.concat(changed), 'identifier', true); + } - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - config._pf.unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - config._pf.empty = false; - } - else { - config._pf.unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - config._pf.unusedTokens.push(token); - } - } + return [all, changed]; + } - // add remaining unparsed input length to the string - config._pf.charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - config._pf.unusedInput.push(string); - } + var TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL + }; - // clear _12h flag if hour is <= 12 - if (config._pf.bigHour === true && config._a[HOUR] <= 12) { - config._pf.bigHour = undefined; + var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; + + /** + * Multi-user touch events input + * @constructor + * @extends Input + */ + function TouchInput() { + this.evTarget = TOUCH_TARGET_EVENTS; + this.targetIds = {}; + + Input.apply(this, arguments); + } + + inherit(TouchInput, Input, { + handler: function MTEhandler(ev) { + var type = TOUCH_INPUT_MAP[ev.type]; + var touches = getTouches.call(this, ev, type); + if (!touches) { + return; } - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); - configFromArray(config); - checkOverflow(config); + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); } + }); + /** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ + function getTouches(ev, type) { + var allTouches = toArray(ev.touches); + var targetIds = this.targetIds; - function meridiemFixWrap (locale, hour, meridiem) { - var isPm; - - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // this is not supposed to happen - return hour; - } + // when there is only one touch, the process can be simplified + if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { + targetIds[allTouches[0].identifier] = true; + return [allTouches, allTouches]; } - function configFromStringAndArray(config) { - var tempConfig, - bestMoment, + var i, + targetTouches, + changedTouches = toArray(ev.changedTouches), + changedTargetTouches = [], + target = this.target; - scoreToBeat, - i, - currentScore; + // get target touches from touches + targetTouches = allTouches.filter(function(touch) { + return hasParent(touch.target, target); + }); - if (config._f.length === 0) { - config._pf.invalidFormat = true; - config._d = new Date(NaN); - return; + // collect touches + if (type === INPUT_START) { + i = 0; + while (i < targetTouches.length) { + targetIds[targetTouches[i].identifier] = true; + i++; } + } - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._pf = defaultParsingFlags(); - tempConfig._f = config._f[i]; - configFromStringAndFormat(tempConfig); + // filter changed touches to only contain touches that exist in the collected target ids + i = 0; + while (i < changedTouches.length) { + if (targetIds[changedTouches[i].identifier]) { + changedTargetTouches.push(changedTouches[i]); + } - if (!valid__isValid(tempConfig)) { - continue; - } + // cleanup removed touches + if (type & (INPUT_END | INPUT_CANCEL)) { + delete targetIds[changedTouches[i].identifier]; + } + i++; + } - // if there is any input that was not parsed add a penalty for that format - currentScore += tempConfig._pf.charsLeftOver; + if (!changedTargetTouches.length) { + return; + } - //or tokens - currentScore += tempConfig._pf.unusedTokens.length * 10; + return [ + // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' + uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), + changedTargetTouches + ]; + } - tempConfig._pf.score = currentScore; + /** + * Combined touch and mouse input + * + * Touch has a higher priority then mouse, and while touching no mouse events are allowed. + * This because touch devices also emit mouse events while doing a touch. + * + * @constructor + * @extends Input + */ + function TouchMouseInput() { + Input.apply(this, arguments); - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } + var handler = bindFn(this.handler, this); + this.touch = new TouchInput(this.manager, handler); + this.mouse = new MouseInput(this.manager, handler); + } - extend(config, bestMoment || tempConfig); - } + inherit(TouchMouseInput, Input, { + /** + * handle mouse and touch events + * @param {Hammer} manager + * @param {String} inputEvent + * @param {Object} inputData + */ + handler: function TMEhandler(manager, inputEvent, inputData) { + var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), + isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); - function configFromObject(config) { - if (config._d) { + // when we're in a touch event, so block all upcoming mouse events + // most mobile browser also emit mouseevents, right after touchstart + if (isTouch) { + this.mouse.allow = false; + } else if (isMouse && !this.mouse.allow) { return; } - var i = normalizeObjectUnits(config._i); - config._a = [i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond]; + // reset the allowMouse when we're done + if (inputEvent & (INPUT_END | INPUT_CANCEL)) { + this.mouse.allow = true; + } - configFromArray(config); + this.callback(manager, inputEvent, inputData); + }, + + /** + * remove the event listeners + */ + destroy: function destroy() { + this.touch.destroy(); + this.mouse.destroy(); } + }); - function createFromConfig (config) { - var input = config._i, - format = config._f, - res; + var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); + var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; - config._locale = config._locale || locale_locales__getLocale(config._l); + // magical touchAction value + var TOUCH_ACTION_COMPUTE = 'compute'; + var TOUCH_ACTION_AUTO = 'auto'; + var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented + var TOUCH_ACTION_NONE = 'none'; + var TOUCH_ACTION_PAN_X = 'pan-x'; + var TOUCH_ACTION_PAN_Y = 'pan-y'; - if (input === null || (format === undefined && input === '')) { - return valid__createInvalid({nullInput: true}); - } + /** + * Touch Action + * sets the touchAction property or uses the js alternative + * @param {Manager} manager + * @param {String} value + * @constructor + */ + function TouchAction(manager, value) { + this.manager = manager; + this.set(value); + } - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); + TouchAction.prototype = { + /** + * set the touchAction value on the element or enable the polyfill + * @param {String} value + */ + set: function(value) { + // find out the touch-action by the event handlers + if (value == TOUCH_ACTION_COMPUTE) { + value = this.compute(); } - if (isMoment(input)) { - return new Moment(checkOverflow(input)); - } else if (isArray(format)) { - configFromStringAndArray(config); - } else if (format) { - configFromStringAndFormat(config); - } else { - configFromInput(config); + if (NATIVE_TOUCH_ACTION) { + this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; } + this.actions = value.toLowerCase().trim(); + }, - res = new Moment(checkOverflow(config)); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; + /** + * just re-set the touchAction value + */ + update: function() { + this.set(this.manager.options.touchAction); + }, + + /** + * compute the value for the touchAction property based on the recognizer's settings + * @returns {String} value + */ + compute: function() { + var actions = []; + each(this.manager.recognizers, function(recognizer) { + if (boolOrFn(recognizer.options.enable, [recognizer])) { + actions = actions.concat(recognizer.getTouchAction()); + } + }); + return cleanTouchActions(actions.join(' ')); + }, + + /** + * this method is called on each input cycle and provides the preventing of the browser behavior + * @param {Object} input + */ + preventDefaults: function(input) { + // not needed with native support for the touchAction property + if (NATIVE_TOUCH_ACTION) { + return; } - return res; - } + var srcEvent = input.srcEvent; + var direction = input.offsetDirection; - function configFromInput(config) { - var input = config._i; - if (input === undefined) { - config._d = new Date(); - } else if (isDate(input)) { - config._d = new Date(+input); - } else if (typeof input === 'string') { - configFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - configFromArray(config); - } else if (typeof(input) === 'object') { - configFromObject(config); - } else if (typeof(input) === 'number') { - // from milliseconds - config._d = new Date(input); - } else { - utils_hooks__hooks.createFromInputFallback(config); + // if the touch action did prevented once this session + if (this.manager.session.prevented) { + srcEvent.preventDefault(); + return; } - } - function createLocalOrUTC (input, format, locale, strict, isUTC) { - var c = {}; + var actions = this.actions; + var hasNone = inStr(actions, TOUCH_ACTION_NONE); + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; + if (hasNone || + (hasPanY && direction & DIRECTION_HORIZONTAL) || + (hasPanX && direction & DIRECTION_VERTICAL)) { + return this.preventSrc(srcEvent); } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c._isAMomentObject = true; - c._useUTC = c._isUTC = isUTC; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - c._pf = defaultParsingFlags(); + }, - return createFromConfig(c); + /** + * call preventDefault to prevent the browser's default behavior (scrolling in most cases) + * @param {Object} srcEvent + */ + preventSrc: function(srcEvent) { + this.manager.session.prevented = true; + srcEvent.preventDefault(); } + }; - function local__createLocal (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, false); + /** + * when the touchActions are collected they are not a valid value, so we need to clean things up. * + * @param {String} actions + * @returns {*} + */ + function cleanTouchActions(actions) { + // none + if (inStr(actions, TOUCH_ACTION_NONE)) { + return TOUCH_ACTION_NONE; } - var prototypeMin = deprecate( - 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', - function () { - var other = local__createLocal.apply(null, arguments); - return other < this ? this : other; - } - ); - - var prototypeMax = deprecate( - 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', - function () { - var other = local__createLocal.apply(null, arguments); - return other > this ? this : other; - } - ); + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return local__createLocal(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (moments[i][fn](res)) { - res = moments[i]; - } - } - return res; + // pan-x and pan-y can be combined + if (hasPanX && hasPanY) { + return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y; } - // TODO: Use [].sort instead? - function min () { - var args = [].slice.call(arguments, 0); + // pan-x OR pan-y + if (hasPanX || hasPanY) { + return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; + } - return pickBy('isBefore', args); + // manipulation + if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { + return TOUCH_ACTION_MANIPULATION; } - function max () { - var args = [].slice.call(arguments, 0); + return TOUCH_ACTION_AUTO; + } - return pickBy('isAfter', args); - } + /** + * Recognizer flow explained; * + * All recognizers have the initial state of POSSIBLE when a input session starts. + * The definition of a input session is from the first input until the last input, with all it's movement in it. * + * Example session for mouse-input: mousedown -> mousemove -> mouseup + * + * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed + * which determines with state it should be. + * + * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to + * POSSIBLE to give it another change on the next cycle. + * + * Possible + * | + * +-----+---------------+ + * | | + * +-----+-----+ | + * | | | + * Failed Cancelled | + * +-------+------+ + * | | + * Recognized Began + * | + * Changed + * | + * Ended/Recognized + */ + var STATE_POSSIBLE = 1; + var STATE_BEGAN = 2; + var STATE_CHANGED = 4; + var STATE_ENDED = 8; + var STATE_RECOGNIZED = STATE_ENDED; + var STATE_CANCELLED = 16; + var STATE_FAILED = 32; - function Duration (duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; + /** + * Recognizer + * Every recognizer needs to extend from this class. + * @constructor + * @param {Object} options + */ + function Recognizer(options) { + this.id = uniqueId(); - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 36e5; // 1000 * 60 * 60 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; + this.manager = null; + this.options = merge(options || {}, this.defaults); - this._data = {}; + // default is enable true + this.options.enable = ifUndefined(this.options.enable, true); - this._locale = locale_locales__getLocale(); + this.state = STATE_POSSIBLE; - this._bubble(); - } + this.simultaneous = {}; + this.requireFail = []; + } - function isDuration (obj) { - return obj instanceof Duration; - } + Recognizer.prototype = { + /** + * @virtual + * @type {Object} + */ + defaults: {}, - function offset (token, separator) { - addFormatToken(token, 0, 0, function () { - var offset = this.utcOffset(); - var sign = '+'; - if (offset < 0) { - offset = -offset; - sign = '-'; - } - return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); - }); - } + /** + * set options + * @param {Object} options + * @return {Recognizer} + */ + set: function(options) { + extend(this.options, options); - offset('Z', ':'); - offset('ZZ', ''); + // also update the touchAction, in case something changed about the directions/enabled state + this.manager && this.manager.touchAction.update(); + return this; + }, - // PARSING + /** + * recognize simultaneous with an other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + recognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { + return this; + } - addRegexToken('Z', matchOffset); - addRegexToken('ZZ', matchOffset); - addParseToken(['Z', 'ZZ'], function (input, array, config) { - config._useUTC = true; - config._tzm = offsetFromString(input); - }); + var simultaneous = this.simultaneous; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (!simultaneous[otherRecognizer.id]) { + simultaneous[otherRecognizer.id] = otherRecognizer; + otherRecognizer.recognizeWith(this); + } + return this; + }, - // HELPERS + /** + * drop the simultaneous link. it doesnt remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRecognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { + return this; + } - // timezone chunker - // '+10:00' > ['10', '00'] - // '-1530' > ['-15', '30'] - var chunkOffset = /([\+\-]|\d\d)/gi; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + delete this.simultaneous[otherRecognizer.id]; + return this; + }, - function offsetFromString(string) { - var matches = ((string || '').match(matchOffset) || []); - var chunk = matches[matches.length - 1] || []; - var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; - var minutes = +(parts[1] * 60) + toInt(parts[2]); + /** + * recognizer can only run when an other is failing + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + requireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { + return this; + } - return parts[0] === '+' ? minutes : -minutes; - } + var requireFail = this.requireFail; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (inArray(requireFail, otherRecognizer) === -1) { + requireFail.push(otherRecognizer); + otherRecognizer.requireFailure(this); + } + return this; + }, - // Return a moment from input, that is local/utc/zone equivalent to model. - function cloneWithOffset(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (isMoment(input) || isDate(input) ? +input : +local__createLocal(input)) - (+res); - // Use low-level api, because this fn is low-level api. - res._d.setTime(+res._d + diff); - utils_hooks__hooks.updateOffset(res, false); - return res; - } else { - return local__createLocal(input).local(); + /** + * drop the requireFailure link. it does not remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRequireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { + return this; } - return model._isUTC ? local__createLocal(input).zone(model._offset || 0) : local__createLocal(input).local(); - } - function getDateOffset (m) { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(m._d.getTimezoneOffset() / 15) * 15; - } + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + var index = inArray(this.requireFail, otherRecognizer); + if (index > -1) { + this.requireFail.splice(index, 1); + } + return this; + }, - // HOOKS + /** + * has require failures boolean + * @returns {boolean} + */ + hasRequireFailures: function() { + return this.requireFail.length > 0; + }, - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - utils_hooks__hooks.updateOffset = function () {}; + /** + * if the recognizer can recognize simultaneous with an other recognizer + * @param {Recognizer} otherRecognizer + * @returns {Boolean} + */ + canRecognizeWith: function(otherRecognizer) { + return !!this.simultaneous[otherRecognizer.id]; + }, - // MOMENTS + /** + * You should use `tryEmit` instead of `emit` directly to check + * that all the needed recognizers has failed before emitting. + * @param {Object} input + */ + emit: function(input) { + var self = this; + var state = this.state; - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - function getSetOffset (input, keepLocalTime) { - var offset = this._offset || 0, - localAdjust; - if (input != null) { - if (typeof input === 'string') { - input = offsetFromString(input); - } - if (Math.abs(input) < 16) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = getDateOffset(this); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - utils_hooks__hooks.updateOffset(this, true); - this._changeInProgress = null; - } - } - return this; - } else { - return this._isUTC ? offset : getDateOffset(this); + function emit(withState) { + self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input); } - } - function getSetZone (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } + // 'panstart' and 'panmove' + if (state < STATE_ENDED) { + emit(true); + } - this.utcOffset(input, keepLocalTime); + emit(); // simple 'eventName' events - return this; - } else { - return -this.utcOffset(); + // panend and pancancel + if (state >= STATE_ENDED) { + emit(true); } - } - - function setOffsetToUTC (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - } + }, - function setOffsetToLocal (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; + /** + * Check that all the require failure recognizers has failed, + * if true, it emits a gesture event, + * otherwise, setup the state to FAILED. + * @param {Object} input + */ + tryEmit: function(input) { + if (this.canEmit()) { + return this.emit(input); + } + // it's failing anyway + this.state = STATE_FAILED; + }, - if (keepLocalTime) { - this.subtract(getDateOffset(this), 'm'); + /** + * can we emit? + * @returns {boolean} + */ + canEmit: function() { + var i = 0; + while (i < this.requireFail.length) { + if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { + return false; } + i++; } - return this; - } + return true; + }, - function setOffsetToParsedOffset () { - if (this._tzm) { - this.utcOffset(this._tzm); - } else if (typeof this._i === 'string') { - this.utcOffset(offsetFromString(this._i)); - } - return this; - } + /** + * update the recognizer + * @param {Object} inputData + */ + recognize: function(inputData) { + // make a new copy of the inputData + // so we can change the inputData without messing up the other recognizers + var inputDataClone = extend({}, inputData); - function hasAlignedHourOffset (input) { - if (!input) { - input = 0; - } - else { - input = local__createLocal(input).utcOffset(); + // is is enabled and allow recognizing? + if (!boolOrFn(this.options.enable, [this, inputDataClone])) { + this.reset(); + this.state = STATE_FAILED; + return; } - return (this.utcOffset() - input) % 60 === 0; - } + // reset when we've reached the end + if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { + this.state = STATE_POSSIBLE; + } - function isDaylightSavingTime () { - return ( - this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset() - ); - } + this.state = this.process(inputDataClone); - function isDaylightSavingTimeShifted () { - if (this._a) { - var other = this._isUTC ? create_utc__createUTC(this._a) : local__createLocal(this._a); - return this.isValid() && compareArrays(this._a, other.toArray()) > 0; + // the recognizer has recognized a gesture + // so trigger an event + if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { + this.tryEmit(inputDataClone); } + }, - return false; - } + /** + * return the state of the recognizer + * the actual recognizing happens in this method + * @virtual + * @param {Object} inputData + * @returns {Const} STATE + */ + process: function(inputData) { }, // jshint ignore:line - function isLocal () { - return !this._isUTC; - } + /** + * return the preferred touch-action + * @virtual + * @returns {Array} + */ + getTouchAction: function() { }, - function isUtcOffset () { - return this._isUTC; + /** + * called when the gesture isn't allowed to recognize + * like when another is being recognized or it is disabled + * @virtual + */ + reset: function() { } + }; + + /** + * get a usable string, used as event postfix + * @param {Const} state + * @returns {String} state + */ + function stateStr(state) { + if (state & STATE_CANCELLED) { + return 'cancel'; + } else if (state & STATE_ENDED) { + return 'end'; + } else if (state & STATE_CHANGED) { + return 'move'; + } else if (state & STATE_BEGAN) { + return 'start'; } + return ''; + } - function isUtc () { - return this._isUTC && this._offset === 0; + /** + * direction cons to string + * @param {Const} direction + * @returns {String} + */ + function directionStr(direction) { + if (direction == DIRECTION_DOWN) { + return 'down'; + } else if (direction == DIRECTION_UP) { + return 'up'; + } else if (direction == DIRECTION_LEFT) { + return 'left'; + } else if (direction == DIRECTION_RIGHT) { + return 'right'; } + return ''; + } - var aspNetRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/; + /** + * get a recognizer by name if it is bound to a manager + * @param {Recognizer|String} otherRecognizer + * @param {Recognizer} recognizer + * @returns {Recognizer} + */ + function getRecognizerByNameIfManager(otherRecognizer, recognizer) { + var manager = recognizer.manager; + if (manager) { + return manager.get(otherRecognizer); + } + return otherRecognizer; + } - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - var create__isoRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/; + /** + * This recognizer is just used as a base for the simple attribute recognizers. + * @constructor + * @extends Recognizer + */ + function AttrRecognizer() { + Recognizer.apply(this, arguments); + } - function create__createDuration (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - diffRes; + inherit(AttrRecognizer, Recognizer, { + /** + * @namespace + * @memberof AttrRecognizer + */ + defaults: { + /** + * @type {Number} + * @default 1 + */ + pointers: 1 + }, - if (isDuration(input)) { - duration = { - ms : input._milliseconds, - d : input._days, - M : input._months - }; - } else if (typeof input === 'number') { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : 0, - d : toInt(match[DATE]) * sign, - h : toInt(match[HOUR]) * sign, - m : toInt(match[MINUTE]) * sign, - s : toInt(match[SECOND]) * sign, - ms : toInt(match[MILLISECOND]) * sign - }; - } else if (!!(match = create__isoRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : parseIso(match[2], sign), - M : parseIso(match[3], sign), - d : parseIso(match[4], sign), - h : parseIso(match[5], sign), - m : parseIso(match[6], sign), - s : parseIso(match[7], sign), - w : parseIso(match[8], sign) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to)); + /** + * Used to check if it the recognizer receives valid input, like input.distance > 10. + * @memberof AttrRecognizer + * @param {Object} input + * @returns {Boolean} recognized + */ + attrTest: function(input) { + var optionPointers = this.options.pointers; + return optionPointers === 0 || input.pointers.length === optionPointers; + }, - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } + /** + * Process the input and return the state for the recognizer + * @memberof AttrRecognizer + * @param {Object} input + * @returns {*} State + */ + process: function(input) { + var state = this.state; + var eventType = input.eventType; - ret = new Duration(duration); + var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); + var isValid = this.attrTest(input); - if (isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; + // on cancel input and we've recognized before, return STATE_CANCELLED + if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { + return state | STATE_CANCELLED; + } else if (isRecognized || isValid) { + if (eventType & INPUT_END) { + return state | STATE_ENDED; + } else if (!(state & STATE_BEGAN)) { + return STATE_BEGAN; + } + return state | STATE_CHANGED; } - - return ret; + return STATE_FAILED; } + }); - create__createDuration.fn = Duration.prototype; + /** + * Pan + * Recognized when the pointer is down and moved in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ + function PanRecognizer() { + AttrRecognizer.apply(this, arguments); - function parseIso (inp, sign) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - } + this.pX = null; + this.pY = null; + } - function positiveMomentsDifference(base, other) { - var res = {milliseconds: 0, months: 0}; + inherit(PanRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PanRecognizer + */ + defaults: { + event: 'pan', + threshold: 10, + pointers: 1, + direction: DIRECTION_ALL + }, - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; + getTouchAction: function() { + var direction = this.options.direction; + var actions = []; + if (direction & DIRECTION_HORIZONTAL) { + actions.push(TOUCH_ACTION_PAN_Y); } - - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - - return res; - } - - function momentsDifference(base, other) { - var res; - other = cloneWithOffset(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; + if (direction & DIRECTION_VERTICAL) { + actions.push(TOUCH_ACTION_PAN_X); } + return actions; + }, - return res; - } + directionTest: function(input) { + var options = this.options; + var hasMoved = true; + var distance = input.distance; + var direction = input.direction; + var x = input.deltaX; + var y = input.deltaY; - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); - tmp = val; val = period; period = tmp; + // lock to axis? + if (!(direction & options.direction)) { + if (options.direction & DIRECTION_HORIZONTAL) { + direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + hasMoved = x != this.pX; + distance = Math.abs(input.deltaX); + } else { + direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; + hasMoved = y != this.pY; + distance = Math.abs(input.deltaY); } + } + input.direction = direction; + return hasMoved && distance > options.threshold && direction & options.direction; + }, - val = typeof val === 'string' ? +val : val; - dur = create__createDuration(val, period); - add_subtract__addSubtract(this, dur, direction); - return this; - }; - } + attrTest: function(input) { + return AttrRecognizer.prototype.attrTest.call(this, input) && + (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); + }, - function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = duration._days, - months = duration._months; - updateOffset = updateOffset == null ? true : updateOffset; + emit: function(input) { + this.pX = input.deltaX; + this.pY = input.deltaY; - if (milliseconds) { - mom._d.setTime(+mom._d + milliseconds * isAdding); - } - if (days) { - get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding); - } - if (months) { - setMonth(mom, get_set__get(mom, 'Month') + months * isAdding); - } - if (updateOffset) { - utils_hooks__hooks.updateOffset(mom, days || months); + var direction = directionStr(input.direction); + if (direction) { + this.manager.emit(this.options.event + direction, input); } - } - - var add_subtract__add = createAdder(1, 'add'); - var add_subtract__subtract = createAdder(-1, 'subtract'); - function moment_calendar__calendar (time) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're local/utc/offset or not. - var now = time || local__createLocal(), - sod = cloneWithOffset(now, this).startOf('day'), - diff = this.diff(sod, 'days', true), - format = diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - return this.format(this.localeData().calendar(format, this, local__createLocal(now))); + this._super.emit.call(this, input); } + }); - function clone () { - return new Moment(this); - } + /** + * Pinch + * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). + * @constructor + * @extends AttrRecognizer + */ + function PinchRecognizer() { + AttrRecognizer.apply(this, arguments); + } - function isAfter (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = isMoment(input) ? input : local__createLocal(input); - return +this > +input; - } else { - inputMs = isMoment(input) ? +input : +local__createLocal(input); - return inputMs < +this.clone().startOf(units); - } - } + inherit(PinchRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'pinch', + threshold: 0, + pointers: 2 + }, - function isBefore (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = isMoment(input) ? input : local__createLocal(input); - return +this < +input; - } else { - inputMs = isMoment(input) ? +input : +local__createLocal(input); - return +this.clone().endOf(units) < inputMs; - } - } + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, - function isBetween (from, to, units) { - return this.isAfter(from, units) && this.isBefore(to, units); - } + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); + }, - function isSame (input, units) { - var inputMs; - units = normalizeUnits(units || 'millisecond'); - if (units === 'millisecond') { - input = isMoment(input) ? input : local__createLocal(input); - return +this === +input; - } else { - inputMs = +local__createLocal(input); - return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); + emit: function(input) { + this._super.emit.call(this, input); + if (input.scale !== 1) { + var inOut = input.scale < 1 ? 'in' : 'out'; + this.manager.emit(this.options.event + inOut, input); } } + }); - function absFloor (number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); - } - } + /** + * Press + * Recognized when the pointer is down for x ms without any movement. + * @constructor + * @extends Recognizer + */ + function PressRecognizer() { + Recognizer.apply(this, arguments); - function diff (input, units, asFloat) { - var that = cloneWithOffset(input, this), - zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4, - delta, output; + this._timer = null; + this._input = null; + } - units = normalizeUnits(units); + inherit(PressRecognizer, Recognizer, { + /** + * @namespace + * @memberof PressRecognizer + */ + defaults: { + event: 'press', + pointers: 1, + time: 500, // minimal time of the pointer to be pressed + threshold: 5 // a minimal movement is ok, but keep it low + }, - if (units === 'year' || units === 'month' || units === 'quarter') { - output = monthDiff(this, that); - if (units === 'quarter') { - output = output / 3; - } else if (units === 'year') { - output = output / 12; - } - } else { - delta = this - that; - output = units === 'second' ? delta / 1e3 : // 1000 - units === 'minute' ? delta / 6e4 : // 1000 * 60 - units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 - units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst - units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst - delta; - } - return asFloat ? output : absFloor(output); - } + getTouchAction: function() { + return [TOUCH_ACTION_AUTO]; + }, - function monthDiff (a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; + process: function(input) { + var options = this.options; + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTime = input.deltaTime > options.time; - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } + this._input = input; - return -(wholeMonthDiff + adjust); - } + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { + this.reset(); + } else if (input.eventType & INPUT_START) { + this.reset(); + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.time, this); + } else if (input.eventType & INPUT_END) { + return STATE_RECOGNIZED; + } + return STATE_FAILED; + }, - utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; + reset: function() { + clearTimeout(this._timer); + }, - function toString () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - } + emit: function(input) { + if (this.state !== STATE_RECOGNIZED) { + return; + } - function moment_format__toISOString () { - var m = this.clone().utc(); - if (0 < m.year() && m.year() <= 9999) { - if ('function' === typeof Date.prototype.toISOString) { - // native implementation is ~50x faster, use it when we can - return this.toDate().toISOString(); - } else { - return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } + if (input && (input.eventType & INPUT_END)) { + this.manager.emit(this.options.event + 'up', input); } else { - return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + this._input.timeStamp = now(); + this.manager.emit(this.options.event, this._input); } } + }); - function format (inputString) { - var output = formatMoment(this, inputString || utils_hooks__hooks.defaultFormat); - return this.localeData().postformat(output); - } - - function from (time, withoutSuffix) { - return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - } + /** + * Rotate + * Recognized when two or more pointer are moving in a circular motion. + * @constructor + * @extends AttrRecognizer + */ + function RotateRecognizer() { + AttrRecognizer.apply(this, arguments); + } - function fromNow (withoutSuffix) { - return this.from(local__createLocal(), withoutSuffix); - } + inherit(RotateRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof RotateRecognizer + */ + defaults: { + event: 'rotate', + threshold: 0, + pointers: 2 + }, - function locale (key) { - var newLocaleData; + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = locale_locales__getLocale(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); } + }); - var lang = deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ); + /** + * Swipe + * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ + function SwipeRecognizer() { + AttrRecognizer.apply(this, arguments); + } - function localeData () { - return this._locale; - } + inherit(SwipeRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof SwipeRecognizer + */ + defaults: { + event: 'swipe', + threshold: 10, + velocity: 0.65, + direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, + pointers: 1 + }, - function startOf (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { - case 'year': - this.month(0); - /* falls through */ - case 'quarter': - case 'month': - this.date(1); - /* falls through */ - case 'week': - case 'isoWeek': - case 'day': - this.hours(0); - /* falls through */ - case 'hour': - this.minutes(0); - /* falls through */ - case 'minute': - this.seconds(0); - /* falls through */ - case 'second': - this.milliseconds(0); - } + getTouchAction: function() { + return PanRecognizer.prototype.getTouchAction.call(this); + }, - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } - if (units === 'isoWeek') { - this.isoWeekday(1); - } + attrTest: function(input) { + var direction = this.options.direction; + var velocity; - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); + if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { + velocity = input.velocity; + } else if (direction & DIRECTION_HORIZONTAL) { + velocity = input.velocityX; + } else if (direction & DIRECTION_VERTICAL) { + velocity = input.velocityY; } - return this; - } + return this._super.attrTest.call(this, input) && + direction & input.direction && + input.distance > this.options.threshold && + abs(velocity) > this.options.velocity && input.eventType & INPUT_END; + }, - function endOf (units) { - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond') { - return this; + emit: function(input) { + var direction = directionStr(input.direction); + if (direction) { + this.manager.emit(this.options.event + direction, input); } - return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); - } - - function to_type__valueOf () { - return +this._d - ((this._offset || 0) * 60000); - } - - function unix () { - return Math.floor(+this / 1000); - } - - function toDate () { - return this._offset ? new Date(+this) : this._d; - } - function toArray () { - var m = this; - return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; + this.manager.emit(this.options.event, input); } + }); - function moment_valid__isValid () { - return valid__isValid(this); - } + /** + * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur + * between the given interval and position. The delay option can be used to recognize multi-taps without firing + * a single tap. + * + * The eventData from the emitted event contains the property `tapCount`, which contains the amount of + * multi-taps being recognized. + * @constructor + * @extends Recognizer + */ + function TapRecognizer() { + Recognizer.apply(this, arguments); - function parsingFlags () { - return extend({}, this._pf); - } + // previous time and center, + // used for tap counting + this.pTime = false; + this.pCenter = false; - function invalidAt () { - return this._pf.overflow; - } + this._timer = null; + this._input = null; + this.count = 0; + } - addFormatToken(0, ['gg', 2], 0, function () { - return this.weekYear() % 100; - }); - - addFormatToken(0, ['GG', 2], 0, function () { - return this.isoWeekYear() % 100; - }); - - function addWeekYearFormatToken (token, getter) { - addFormatToken(0, [token, token.length], 0, getter); - } + inherit(TapRecognizer, Recognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'tap', + pointers: 1, + taps: 1, + interval: 300, // max time between the multi-tap taps + time: 250, // max time of the pointer to be down (like finger on the screen) + threshold: 2, // a minimal movement is ok, but keep it low + posThreshold: 10 // a multi-tap can be a bit off the initial position + }, - addWeekYearFormatToken('gggg', 'weekYear'); - addWeekYearFormatToken('ggggg', 'weekYear'); - addWeekYearFormatToken('GGGG', 'isoWeekYear'); - addWeekYearFormatToken('GGGGG', 'isoWeekYear'); + getTouchAction: function() { + return [TOUCH_ACTION_MANIPULATION]; + }, - // ALIASES + process: function(input) { + var options = this.options; - addUnitAlias('weekYear', 'gg'); - addUnitAlias('isoWeekYear', 'GG'); + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTouchTime = input.deltaTime < options.time; - // PARSING + this.reset(); - addRegexToken('G', matchSigned); - addRegexToken('g', matchSigned); - addRegexToken('GG', match1to2, match2); - addRegexToken('gg', match1to2, match2); - addRegexToken('GGGG', match1to4, match4); - addRegexToken('gggg', match1to4, match4); - addRegexToken('GGGGG', match1to6, match6); - addRegexToken('ggggg', match1to6, match6); + if ((input.eventType & INPUT_START) && (this.count === 0)) { + return this.failTimeout(); + } - addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { - week[token.substr(0, 2)] = toInt(input); - }); + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (validMovement && validTouchTime && validPointers) { + if (input.eventType != INPUT_END) { + return this.failTimeout(); + } - addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { - week[token] = utils_hooks__hooks.parseTwoDigitYear(input); - }); + var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; + var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; - // HELPERS + this.pTime = input.timeStamp; + this.pCenter = input.center; - function weeksInYear(year, dow, doy) { - return weekOfYear(local__createLocal([year, 11, 31 + dow - doy]), dow, doy).week; - } + if (!validMultiTap || !validInterval) { + this.count = 1; + } else { + this.count += 1; + } - // MOMENTS + this._input = input; - function getSetWeekYear (input) { - var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; - return input == null ? year : this.add((input - year), 'y'); - } + // if tap count matches we have recognized it, + // else it has began recognizing... + var tapCount = this.count % options.taps; + if (tapCount === 0) { + // no failing requirements, immediately trigger the tap event + // or wait as long as the multitap interval to trigger + if (!this.hasRequireFailures()) { + return STATE_RECOGNIZED; + } else { + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.interval, this); + return STATE_BEGAN; + } + } + } + return STATE_FAILED; + }, - function getSetISOWeekYear (input) { - var year = weekOfYear(this, 1, 4).year; - return input == null ? year : this.add((input - year), 'y'); - } + failTimeout: function() { + this._timer = setTimeoutContext(function() { + this.state = STATE_FAILED; + }, this.options.interval, this); + return STATE_FAILED; + }, - function getISOWeeksInYear () { - return weeksInYear(this.year(), 1, 4); - } + reset: function() { + clearTimeout(this._timer); + }, - function getWeeksInYear () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + emit: function() { + if (this.state == STATE_RECOGNIZED ) { + this._input.tapCount = this.count; + this.manager.emit(this.options.event, this._input); + } } + }); - addFormatToken('Q', 0, 0, 'quarter'); - - // ALIASES - - addUnitAlias('quarter', 'Q'); + /** + * Simple way to create an manager with a default set of recognizers. + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ + function Hammer(element, options) { + options = options || {}; + options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); + return new Manager(element, options); + } - // PARSING + /** + * @const {string} + */ + Hammer.VERSION = '2.0.4'; - addRegexToken('Q', match1); - addParseToken('Q', function (input, array) { - array[MONTH] = (toInt(input) - 1) * 3; - }); + /** + * default settings + * @namespace + */ + Hammer.defaults = { + /** + * set if DOM events are being triggered. + * But this is slower and unused by simple implementations, so disabled by default. + * @type {Boolean} + * @default false + */ + domEvents: false, - // MOMENTS + /** + * The value for the touchAction property/fallback. + * When set to `compute` it will magically set the correct value based on the added recognizers. + * @type {String} + * @default compute + */ + touchAction: TOUCH_ACTION_COMPUTE, - function getSetQuarter (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - } + /** + * @type {Boolean} + * @default true + */ + enable: true, - addFormatToken('D', ['DD', 2], 'Do', 'date'); + /** + * EXPERIMENTAL FEATURE -- can be removed/changed + * Change the parent input target element. + * If Null, then it is being set the to main element. + * @type {Null|EventTarget} + * @default null + */ + inputTarget: null, - // ALIASES + /** + * force an input class + * @type {Null|Function} + * @default null + */ + inputClass: null, - addUnitAlias('date', 'D'); + /** + * Default recognizer setup when calling `Hammer()` + * When creating a new Manager these will be skipped. + * @type {Array} + */ + preset: [ + // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] + [RotateRecognizer, { enable: false }], + [PinchRecognizer, { enable: false }, ['rotate']], + [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }], + [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], + [TapRecognizer], + [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], + [PressRecognizer] + ], - // PARSING + /** + * Some CSS properties can be used to improve the working of Hammer. + * Add them to this method and they will be set when creating a new Manager. + * @namespace + */ + cssProps: { + /** + * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userSelect: 'none', - addRegexToken('D', match1to2); - addRegexToken('DD', match1to2, match2); - addRegexToken('Do', function (isStrict, locale) { - return isStrict ? locale._ordinalParse : locale._ordinalParseLenient; - }); + /** + * Disable the Windows Phone grippers when pressing an element. + * @type {String} + * @default 'none' + */ + touchSelect: 'none', - addParseToken(['D', 'DD'], DATE); - addParseToken('Do', function (input, array) { - array[DATE] = toInt(input.match(match1to2)[0], 10); - }); + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @type {String} + * @default 'none' + */ + touchCallout: 'none', - // MOMENTS + /** + * Specifies whether zooming is enabled. Used by IE10> + * @type {String} + * @default 'none' + */ + contentZooming: 'none', - var getSetDayOfMonth = makeGetSet('Date', true); + /** + * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userDrag: 'none', - addFormatToken('d', 0, 'do', 'day'); + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in iOS. This property obeys the alpha value, if specified. + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' + } + }; - addFormatToken('dd', 0, 0, function (format) { - return this.localeData().weekdaysMin(this, format); - }); + var STOP = 1; + var FORCED_STOP = 2; - addFormatToken('ddd', 0, 0, function (format) { - return this.localeData().weekdaysShort(this, format); - }); + /** + * Manager + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ + function Manager(element, options) { + options = options || {}; - addFormatToken('dddd', 0, 0, function (format) { - return this.localeData().weekdays(this, format); - }); + this.options = merge(options, Hammer.defaults); + this.options.inputTarget = this.options.inputTarget || element; - addFormatToken('e', 0, 0, 'weekday'); - addFormatToken('E', 0, 0, 'isoWeekday'); + this.handlers = {}; + this.session = {}; + this.recognizers = []; - // ALIASES + this.element = element; + this.input = createInputInstance(this); + this.touchAction = new TouchAction(this, this.options.touchAction); - addUnitAlias('day', 'd'); - addUnitAlias('weekday', 'e'); - addUnitAlias('isoWeekday', 'E'); + toggleCssProps(this, true); - // PARSING + each(options.recognizers, function(item) { + var recognizer = this.add(new (item[0])(item[1])); + item[2] && recognizer.recognizeWith(item[2]); + item[3] && recognizer.requireFailure(item[3]); + }, this); + } - addRegexToken('d', match1to2); - addRegexToken('e', match1to2); - addRegexToken('E', match1to2); - addRegexToken('dd', matchWord); - addRegexToken('ddd', matchWord); - addRegexToken('dddd', matchWord); + Manager.prototype = { + /** + * set options + * @param {Object} options + * @returns {Manager} + */ + set: function(options) { + extend(this.options, options); - addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config) { - var weekday = config._locale.weekdaysParse(input); - // if we didn't get a weekday name, mark the date as invalid - if (weekday != null) { - week.d = weekday; - } else { - config._pf.invalidWeekday = input; + // Options that need a little more setup + if (options.touchAction) { + this.touchAction.update(); } - }); - - addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { - week[token] = toInt(input); - }); - - // HELPERS - - function parseWeekday(input, locale) { - if (typeof input === 'string') { - if (!isNaN(input)) { - input = parseInt(input, 10); - } - else { - input = locale.weekdaysParse(input); - if (typeof input !== 'number') { - return null; - } - } + if (options.inputTarget) { + // Clean up existing event listeners and reinitialize + this.input.destroy(); + this.input.target = options.inputTarget; + this.input.init(); } - return input; - } + return this; + }, - // LOCALES + /** + * stop recognizing for this session. + * This session will be discarded, when a new [input]start event is fired. + * When forced, the recognizer cycle is stopped immediately. + * @param {Boolean} [force] + */ + stop: function(force) { + this.session.stopped = force ? FORCED_STOP : STOP; + }, - var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); - function localeWeekdays (m) { - return this._weekdays[m.day()]; - } + /** + * run the recognizers! + * called by the inputHandler function on every movement of the pointers (touches) + * it walks through all the recognizers and tries to detect the gesture that is being made + * @param {Object} inputData + */ + recognize: function(inputData) { + var session = this.session; + if (session.stopped) { + return; + } - var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); - function localeWeekdaysShort (m) { - return this._weekdaysShort[m.day()]; - } + // run the touch-action polyfill + this.touchAction.preventDefaults(inputData); - var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); - function localeWeekdaysMin (m) { - return this._weekdaysMin[m.day()]; - } + var recognizer; + var recognizers = this.recognizers; - function localeWeekdaysParse (weekdayName) { - var i, mom, regex; + // this holds the recognizer that is being recognized. + // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED + // if no recognizer is detecting a thing, it is set to `null` + var curRecognizer = session.curRecognizer; - if (!this._weekdaysParse) { - this._weekdaysParse = []; + // reset when the last recognizer is recognized + // or when we're in a new session + if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { + curRecognizer = session.curRecognizer = null; } - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - if (!this._weekdaysParse[i]) { - mom = local__createLocal([2000, 1]).day(i); - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + var i = 0; + while (i < recognizers.length) { + recognizer = recognizers[i]; + + // find out if we are allowed try to recognize the input for this one. + // 1. allow if the session is NOT forced stopped (see the .stop() method) + // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one + // that is being recognized. + // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. + // this can be setup with the `recognizeWith()` method on the recognizer. + if (session.stopped !== FORCED_STOP && ( // 1 + !curRecognizer || recognizer == curRecognizer || // 2 + recognizer.canRecognizeWith(curRecognizer))) { // 3 + recognizer.recognize(inputData); + } else { + recognizer.reset(); } - // test the regex - if (this._weekdaysParse[i].test(weekdayName)) { - return i; + + // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the + // current active recognizer. but only if we don't already have an active recognizer + if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { + curRecognizer = session.curRecognizer = recognizer; } + i++; } - } + }, - // MOMENTS + /** + * get a recognizer by its event name. + * @param {Recognizer|String} recognizer + * @returns {Recognizer|Null} + */ + get: function(recognizer) { + if (recognizer instanceof Recognizer) { + return recognizer; + } - function getSetDayOfWeek (input) { - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; + var recognizers = this.recognizers; + for (var i = 0; i < recognizers.length; i++) { + if (recognizers[i].options.event == recognizer) { + return recognizers[i]; + } } - } + return null; + }, - function getSetLocaleDayOfWeek (input) { - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - } + /** + * add a recognizer to the manager + * existing recognizers with the same event name will be removed + * @param {Recognizer} recognizer + * @returns {Recognizer|Manager} + */ + add: function(recognizer) { + if (invokeArrayArg(recognizer, 'add', this)) { + return this; + } - function getSetISODayOfWeek (input) { - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); - } + // remove existing + var existing = this.get(recognizer.options.event); + if (existing) { + this.remove(existing); + } - addFormatToken('H', ['HH', 2], 0, 'hour'); - addFormatToken('h', ['hh', 2], 0, function () { - return this.hours() % 12 || 12; - }); + this.recognizers.push(recognizer); + recognizer.manager = this; - function meridiem (token, lowercase) { - addFormatToken(token, 0, 0, function () { - return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); - }); - } + this.touchAction.update(); + return recognizer; + }, - meridiem('a', true); - meridiem('A', false); - - // ALIASES + /** + * remove a recognizer by name or instance + * @param {Recognizer|String} recognizer + * @returns {Manager} + */ + remove: function(recognizer) { + if (invokeArrayArg(recognizer, 'remove', this)) { + return this; + } - addUnitAlias('hour', 'h'); + var recognizers = this.recognizers; + recognizer = this.get(recognizer); + recognizers.splice(inArray(recognizers, recognizer), 1); - // PARSING + this.touchAction.update(); + return this; + }, - function matchMeridiem (isStrict, locale) { - return locale._meridiemParse; - } + /** + * bind event + * @param {String} events + * @param {Function} handler + * @returns {EventEmitter} this + */ + on: function(events, handler) { + var handlers = this.handlers; + each(splitStr(events), function(event) { + handlers[event] = handlers[event] || []; + handlers[event].push(handler); + }); + return this; + }, - addRegexToken('a', matchMeridiem); - addRegexToken('A', matchMeridiem); - addRegexToken('H', match1to2); - addRegexToken('h', match1to2); - addRegexToken('HH', match1to2, match2); - addRegexToken('hh', match1to2, match2); + /** + * unbind event, leave emit blank to remove all handlers + * @param {String} events + * @param {Function} [handler] + * @returns {EventEmitter} this + */ + off: function(events, handler) { + var handlers = this.handlers; + each(splitStr(events), function(event) { + if (!handler) { + delete handlers[event]; + } else { + handlers[event].splice(inArray(handlers[event], handler), 1); + } + }); + return this; + }, - addParseToken(['H', 'HH'], HOUR); - addParseToken(['a', 'A'], function (input, array, config) { - config._isPm = config._locale.isPM(input); - config._meridiem = input; - }); - addParseToken(['h', 'hh'], function (input, array, config) { - array[HOUR] = toInt(input); - config._pf.bigHour = true; - }); + /** + * emit event to the listeners + * @param {String} event + * @param {Object} data + */ + emit: function(event, data) { + // we also want to trigger dom events + if (this.options.domEvents) { + triggerDomEvent(event, data); + } - // LOCALES + // no handlers, so skip it all + var handlers = this.handlers[event] && this.handlers[event].slice(); + if (!handlers || !handlers.length) { + return; + } - function localeIsPM (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - } + data.type = event; + data.preventDefault = function() { + data.srcEvent.preventDefault(); + }; - var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; - function localeMeridiem (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; + var i = 0; + while (i < handlers.length) { + handlers[i](data); + i++; } - } - + }, - // MOMENTS + /** + * destroy the manager and unbinds all events + * it doesn't unbind dom events, that is the user own responsibility + */ + destroy: function() { + this.element && toggleCssProps(this, false); - // Setting the hour should keep the time, because the user explicitly - // specified which hour he wants. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - var getSetHour = makeGetSet('Hours', true); + this.handlers = {}; + this.session = {}; + this.input.destroy(); + this.element = null; + } + }; - addFormatToken('m', ['mm', 2], 0, 'minute'); + /** + * add/remove the css properties as defined in manager.options.cssProps + * @param {Manager} manager + * @param {Boolean} add + */ + function toggleCssProps(manager, add) { + var element = manager.element; + each(manager.options.cssProps, function(value, name) { + element.style[prefixed(element.style, name)] = add ? value : ''; + }); + } - // ALIASES + /** + * trigger dom event + * @param {String} event + * @param {Object} data + */ + function triggerDomEvent(event, data) { + var gestureEvent = document.createEvent('Event'); + gestureEvent.initEvent(event, true, true); + gestureEvent.gesture = data; + data.target.dispatchEvent(gestureEvent); + } - addUnitAlias('minute', 'm'); + extend(Hammer, { + INPUT_START: INPUT_START, + INPUT_MOVE: INPUT_MOVE, + INPUT_END: INPUT_END, + INPUT_CANCEL: INPUT_CANCEL, - // PARSING + STATE_POSSIBLE: STATE_POSSIBLE, + STATE_BEGAN: STATE_BEGAN, + STATE_CHANGED: STATE_CHANGED, + STATE_ENDED: STATE_ENDED, + STATE_RECOGNIZED: STATE_RECOGNIZED, + STATE_CANCELLED: STATE_CANCELLED, + STATE_FAILED: STATE_FAILED, - addRegexToken('m', match1to2); - addRegexToken('mm', match1to2, match2); - addParseToken(['m', 'mm'], MINUTE); + DIRECTION_NONE: DIRECTION_NONE, + DIRECTION_LEFT: DIRECTION_LEFT, + DIRECTION_RIGHT: DIRECTION_RIGHT, + DIRECTION_UP: DIRECTION_UP, + DIRECTION_DOWN: DIRECTION_DOWN, + DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, + DIRECTION_VERTICAL: DIRECTION_VERTICAL, + DIRECTION_ALL: DIRECTION_ALL, - // MOMENTS + Manager: Manager, + Input: Input, + TouchAction: TouchAction, - var getSetMinute = makeGetSet('Minutes', false); + TouchInput: TouchInput, + MouseInput: MouseInput, + PointerEventInput: PointerEventInput, + TouchMouseInput: TouchMouseInput, + SingleTouchInput: SingleTouchInput, - addFormatToken('s', ['ss', 2], 0, 'second'); + Recognizer: Recognizer, + AttrRecognizer: AttrRecognizer, + Tap: TapRecognizer, + Pan: PanRecognizer, + Swipe: SwipeRecognizer, + Pinch: PinchRecognizer, + Rotate: RotateRecognizer, + Press: PressRecognizer, - // ALIASES + on: addEventListeners, + off: removeEventListeners, + each: each, + merge: merge, + extend: extend, + inherit: inherit, + bindFn: bindFn, + prefixed: prefixed + }); - addUnitAlias('second', 's'); + if ("function" == TYPE_FUNCTION && __webpack_require__(107)) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return Hammer; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof module != 'undefined' && module.exports) { + module.exports = Hammer; + } else { + window[exportName] = Hammer; + } - // PARSING + })(window, document, 'Hammer'); - addRegexToken('s', match1to2); - addRegexToken('ss', match1to2, match2); - addParseToken(['s', 'ss'], SECOND); - // MOMENTS +/***/ }, +/* 72 */ +/***/ function(module, exports, __webpack_require__) { - var getSetSecond = makeGetSet('Seconds', false); + /* WEBPACK VAR INJECTION */(function(module) {//! moment.js + //! version : 2.10.2 + //! authors : Tim Wood, Iskren Chernev, Moment.js contributors + //! license : MIT + //! momentjs.com - addFormatToken('S', 0, 0, function () { - return ~~(this.millisecond() / 100); - }); + (function (global, factory) { + true ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + global.moment = factory() + }(this, function () { 'use strict'; - addFormatToken(0, ['SS', 2], 0, function () { - return ~~(this.millisecond() / 10); - }); + var hookCallback; - function millisecond__milliseconds (token) { - addFormatToken(0, [token, 3], 0, 'millisecond'); + function utils_hooks__hooks () { + return hookCallback.apply(null, arguments); } - millisecond__milliseconds('SSS'); - millisecond__milliseconds('SSSS'); + // This is done to register the method called with moment() + // without creating circular dependencies. + function setHookCallback (callback) { + hookCallback = callback; + } - // ALIASES + function defaultParsingFlags() { + // We need to deep clone this object. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso : false + }; + } - addUnitAlias('millisecond', 'ms'); + function isArray(input) { + return Object.prototype.toString.call(input) === '[object Array]'; + } - // PARSING + function isDate(input) { + return Object.prototype.toString.call(input) === '[object Date]' || input instanceof Date; + } - addRegexToken('S', match1to3, match1); - addRegexToken('SS', match1to3, match2); - addRegexToken('SSS', match1to3, match3); - addRegexToken('SSSS', matchUnsigned); - addParseToken(['S', 'SS', 'SSS', 'SSSS'], function (input, array) { - array[MILLISECOND] = toInt(('0.' + input) * 1000); - }); + function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; + } - // MOMENTS + function hasOwnProp(a, b) { + return Object.prototype.hasOwnProperty.call(a, b); + } - var getSetMillisecond = makeGetSet('Milliseconds', false); + function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } + } - addFormatToken('z', 0, 0, 'zoneAbbr'); - addFormatToken('zz', 0, 0, 'zoneName'); + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } - // MOMENTS + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } - function getZoneAbbr () { - return this._isUTC ? 'UTC' : ''; + return a; } - function getZoneName () { - return this._isUTC ? 'Coordinated Universal Time' : ''; + function create_utc__createUTC (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, true).utc(); } - var momentPrototype__proto = Moment.prototype; - - momentPrototype__proto.add = add_subtract__add; - momentPrototype__proto.calendar = moment_calendar__calendar; - momentPrototype__proto.clone = clone; - momentPrototype__proto.diff = diff; - momentPrototype__proto.endOf = endOf; - momentPrototype__proto.format = format; - momentPrototype__proto.from = from; - momentPrototype__proto.fromNow = fromNow; - momentPrototype__proto.get = getSet; - momentPrototype__proto.invalidAt = invalidAt; - momentPrototype__proto.isAfter = isAfter; - momentPrototype__proto.isBefore = isBefore; - momentPrototype__proto.isBetween = isBetween; - momentPrototype__proto.isSame = isSame; - momentPrototype__proto.isValid = moment_valid__isValid; - momentPrototype__proto.lang = lang; - momentPrototype__proto.locale = locale; - momentPrototype__proto.localeData = localeData; - momentPrototype__proto.max = prototypeMax; - momentPrototype__proto.min = prototypeMin; - momentPrototype__proto.parsingFlags = parsingFlags; - momentPrototype__proto.set = getSet; - momentPrototype__proto.startOf = startOf; - momentPrototype__proto.subtract = add_subtract__subtract; - momentPrototype__proto.toArray = toArray; - momentPrototype__proto.toDate = toDate; - momentPrototype__proto.toISOString = moment_format__toISOString; - momentPrototype__proto.toJSON = moment_format__toISOString; - momentPrototype__proto.toString = toString; - momentPrototype__proto.unix = unix; - momentPrototype__proto.valueOf = to_type__valueOf; - - // Year - momentPrototype__proto.year = getSetYear; - momentPrototype__proto.isLeapYear = getIsLeapYear; - - // Week Year - momentPrototype__proto.weekYear = getSetWeekYear; - momentPrototype__proto.isoWeekYear = getSetISOWeekYear; + function valid__isValid(m) { + if (m._isValid == null) { + m._isValid = !isNaN(m._d.getTime()) && + m._pf.overflow < 0 && + !m._pf.empty && + !m._pf.invalidMonth && + !m._pf.nullInput && + !m._pf.invalidFormat && + !m._pf.userInvalidated; - // Quarter - momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter; + if (m._strict) { + m._isValid = m._isValid && + m._pf.charsLeftOver === 0 && + m._pf.unusedTokens.length === 0 && + m._pf.bigHour === undefined; + } + } + return m._isValid; + } - // Month - momentPrototype__proto.month = getSetMonth; - momentPrototype__proto.daysInMonth = getDaysInMonth; + function valid__createInvalid (flags) { + var m = create_utc__createUTC(NaN); + if (flags != null) { + extend(m._pf, flags); + } + else { + m._pf.userInvalidated = true; + } - // Week - momentPrototype__proto.week = momentPrototype__proto.weeks = getSetWeek; - momentPrototype__proto.isoWeek = momentPrototype__proto.isoWeeks = getSetISOWeek; - momentPrototype__proto.weeksInYear = getWeeksInYear; - momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear; + return m; + } - // Day - momentPrototype__proto.date = getSetDayOfMonth; - momentPrototype__proto.day = momentPrototype__proto.days = getSetDayOfWeek; - momentPrototype__proto.weekday = getSetLocaleDayOfWeek; - momentPrototype__proto.isoWeekday = getSetISODayOfWeek; - momentPrototype__proto.dayOfYear = getSetDayOfYear; + var momentProperties = utils_hooks__hooks.momentProperties = []; - // Hour - momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour; + function copyConfig(to, from) { + var i, prop, val; - // Minute - momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute; + if (typeof from._isAMomentObject !== 'undefined') { + to._isAMomentObject = from._isAMomentObject; + } + if (typeof from._i !== 'undefined') { + to._i = from._i; + } + if (typeof from._f !== 'undefined') { + to._f = from._f; + } + if (typeof from._l !== 'undefined') { + to._l = from._l; + } + if (typeof from._strict !== 'undefined') { + to._strict = from._strict; + } + if (typeof from._tzm !== 'undefined') { + to._tzm = from._tzm; + } + if (typeof from._isUTC !== 'undefined') { + to._isUTC = from._isUTC; + } + if (typeof from._offset !== 'undefined') { + to._offset = from._offset; + } + if (typeof from._pf !== 'undefined') { + to._pf = from._pf; + } + if (typeof from._locale !== 'undefined') { + to._locale = from._locale; + } - // Second - momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond; + if (momentProperties.length > 0) { + for (i in momentProperties) { + prop = momentProperties[i]; + val = from[prop]; + if (typeof val !== 'undefined') { + to[prop] = val; + } + } + } - // Millisecond - momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond; + return to; + } - // Offset - momentPrototype__proto.utcOffset = getSetOffset; - momentPrototype__proto.utc = setOffsetToUTC; - momentPrototype__proto.local = setOffsetToLocal; - momentPrototype__proto.parseZone = setOffsetToParsedOffset; - momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset; - momentPrototype__proto.isDST = isDaylightSavingTime; - momentPrototype__proto.isDSTShifted = isDaylightSavingTimeShifted; - momentPrototype__proto.isLocal = isLocal; - momentPrototype__proto.isUtcOffset = isUtcOffset; - momentPrototype__proto.isUtc = isUtc; - momentPrototype__proto.isUTC = isUtc; + var updateInProgress = false; - // Timezone - momentPrototype__proto.zoneAbbr = getZoneAbbr; - momentPrototype__proto.zoneName = getZoneName; + // Moment prototype object + function Moment(config) { + copyConfig(this, config); + this._d = new Date(+config._d); + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + utils_hooks__hooks.updateOffset(this); + updateInProgress = false; + } + } - // Deprecations - momentPrototype__proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); - momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); - momentPrototype__proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); - momentPrototype__proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779', getSetZone); + function isMoment (obj) { + return obj instanceof Moment || (obj != null && hasOwnProp(obj, '_isAMomentObject')); + } - var momentPrototype = momentPrototype__proto; + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; - function moment__createUnix (input) { - return local__createLocal(input * 1000); - } + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + if (coercedNumber >= 0) { + value = Math.floor(coercedNumber); + } else { + value = Math.ceil(coercedNumber); + } + } - function moment__createInZone () { - return local__createLocal.apply(null, arguments).parseZone(); + return value; } - var defaultCalendar = { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }; - - function locale_calendar__calendar (key, mom, now) { - var output = this._calendar[key]; - return typeof output === 'function' ? output.call(mom, now) : output; - } - - var defaultLongDateFormat = { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY LT', - LLLL : 'dddd, MMMM D, YYYY LT' - }; - - function longDateFormat (key) { - var output = this._longDateFormat[key]; - if (!output && this._longDateFormat[key.toUpperCase()]) { - output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - this._longDateFormat[key] = output; - } - return output; - } - - var defaultInvalidDate = 'Invalid date'; - - function invalidDate () { - return this._invalidDate; - } - - var defaultOrdinal = '%d'; - var defaultOrdinalParse = /\d{1,2}/; - - function ordinal (number) { - return this._ordinal.replace('%d', number); - } - - function preParsePostFormat (string) { - return string; - } - - var defaultRelativeTime = { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }; - - function relative__relativeTime (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (typeof output === 'function') ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - } - - function pastFuture (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); - } - - function locale_set__set (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (typeof prop === 'function') { - this[i] = prop; - } else { - this['_' + i] = prop; + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; } } - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _ordinalParseLenient. - this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); - } - - var prototype__proto = Locale.prototype; - - prototype__proto._calendar = defaultCalendar; - prototype__proto.calendar = locale_calendar__calendar; - prototype__proto._longDateFormat = defaultLongDateFormat; - prototype__proto.longDateFormat = longDateFormat; - prototype__proto._invalidDate = defaultInvalidDate; - prototype__proto.invalidDate = invalidDate; - prototype__proto._ordinal = defaultOrdinal; - prototype__proto.ordinal = ordinal; - prototype__proto._ordinalParse = defaultOrdinalParse; - prototype__proto.preparse = preParsePostFormat; - prototype__proto.postformat = preParsePostFormat; - prototype__proto._relativeTime = defaultRelativeTime; - prototype__proto.relativeTime = relative__relativeTime; - prototype__proto.pastFuture = pastFuture; - prototype__proto.set = locale_set__set; - - // Month - prototype__proto.months = localeMonths; - prototype__proto._months = defaultLocaleMonths; - prototype__proto.monthsShort = localeMonthsShort; - prototype__proto._monthsShort = defaultLocaleMonthsShort; - prototype__proto.monthsParse = localeMonthsParse; - - // Week - prototype__proto.week = localeWeek; - prototype__proto._week = defaultLocaleWeek; - prototype__proto.firstDayOfYear = localeFirstDayOfYear; - prototype__proto.firstDayOfWeek = localeFirstDayOfWeek; - - // Day of Week - prototype__proto.weekdays = localeWeekdays; - prototype__proto._weekdays = defaultLocaleWeekdays; - prototype__proto.weekdaysMin = localeWeekdaysMin; - prototype__proto._weekdaysMin = defaultLocaleWeekdaysMin; - prototype__proto.weekdaysShort = localeWeekdaysShort; - prototype__proto._weekdaysShort = defaultLocaleWeekdaysShort; - prototype__proto.weekdaysParse = localeWeekdaysParse; - - // Hours - prototype__proto.isPM = localeIsPM; - prototype__proto._meridiemParse = defaultLocaleMeridiemParse; - prototype__proto.meridiem = localeMeridiem; - - function lists__get (format, index, field, setter) { - var locale = locale_locales__getLocale(); - var utc = create_utc__createUTC().set(setter, index); - return locale[field](utc, format); - } - - function list (format, index, field, count, setter) { - if (typeof format === 'number') { - index = format; - format = undefined; - } - - format = format || ''; - - if (index != null) { - return lists__get(format, index, field, setter); - } - - var i; - var out = []; - for (i = 0; i < count; i++) { - out[i] = lists__get(format, i, field, setter); - } - return out; - } - - function lists__listMonths (format, index) { - return list(format, index, 'months', 12, 'month'); - } - - function lists__listMonthsShort (format, index) { - return list(format, index, 'monthsShort', 12, 'month'); - } - - function lists__listWeekdays (format, index) { - return list(format, index, 'weekdays', 7, 'day'); - } - - function lists__listWeekdaysShort (format, index) { - return list(format, index, 'weekdaysShort', 7, 'day'); - } - - function lists__listWeekdaysMin (format, index) { - return list(format, index, 'weekdaysMin', 7, 'day'); - } - - locale_locales__getSetGlobalLocale('en', { - ordinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); - - // Side effect imports - utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale); - utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale); - - var mathAbs = Math.abs; - - function duration_abs__abs () { - var data = this._data; - - this._milliseconds = mathAbs(this._milliseconds); - this._days = mathAbs(this._days); - this._months = mathAbs(this._months); - - data.milliseconds = mathAbs(data.milliseconds); - data.seconds = mathAbs(data.seconds); - data.minutes = mathAbs(data.minutes); - data.hours = mathAbs(data.hours); - data.months = mathAbs(data.months); - data.years = mathAbs(data.years); - - return this; - } - - function duration_add_subtract__addSubtract (duration, input, value, direction) { - var other = create__createDuration(input, value); - - duration._milliseconds += direction * other._milliseconds; - duration._days += direction * other._days; - duration._months += direction * other._months; - - return duration._bubble(); - } - - // supports only 2.0-style add(1, 's') or add(duration) - function duration_add_subtract__add (input, value) { - return duration_add_subtract__addSubtract(this, input, value, 1); - } - - // supports only 2.0-style subtract(1, 's') or subtract(duration) - function duration_add_subtract__subtract (input, value) { - return duration_add_subtract__addSubtract(this, input, value, -1); + return diffs + lengthDiff; } - function bubble () { - var milliseconds = this._milliseconds; - var days = this._days; - var months = this._months; - var data = this._data; - var seconds, minutes, hours, years = 0; - - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; - - seconds = absFloor(milliseconds / 1000); - data.seconds = seconds % 60; - - minutes = absFloor(seconds / 60); - data.minutes = minutes % 60; - - hours = absFloor(minutes / 60); - data.hours = hours % 24; - - days += absFloor(hours / 24); - - // Accurately convert days to years, assume start from year 0. - years = absFloor(daysToYears(days)); - days -= absFloor(yearsToDays(years)); - - // 30 days to a month - // TODO (iskren): Use anchor date (like 1st Jan) to compute this. - months += absFloor(days / 30); - days %= 30; - - // 12 months -> 1 year - years += absFloor(months / 12); - months %= 12; - - data.days = days; - data.months = months; - data.years = years; - - return this; + function Locale() { } - function daysToYears (days) { - // 400 years have 146097 days (taking into account leap year rules) - return days * 400 / 146097; - } + var locales = {}; + var globalLocale; - function yearsToDays (years) { - // years * 365 + absFloor(years / 4) - - // absFloor(years / 100) + absFloor(years / 400); - return years * 146097 / 400; + function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; } - function as (units) { - var days; - var months; - var milliseconds = this._milliseconds; - - units = normalizeUnits(units); + // pick the locale from the array + // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + function chooseLocale(names) { + var i = 0, j, next, locale, split; - if (units === 'month' || units === 'year') { - days = this._days + milliseconds / 864e5; - months = this._months + daysToYears(days) * 12; - return units === 'month' ? months : months / 12; - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(yearsToDays(this._months / 12)); - switch (units) { - case 'week' : return days / 7 + milliseconds / 6048e5; - case 'day' : return days + milliseconds / 864e5; - case 'hour' : return days * 24 + milliseconds / 36e5; - case 'minute' : return days * 24 * 60 + milliseconds / 6e4; - case 'second' : return days * 24 * 60 * 60 + milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + milliseconds; - default: throw new Error('Unknown unit ' + units); + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; } + i++; } + return null; } - // TODO: Use this.as('ms')? - function duration_as__valueOf () { - return ( - this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6 - ); - } - - function makeAs (alias) { - return function () { - return this.as(alias); - }; - } - - var asMilliseconds = makeAs('ms'); - var asSeconds = makeAs('s'); - var asMinutes = makeAs('m'); - var asHours = makeAs('h'); - var asDays = makeAs('d'); - var asWeeks = makeAs('w'); - var asMonths = makeAs('M'); - var asYears = makeAs('y'); - - function duration_get__get (units) { - units = normalizeUnits(units); - return this[units + 's'](); - } - - function makeGetter(name) { - return function () { - return this._data[name]; - }; - } - - var duration_get__milliseconds = makeGetter('milliseconds'); - var seconds = makeGetter('seconds'); - var minutes = makeGetter('minutes'); - var hours = makeGetter('hours'); - var days = makeGetter('days'); - var months = makeGetter('months'); - var years = makeGetter('years'); - - function weeks () { - return absFloor(this.days() / 7); - } - - var round = Math.round; - var thresholds = { - s: 45, // seconds to minute - m: 45, // minutes to hour - h: 22, // hours to day - d: 26, // days to month - M: 11 // months to year - }; - - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } - - function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) { - var duration = create__createDuration(posNegDuration).abs(); - var seconds = round(duration.as('s')); - var minutes = round(duration.as('m')); - var hours = round(duration.as('h')); - var days = round(duration.as('d')); - var months = round(duration.as('M')); - var years = round(duration.as('y')); - - var a = seconds < thresholds.s && ['s', seconds] || - minutes === 1 && ['m'] || - minutes < thresholds.m && ['mm', minutes] || - hours === 1 && ['h'] || - hours < thresholds.h && ['hh', hours] || - days === 1 && ['d'] || - days < thresholds.d && ['dd', days] || - months === 1 && ['M'] || - months < thresholds.M && ['MM', months] || - years === 1 && ['y'] || ['yy', years]; - - a[2] = withoutSuffix; - a[3] = +posNegDuration > 0; - a[4] = locale; - return substituteTimeAgo.apply(null, a); - } - - // This function allows you to set a threshold for relative time strings - function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) { - if (thresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return thresholds[threshold]; - } - thresholds[threshold] = limit; - return true; - } - - function humanize (withSuffix) { - var locale = this.localeData(); - var output = duration_humanize__relativeTime(this, !withSuffix, locale); - - if (withSuffix) { - output = locale.pastFuture(+this, output); + function loadLocale(name) { + var oldLocale = null; + // TODO: Find a better way to register and load all the locales in Node + if (!locales[name] && typeof module !== 'undefined' && + module && module.exports) { + try { + oldLocale = globalLocale._abbr; + !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); + // because defineLocale currently also sets the global locale, we + // want to undo that for lazy loaded locales + locale_locales__getSetGlobalLocale(oldLocale); + } catch (e) { } } - - return locale.postformat(output); + return locales[name]; } - var iso_string__abs = Math.abs; - - function iso_string__toISOString() { - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var Y = iso_string__abs(this.years()); - var M = iso_string__abs(this.months()); - var D = iso_string__abs(this.days()); - var h = iso_string__abs(this.hours()); - var m = iso_string__abs(this.minutes()); - var s = iso_string__abs(this.seconds() + this.milliseconds() / 1000); - var total = this.asSeconds(); + // This function will load locale and then set the global locale. If + // no arguments are passed in, it will simply return the current global + // locale key. + function locale_locales__getSetGlobalLocale (key, values) { + var data; + if (key) { + if (typeof values === 'undefined') { + data = locale_locales__getLocale(key); + } + else { + data = defineLocale(key, values); + } - if (!total) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; + if (data) { + // moment.duration._locale = moment._locale = data; + globalLocale = data; + } } - return (total < 0 ? '-' : '') + - 'P' + - (Y ? Y + 'Y' : '') + - (M ? M + 'M' : '') + - (D ? D + 'D' : '') + - ((h || m || s) ? 'T' : '') + - (h ? h + 'H' : '') + - (m ? m + 'M' : '') + - (s ? s + 'S' : ''); - } - - var duration_prototype__proto = Duration.prototype; - - duration_prototype__proto.abs = duration_abs__abs; - duration_prototype__proto.add = duration_add_subtract__add; - duration_prototype__proto.subtract = duration_add_subtract__subtract; - duration_prototype__proto.as = as; - duration_prototype__proto.asMilliseconds = asMilliseconds; - duration_prototype__proto.asSeconds = asSeconds; - duration_prototype__proto.asMinutes = asMinutes; - duration_prototype__proto.asHours = asHours; - duration_prototype__proto.asDays = asDays; - duration_prototype__proto.asWeeks = asWeeks; - duration_prototype__proto.asMonths = asMonths; - duration_prototype__proto.asYears = asYears; - duration_prototype__proto.valueOf = duration_as__valueOf; - duration_prototype__proto._bubble = bubble; - duration_prototype__proto.get = duration_get__get; - duration_prototype__proto.milliseconds = duration_get__milliseconds; - duration_prototype__proto.seconds = seconds; - duration_prototype__proto.minutes = minutes; - duration_prototype__proto.hours = hours; - duration_prototype__proto.days = days; - duration_prototype__proto.weeks = weeks; - duration_prototype__proto.months = months; - duration_prototype__proto.years = years; - duration_prototype__proto.humanize = humanize; - duration_prototype__proto.toISOString = iso_string__toISOString; - duration_prototype__proto.toString = iso_string__toISOString; - duration_prototype__proto.toJSON = iso_string__toISOString; - duration_prototype__proto.locale = locale; - duration_prototype__proto.localeData = localeData; - - // Deprecations - duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString); - duration_prototype__proto.lang = lang; - - // Side effect imports - - addFormatToken('X', 0, 0, 'unix'); - addFormatToken('x', 0, 0, 'valueOf'); - - // PARSING - - addRegexToken('x', matchSigned); - addRegexToken('X', matchTimestamp); - addParseToken('X', function (input, array, config) { - config._d = new Date(parseFloat(input, 10) * 1000); - }); - addParseToken('x', function (input, array, config) { - config._d = new Date(toInt(input)); - }); - - // Side effect imports - - - utils_hooks__hooks.version = '2.10.2'; - - setHookCallback(local__createLocal); - - utils_hooks__hooks.fn = momentPrototype; - utils_hooks__hooks.min = min; - utils_hooks__hooks.max = max; - utils_hooks__hooks.utc = create_utc__createUTC; - utils_hooks__hooks.unix = moment__createUnix; - utils_hooks__hooks.months = lists__listMonths; - utils_hooks__hooks.isDate = isDate; - utils_hooks__hooks.locale = locale_locales__getSetGlobalLocale; - utils_hooks__hooks.invalid = valid__createInvalid; - utils_hooks__hooks.duration = create__createDuration; - utils_hooks__hooks.isMoment = isMoment; - utils_hooks__hooks.weekdays = lists__listWeekdays; - utils_hooks__hooks.parseZone = moment__createInZone; - utils_hooks__hooks.localeData = locale_locales__getLocale; - utils_hooks__hooks.isDuration = isDuration; - utils_hooks__hooks.monthsShort = lists__listMonthsShort; - utils_hooks__hooks.weekdaysMin = lists__listWeekdaysMin; - utils_hooks__hooks.defineLocale = defineLocale; - utils_hooks__hooks.weekdaysShort = lists__listWeekdaysShort; - utils_hooks__hooks.normalizeUnits = normalizeUnits; - utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold; - - var _moment = utils_hooks__hooks; - - return _moment; - - })); - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(90)(module))) - -/***/ }, -/* 71 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;'use strict'; - - (function (factory) { - if (true) { - // AMD. Register as an anonymous module. - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports === 'object') { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(); - } else { - // Browser globals (root is window) - window.propagating = factory(); - } - }(function () { - var _firstTarget = null; // singleton, will contain the target element where the touch event started - var _processing = false; // singleton, true when a touch event is being handled - - /** - * Extend an Hammer.js instance with event propagation. - * - * Features: - * - Events emitted by hammer will propagate in order from child to parent - * elements. - * - Events are extended with a function `event.stopPropagation()` to stop - * propagation to parent elements. - * - An option `preventDefault` to stop all default browser behavior. - * - * Usage: - * var hammer = propagatingHammer(new Hammer(element)); - * var hammer = propagatingHammer(new Hammer(element), {preventDefault: true}); - * - * @param {Hammer.Manager} hammer An hammer instance. - * @param {Object} [options] Available options: - * - `preventDefault: true | 'mouse' | 'touch' | 'pen'`. - * Enforce preventing the default browser behavior. - * Cannot be set to `false`. - * @return {Hammer.Manager} Returns the same hammer instance with extended - * functionality - */ - return function propagating(hammer, options) { - if (options && options.preventDefault === false) { - throw new Error('Only supports preventDefault == true'); - } - var _options = options || { - preventDefault: false - }; - - if (hammer.Manager) { - // This looks like the Hammer constructor. - // Overload the constructors with our own. - var Hammer = hammer; - - var PropagatingHammer = function(element, options) { - return propagating(new Hammer(element, options), _options); - }; - Hammer.extend(PropagatingHammer, Hammer); - PropagatingHammer.Manager = function (element, options) { - return propagating(new Hammer.Manager(element, options), _options); - }; - - return PropagatingHammer; + return globalLocale._abbr; } - // attach to DOM element - var element = hammer.element; - element.hammer = hammer; - - // move the original functions that we will wrap - hammer._on = hammer.on; - hammer._off = hammer.off; - hammer._emit = hammer.emit; - hammer._destroy = hammer.destroy; - - /** @type {Object.>} */ - hammer._handlers = {}; - - // register an event to catch the start of a gesture and store the - // target in a singleton - hammer._on('hammer.input', function (event) { - if (_options.preventDefault === true || (_options.preventDefault === event.pointerType)) { - event.preventDefault(); - } - if (event.isFirst) { - _firstTarget = event.target; - _processing = true; - } - if (event.isFinal) { - _processing = false; - } - }); - - /** - * Register a handler for one or multiple events - * @param {String} events A space separated string with events - * @param {function} handler A callback function, called as handler(event) - * @returns {Hammer.Manager} Returns the hammer instance - */ - hammer.on = function (events, handler) { - // register the handler - split(events).forEach(function (event) { - var _handlers = hammer._handlers[event]; - if (!_handlers) { - hammer._handlers[event] = _handlers = []; - - // register the static, propagated handler - hammer._on(event, propagatedHandler); - } - _handlers.push(handler); - }); - - return hammer; - }; - - /** - * Unregister a handler for one or multiple events - * @param {String} events A space separated string with events - * @param {function} [handler] Optional. The registered handler. If not - * provided, all handlers for given events - * are removed. - * @returns {Hammer.Manager} Returns the hammer instance - */ - hammer.off = function (events, handler) { - // unregister the handler - split(events).forEach(function (event) { - var _handlers = hammer._handlers[event]; - if (_handlers) { - _handlers = handler ? _handlers.filter(function (h) { - return h !== handler; - }) : []; - - if (_handlers.length > 0) { - hammer._handlers[event] = _handlers; - } - else { - // remove static, propagated handler - hammer._off(event, propagatedHandler); - delete hammer._handlers[event]; - } - } - }); - - return hammer; - }; - - /** - * Emit to the event listeners - * @param {string} eventType - * @param {Event} event - */ - hammer.emit = function(eventType, event) { - if (!_processing) { - _firstTarget = event.target; - } - hammer._emit(eventType, event); - }; - - hammer.destroy = function () { - // Detach from DOM element - var element = hammer.element; - delete element.hammer; - - // clear all handlers - hammer._handlers = {}; - - // call original hammer destroy - hammer._destroy(); - }; - - // split a string with space separated words - function split(events) { - return events.match(/[^ ]+/g); - } + function defineLocale (name, values) { + if (values !== null) { + values.abbr = name; + if (!locales[name]) { + locales[name] = new Locale(); + } + locales[name].set(values); - /** - * A static event handler, applying event propagation. - * @param {Object} event - */ - function propagatedHandler(event) { - // let only a single hammer instance handle this event - if (event.type !== 'hammer.input') { - // it is possible that the same srcEvent is used with multiple hammer events, - // we keep track on which events are handled in an object _handled - if (!event.srcEvent._handled) { - event.srcEvent._handled = {}; - } + // backwards compat for now: also set the locale + locale_locales__getSetGlobalLocale(name); - if (event.srcEvent._handled[event.type]) { - return; - } - else { - event.srcEvent._handled[event.type] = true; + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; } - } - - // attach a stopPropagation function to the event - var stopped = false; - event.stopPropagation = function () { - stopped = true; - }; + } - // attach firstTarget property to the event - event.firstTarget = _firstTarget; + // returns locale data + function locale_locales__getLocale (key) { + var locale; - // propagate over all elements (until stopped) - var elem = _firstTarget; - while (elem && !stopped) { - var _handlers = elem.hammer && elem.hammer._handlers[event.type]; - if (_handlers) { - for (var i = 0; i < _handlers.length && !stopped; i++) { - _handlers[i](event); - } + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; } - elem = elem.parentNode; - } - } + if (!key) { + return globalLocale; + } - return hammer; - }; - })); + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } + return chooseLocale(key); + } -/***/ }, -/* 72 */ -/***/ function(module, exports, __webpack_require__) { + var aliases = {}; - var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.4 - 2014-09-28 - * http://hammerjs.github.io/ - * - * Copyright (c) 2014 Jorik Tangelder; - * Licensed under the MIT license */ - (function(window, document, exportName, undefined) { - 'use strict'; + function addUnitAlias (unit, shorthand) { + var lowerCase = unit.toLowerCase(); + aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; + } - var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o']; - var TEST_ELEMENT = document.createElement('div'); + function normalizeUnits(units) { + return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; + } - var TYPE_FUNCTION = 'function'; + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; - var round = Math.round; - var abs = Math.abs; - var now = Date.now; + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } - /** - * set a timeout with a given scope - * @param {Function} fn - * @param {Number} timeout - * @param {Object} context - * @returns {number} - */ - function setTimeoutContext(fn, timeout, context) { - return setTimeout(bindFn(fn, context), timeout); - } + return normalizedInput; + } - /** - * if the argument is an array, we want to execute the fn on each entry - * if it aint an array we don't want to do a thing. - * this is used by all the methods that accept a single and array argument. - * @param {*|Array} arg - * @param {String} fn - * @param {Object} [context] - * @returns {Boolean} - */ - function invokeArrayArg(arg, fn, context) { - if (Array.isArray(arg)) { - each(arg, context[fn], context); - return true; + function makeGetSet (unit, keepTime) { + return function (value) { + if (value != null) { + get_set__set(this, unit, value); + utils_hooks__hooks.updateOffset(this, keepTime); + return this; + } else { + return get_set__get(this, unit); + } + }; } - return false; - } - /** - * walk objects and arrays - * @param {Object} obj - * @param {Function} iterator - * @param {Object} context - */ - function each(obj, iterator, context) { - var i; + function get_set__get (mom, unit) { + return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); + } - if (!obj) { - return; + function get_set__set (mom, unit, value) { + return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); } - if (obj.forEach) { - obj.forEach(iterator, context); - } else if (obj.length !== undefined) { - i = 0; - while (i < obj.length) { - iterator.call(context, obj[i], i, obj); - i++; - } - } else { - for (i in obj) { - obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); + // MOMENTS + + function getSet (units, value) { + var unit; + if (typeof units === 'object') { + for (unit in units) { + this.set(unit, units[unit]); + } + } else { + units = normalizeUnits(units); + if (typeof this[units] === 'function') { + return this[units](value); + } } + return this; } - } - /** - * extend object. - * means that properties in dest will be overwritten by the ones in src. - * @param {Object} dest - * @param {Object} src - * @param {Boolean} [merge] - * @returns {Object} dest - */ - function extend(dest, src, merge) { - var keys = Object.keys(src); - var i = 0; - while (i < keys.length) { - if (!merge || (merge && dest[keys[i]] === undefined)) { - dest[keys[i]] = src[keys[i]]; + function zeroFill(number, targetLength, forceSign) { + var output = '' + Math.abs(number), + sign = number >= 0; + + while (output.length < targetLength) { + output = '0' + output; } - i++; + return (sign ? (forceSign ? '+' : '') : '-') + output; } - return dest; - } - /** - * merge the values from src in the dest. - * means that properties that exist in dest will not be overwritten by src - * @param {Object} dest - * @param {Object} src - * @returns {Object} dest - */ - function merge(dest, src) { - return extend(dest, src, true); - } + var formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g; - /** - * simple class inheritance - * @param {Function} child - * @param {Function} base - * @param {Object} [properties] - */ - function inherit(child, base, properties) { - var baseP = base.prototype, - childP; + var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; - childP = child.prototype = Object.create(baseP); - childP.constructor = child; - childP._super = baseP; + var formatFunctions = {}; - if (properties) { - extend(childP, properties); - } - } + var formatTokenFunctions = {}; - /** - * simple function bind - * @param {Function} fn - * @param {Object} context - * @returns {Function} - */ - function bindFn(fn, context) { - return function boundFn() { - return fn.apply(context, arguments); - }; - } + // token: 'M' + // padded: ['MM', 2] + // ordinal: 'Mo' + // callback: function () { this.month() + 1 } + function addFormatToken (token, padded, ordinal, callback) { + var func = callback; + if (typeof callback === 'string') { + func = function () { + return this[callback](); + }; + } + if (token) { + formatTokenFunctions[token] = func; + } + if (padded) { + formatTokenFunctions[padded[0]] = function () { + return zeroFill(func.apply(this, arguments), padded[1], padded[2]); + }; + } + if (ordinal) { + formatTokenFunctions[ordinal] = function () { + return this.localeData().ordinal(func.apply(this, arguments), token); + }; + } + } - /** - * let a boolean value also be a function that must return a boolean - * this first item in args will be used as the context - * @param {Boolean|Function} val - * @param {Array} [args] - * @returns {Boolean} - */ - function boolOrFn(val, args) { - if (typeof val == TYPE_FUNCTION) { - return val.apply(args ? args[0] || undefined : undefined, args); + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); } - return val; - } - /** - * use the val2 when val1 is undefined - * @param {*} val1 - * @param {*} val2 - * @returns {*} - */ - function ifUndefined(val1, val2) { - return (val1 === undefined) ? val2 : val1; - } + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; - /** - * addEventListener with multiple events at once - * @param {EventTarget} target - * @param {String} types - * @param {Function} handler - */ - function addEventListeners(target, types, handler) { - each(splitStr(types), function(type) { - target.addEventListener(type, handler, false); - }); - } + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } - /** - * removeEventListener with multiple events at once - * @param {EventTarget} target - * @param {String} types - * @param {Function} handler - */ - function removeEventListeners(target, types, handler) { - each(splitStr(types), function(type) { - target.removeEventListener(type, handler, false); - }); - } + return function (mom) { + var output = ''; + for (i = 0; i < length; i++) { + output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; + } + return output; + }; + } - /** - * find if a node is in the given parent - * @method hasParent - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @return {Boolean} found - */ - function hasParent(node, parent) { - while (node) { - if (node == parent) { - return true; + // format date using native date object + function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); } - node = node.parentNode; + + format = expandFormat(format, m.localeData()); + + if (!formatFunctions[format]) { + formatFunctions[format] = makeFormatFunction(format); + } + + return formatFunctions[format](m); } - return false; - } - /** - * small indexOf wrapper - * @param {String} str - * @param {String} find - * @returns {Boolean} found - */ - function inStr(str, find) { - return str.indexOf(find) > -1; - } + function expandFormat(format, locale) { + var i = 5; - /** - * split string on whitespace - * @param {String} str - * @returns {Array} words - */ - function splitStr(str) { - return str.trim().split(/\s+/g); - } + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } - /** - * find if a array contains the object using indexOf or a simple polyFill - * @param {Array} src - * @param {String} find - * @param {String} [findByKey] - * @return {Boolean|Number} false when not found, or the index - */ - function inArray(src, find, findByKey) { - if (src.indexOf && !findByKey) { - return src.indexOf(find); - } else { - var i = 0; - while (i < src.length) { - if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { - return i; - } - i++; + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; } - return -1; + + return format; } - } - /** - * convert array-like objects to real arrays - * @param {Object} obj - * @returns {Array} - */ - function toArray(obj) { - return Array.prototype.slice.call(obj, 0); - } + var match1 = /\d/; // 0 - 9 + var match2 = /\d\d/; // 00 - 99 + var match3 = /\d{3}/; // 000 - 999 + var match4 = /\d{4}/; // 0000 - 9999 + var match6 = /[+-]?\d{6}/; // -999999 - 999999 + var match1to2 = /\d\d?/; // 0 - 99 + var match1to3 = /\d{1,3}/; // 0 - 999 + var match1to4 = /\d{1,4}/; // 0 - 9999 + var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 - /** - * unique array with objects based on a key (like 'id') or just by the array's value - * @param {Array} src [{id:1},{id:2},{id:1}] - * @param {String} [key] - * @param {Boolean} [sort=False] - * @returns {Array} [{id:1},{id:2}] - */ - function uniqueArray(src, key, sort) { - var results = []; - var values = []; - var i = 0; + var matchUnsigned = /\d+/; // 0 - inf + var matchSigned = /[+-]?\d+/; // -inf - inf - while (i < src.length) { - var val = key ? src[i][key] : src[i]; - if (inArray(values, val) < 0) { - results.push(src[i]); - } - values[i] = val; - i++; + var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z + + var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 + + // any word (or two) characters or numbers including two/three word month in arabic. + var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; + + var regexes = {}; + + function addRegexToken (token, regex, strictRegex) { + regexes[token] = typeof regex === 'function' ? regex : function (isStrict) { + return (isStrict && strictRegex) ? strictRegex : regex; + }; } - if (sort) { - if (!key) { - results = results.sort(); - } else { - results = results.sort(function sortUniqueArray(a, b) { - return a[key] > b[key]; - }); + function getParseRegexForToken (token, config) { + if (!hasOwnProp(regexes, token)) { + return new RegExp(unescapeFormat(token)); } - } - return results; - } + return regexes[token](config._strict, config._locale); + } - /** - * get the prefixed property - * @param {Object} obj - * @param {String} property - * @returns {String|Undefined} prefixed - */ - function prefixed(obj, property) { - var prefix, prop; - var camelProp = property[0].toUpperCase() + property.slice(1); + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function unescapeFormat(s) { + return s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + }).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } - var i = 0; - while (i < VENDOR_PREFIXES.length) { - prefix = VENDOR_PREFIXES[i]; - prop = (prefix) ? prefix + camelProp : property; + var tokens = {}; - if (prop in obj) { - return prop; + function addParseToken (token, callback) { + var i, func = callback; + if (typeof token === 'string') { + token = [token]; + } + if (typeof callback === 'number') { + func = function (input, array) { + array[callback] = toInt(input); + }; + } + for (i = 0; i < token.length; i++) { + tokens[token[i]] = func; } - i++; } - return undefined; - } - /** - * get a unique id - * @returns {number} uniqueId - */ - var _uniqueId = 1; - function uniqueId() { - return _uniqueId++; - } + function addWeekParseToken (token, callback) { + addParseToken(token, function (input, array, config, token) { + config._w = config._w || {}; + callback(input, config._w, config, token); + }); + } - /** - * get the window object of an element - * @param {HTMLElement} element - * @returns {DocumentView|Window} - */ - function getWindowForElement(element) { - var doc = element.ownerDocument; - return (doc.defaultView || doc.parentWindow); - } + function addTimeToArrayFromToken(token, input, config) { + if (input != null && hasOwnProp(tokens, token)) { + tokens[token](input, config._a, config, token); + } + } + + var YEAR = 0; + var MONTH = 1; + var DATE = 2; + var HOUR = 3; + var MINUTE = 4; + var SECOND = 5; + var MILLISECOND = 6; - var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; + function daysInMonth(year, month) { + return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + } - var SUPPORT_TOUCH = ('ontouchstart' in window); - var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; - var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); + // FORMATTING - var INPUT_TYPE_TOUCH = 'touch'; - var INPUT_TYPE_PEN = 'pen'; - var INPUT_TYPE_MOUSE = 'mouse'; - var INPUT_TYPE_KINECT = 'kinect'; + addFormatToken('M', ['MM', 2], 'Mo', function () { + return this.month() + 1; + }); - var COMPUTE_INTERVAL = 25; + addFormatToken('MMM', 0, 0, function (format) { + return this.localeData().monthsShort(this, format); + }); - var INPUT_START = 1; - var INPUT_MOVE = 2; - var INPUT_END = 4; - var INPUT_CANCEL = 8; + addFormatToken('MMMM', 0, 0, function (format) { + return this.localeData().months(this, format); + }); - var DIRECTION_NONE = 1; - var DIRECTION_LEFT = 2; - var DIRECTION_RIGHT = 4; - var DIRECTION_UP = 8; - var DIRECTION_DOWN = 16; + // ALIASES - var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; - var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; - var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; + addUnitAlias('month', 'M'); - var PROPS_XY = ['x', 'y']; - var PROPS_CLIENT_XY = ['clientX', 'clientY']; + // PARSING - /** - * create new input type manager - * @param {Manager} manager - * @param {Function} callback - * @returns {Input} - * @constructor - */ - function Input(manager, callback) { - var self = this; - this.manager = manager; - this.callback = callback; - this.element = manager.element; - this.target = manager.options.inputTarget; + addRegexToken('M', match1to2); + addRegexToken('MM', match1to2, match2); + addRegexToken('MMM', matchWord); + addRegexToken('MMMM', matchWord); - // smaller wrapper around the handler, for the scope and the enabled state of the manager, - // so when disabled the input events are completely bypassed. - this.domHandler = function(ev) { - if (boolOrFn(manager.options.enable, [manager])) { - self.handler(ev); + addParseToken(['M', 'MM'], function (input, array) { + array[MONTH] = toInt(input) - 1; + }); + + addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { + var month = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (month != null) { + array[MONTH] = month; + } else { + config._pf.invalidMonth = input; } - }; + }); - this.init(); + // LOCALES - } + var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); + function localeMonths (m) { + return this._months[m.month()]; + } - Input.prototype = { - /** - * should handle the inputEvent data and trigger the callback - * @virtual - */ - handler: function() { }, + var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); + function localeMonthsShort (m) { + return this._monthsShort[m.month()]; + } - /** - * bind the events - */ - init: function() { - this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); - this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); - this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); - }, + function localeMonthsParse (monthName, format, strict) { + var i, mom, regex; - /** - * unbind the events - */ - destroy: function() { - this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); - this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); - this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } + + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = create_utc__createUTC([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); + } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } } - }; - /** - * create new input type manager - * called by the Manager constructor - * @param {Hammer} manager - * @returns {Input} - */ - function createInputInstance(manager) { - var Type; - var inputClass = manager.options.inputClass; + // MOMENTS - if (inputClass) { - Type = inputClass; - } else if (SUPPORT_POINTER_EVENTS) { - Type = PointerEventInput; - } else if (SUPPORT_ONLY_TOUCH) { - Type = TouchInput; - } else if (!SUPPORT_TOUCH) { - Type = MouseInput; - } else { - Type = TouchMouseInput; - } - return new (Type)(manager, inputHandler); - } + function setMonth (mom, value) { + var dayOfMonth; - /** - * handle input events - * @param {Manager} manager - * @param {String} eventType - * @param {Object} input - */ - function inputHandler(manager, eventType, input) { - var pointersLen = input.pointers.length; - var changedPointersLen = input.changedPointers.length; - var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); - var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); + // TODO: Move this out of here! + if (typeof value === 'string') { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (typeof value !== 'number') { + return mom; + } + } - input.isFirst = !!isFirst; - input.isFinal = !!isFinal; + dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; + } - if (isFirst) { - manager.session = {}; + function getSetMonth (value) { + if (value != null) { + setMonth(this, value); + utils_hooks__hooks.updateOffset(this, true); + return this; + } else { + return get_set__get(this, 'Month'); + } } - // source event is the normalized value of the domEvents - // like 'touchstart, mouseup, pointerdown' - input.eventType = eventType; + function getDaysInMonth () { + return daysInMonth(this.year(), this.month()); + } - // compute scale, rotation etc - computeInputData(manager, input); + function checkOverflow (m) { + var overflow; + var a = m._a; - // emit secret event - manager.emit('hammer.input', input); + if (a && m._pf.overflow === -2) { + overflow = + a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : + a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : + a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : + a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : + a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : + a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : + -1; - manager.recognize(input); - manager.session.prevInput = input; - } + if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } - /** - * extend the data with some usable properties like scale, rotate, velocity etc - * @param {Object} manager - * @param {Object} input - */ - function computeInputData(manager, input) { - var session = manager.session; - var pointers = input.pointers; - var pointersLength = pointers.length; + m._pf.overflow = overflow; + } - // store the first input to calculate the distance and direction - if (!session.firstInput) { - session.firstInput = simpleCloneInputData(input); + return m; } - // to compute scale and rotation we need to store the multiple touches - if (pointersLength > 1 && !session.firstMultiple) { - session.firstMultiple = simpleCloneInputData(input); - } else if (pointersLength === 1) { - session.firstMultiple = false; + function warn(msg) { + if (utils_hooks__hooks.suppressDeprecationWarnings === false && typeof console !== 'undefined' && console.warn) { + console.warn('Deprecation warning: ' + msg); + } } - var firstInput = session.firstInput; - var firstMultiple = session.firstMultiple; - var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; + function deprecate(msg, fn) { + var firstTime = true; + return extend(function () { + if (firstTime) { + warn(msg); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); + } - var center = input.center = getCenter(pointers); - input.timeStamp = now(); - input.deltaTime = input.timeStamp - firstInput.timeStamp; + var deprecations = {}; - input.angle = getAngle(offsetCenter, center); - input.distance = getDistance(offsetCenter, center); + function deprecateSimple(name, msg) { + if (!deprecations[name]) { + warn(msg); + deprecations[name] = true; + } + } - computeDeltaXY(session, input); - input.offsetDirection = getDirection(input.deltaX, input.deltaY); + utils_hooks__hooks.suppressDeprecationWarnings = false; - input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; - input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; + var from_string__isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; - computeIntervalInputData(session, input); + var isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], + ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], + ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], + ['GGGG-[W]WW', /\d{4}-W\d{2}/], + ['YYYY-DDD', /\d{4}-\d{3}/] + ]; - // find the correct target - var target = manager.element; - if (hasParent(input.srcEvent.target, target)) { - target = input.srcEvent.target; - } - input.target = target; - } + // iso time formats and regexes + var isoTimes = [ + ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], + ['HH:mm', /(T| )\d\d:\d\d/], + ['HH', /(T| )\d\d/] + ]; - function computeDeltaXY(session, input) { - var center = input.center; - var offset = session.offsetDelta || {}; - var prevDelta = session.prevDelta || {}; - var prevInput = session.prevInput || {}; + var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; - if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { - prevDelta = session.prevDelta = { - x: prevInput.deltaX || 0, - y: prevInput.deltaY || 0 - }; + // date from iso format + function configFromISO(config) { + var i, l, + string = config._i, + match = from_string__isoRegex.exec(string); - offset = session.offsetDelta = { - x: center.x, - y: center.y - }; + if (match) { + config._pf.iso = true; + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(string)) { + // match[5] should be 'T' or undefined + config._f = isoDates[i][0] + (match[6] || ' '); + break; + } + } + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(string)) { + config._f += isoTimes[i][0]; + break; + } + } + if (string.match(matchOffset)) { + config._f += 'Z'; + } + configFromStringAndFormat(config); + } else { + config._isValid = false; + } } - input.deltaX = prevDelta.x + (center.x - offset.x); - input.deltaY = prevDelta.y + (center.y - offset.y); - } + // date from iso format or fallback + function configFromString(config) { + var matched = aspNetJsonRegex.exec(config._i); - /** - * velocity is calculated every x ms - * @param {Object} session - * @param {Object} input - */ - function computeIntervalInputData(session, input) { - var last = session.lastInterval || input, - deltaTime = input.timeStamp - last.timeStamp, - velocity, velocityX, velocityY, direction; + if (matched !== null) { + config._d = new Date(+matched[1]); + return; + } - if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { - var deltaX = last.deltaX - input.deltaX; - var deltaY = last.deltaY - input.deltaY; + configFromISO(config); + if (config._isValid === false) { + delete config._isValid; + utils_hooks__hooks.createFromInputFallback(config); + } + } - var v = getVelocity(deltaTime, deltaX, deltaY); - velocityX = v.x; - velocityY = v.y; - velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; - direction = getDirection(deltaX, deltaY); + utils_hooks__hooks.createFromInputFallback = deprecate( + 'moment construction falls back to js Date. This is ' + + 'discouraged and will be removed in upcoming major ' + + 'release. Please refer to ' + + 'https://github.com/moment/moment/issues/1407 for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } + ); - session.lastInterval = input; - } else { - // use latest velocity info if it doesn't overtake a minimum period - velocity = last.velocity; - velocityX = last.velocityX; - velocityY = last.velocityY; - direction = last.direction; - } + function createDate (y, m, d, h, M, s, ms) { + //can't just apply() to create a date: + //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply + var date = new Date(y, m, d, h, M, s, ms); - input.velocity = velocity; - input.velocityX = velocityX; - input.velocityY = velocityY; - input.direction = direction; - } + //the date constructor doesn't accept years < 1970 + if (y < 1970) { + date.setFullYear(y); + } + return date; + } - /** - * create a simple clone from the input used for storage of firstInput and firstMultiple - * @param {Object} input - * @returns {Object} clonedInputData - */ - function simpleCloneInputData(input) { - // make a simple copy of the pointers because we will get a reference if we don't - // we only need clientXY for the calculations - var pointers = []; - var i = 0; - while (i < input.pointers.length) { - pointers[i] = { - clientX: round(input.pointers[i].clientX), - clientY: round(input.pointers[i].clientY) - }; - i++; + function createUTCDate (y) { + var date = new Date(Date.UTC.apply(null, arguments)); + if (y < 1970) { + date.setUTCFullYear(y); + } + return date; } - return { - timeStamp: now(), - pointers: pointers, - center: getCenter(pointers), - deltaX: input.deltaX, - deltaY: input.deltaY - }; - } + addFormatToken(0, ['YY', 2], 0, function () { + return this.year() % 100; + }); - /** - * get the center of all the pointers - * @param {Array} pointers - * @return {Object} center contains `x` and `y` properties - */ - function getCenter(pointers) { - var pointersLength = pointers.length; + addFormatToken(0, ['YYYY', 4], 0, 'year'); + addFormatToken(0, ['YYYYY', 5], 0, 'year'); + addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); - // no need to loop when only one touch - if (pointersLength === 1) { - return { - x: round(pointers[0].clientX), - y: round(pointers[0].clientY) - }; - } + // ALIASES - var x = 0, y = 0, i = 0; - while (i < pointersLength) { - x += pointers[i].clientX; - y += pointers[i].clientY; - i++; - } + addUnitAlias('year', 'y'); - return { - x: round(x / pointersLength), - y: round(y / pointersLength) - }; - } + // PARSING - /** - * calculate the velocity between two points. unit is in px per ms. - * @param {Number} deltaTime - * @param {Number} x - * @param {Number} y - * @return {Object} velocity `x` and `y` - */ - function getVelocity(deltaTime, x, y) { - return { - x: x / deltaTime || 0, - y: y / deltaTime || 0 - }; - } + addRegexToken('Y', matchSigned); + addRegexToken('YY', match1to2, match2); + addRegexToken('YYYY', match1to4, match4); + addRegexToken('YYYYY', match1to6, match6); + addRegexToken('YYYYYY', match1to6, match6); - /** - * get the direction between two points - * @param {Number} x - * @param {Number} y - * @return {Number} direction - */ - function getDirection(x, y) { - if (x === y) { - return DIRECTION_NONE; - } + addParseToken(['YYYY', 'YYYYY', 'YYYYYY'], YEAR); + addParseToken('YY', function (input, array) { + array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input); + }); - if (abs(x) >= abs(y)) { - return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + // HELPERS + + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; } - return y > 0 ? DIRECTION_UP : DIRECTION_DOWN; - } - /** - * calculate the absolute distance between two points - * @param {Object} p1 {x, y} - * @param {Object} p2 {x, y} - * @param {Array} [props] containing x and y keys - * @return {Number} distance - */ - function getDistance(p1, p2, props) { - if (!props) { - props = PROPS_XY; + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } - var x = p2[props[0]] - p1[props[0]], - y = p2[props[1]] - p1[props[1]]; - return Math.sqrt((x * x) + (y * y)); - } + // HOOKS - /** - * calculate the angle between two coordinates - * @param {Object} p1 - * @param {Object} p2 - * @param {Array} [props] containing x and y keys - * @return {Number} angle - */ - function getAngle(p1, p2, props) { - if (!props) { - props = PROPS_XY; + utils_hooks__hooks.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; + + // MOMENTS + + var getSetYear = makeGetSet('FullYear', false); + + function getIsLeapYear () { + return isLeapYear(this.year()); } - var x = p2[props[0]] - p1[props[0]], - y = p2[props[1]] - p1[props[1]]; - return Math.atan2(y, x) * 180 / Math.PI; - } - /** - * calculate the rotation degrees between two pointersets - * @param {Array} start array of pointers - * @param {Array} end array of pointers - * @return {Number} rotation - */ - function getRotation(start, end) { - return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY); - } + addFormatToken('w', ['ww', 2], 'wo', 'week'); + addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); - /** - * calculate the scale factor between two pointersets - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @param {Array} start array of pointers - * @param {Array} end array of pointers - * @return {Number} scale - */ - function getScale(start, end) { - return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); - } + // ALIASES - var MOUSE_INPUT_MAP = { - mousedown: INPUT_START, - mousemove: INPUT_MOVE, - mouseup: INPUT_END - }; + addUnitAlias('week', 'w'); + addUnitAlias('isoWeek', 'W'); - var MOUSE_ELEMENT_EVENTS = 'mousedown'; - var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; + // PARSING - /** - * Mouse events input - * @constructor - * @extends Input - */ - function MouseInput() { - this.evEl = MOUSE_ELEMENT_EVENTS; - this.evWin = MOUSE_WINDOW_EVENTS; + addRegexToken('w', match1to2); + addRegexToken('ww', match1to2, match2); + addRegexToken('W', match1to2); + addRegexToken('WW', match1to2, match2); - this.allow = true; // used by Input.TouchMouse to disable mouse events - this.pressed = false; // mousedown state + addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { + week[token.substr(0, 1)] = toInt(input); + }); - Input.apply(this, arguments); - } + // HELPERS - inherit(MouseInput, Input, { - /** - * handle mouse events - * @param {Object} ev - */ - handler: function MEhandler(ev) { - var eventType = MOUSE_INPUT_MAP[ev.type]; + // firstDayOfWeek 0 = sun, 6 = sat + // the day of the week that starts the week + // (usually sunday or monday) + // firstDayOfWeekOfYear 0 = sun, 6 = sat + // the first week is the week that contains the first + // of this day of the week + // (eg. ISO weeks use thursday (4)) + function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { + var end = firstDayOfWeekOfYear - firstDayOfWeek, + daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), + adjustedMoment; - // on start we want to have the left mouse button down - if (eventType & INPUT_START && ev.button === 0) { - this.pressed = true; - } - if (eventType & INPUT_MOVE && ev.which !== 1) { - eventType = INPUT_END; + if (daysToDayOfWeek > end) { + daysToDayOfWeek -= 7; } - // mouse must be down, and mouse events are allowed (see the TouchMouse input) - if (!this.pressed || !this.allow) { - return; + if (daysToDayOfWeek < end - 7) { + daysToDayOfWeek += 7; } - if (eventType & INPUT_END) { - this.pressed = false; - } + adjustedMoment = local__createLocal(mom).add(daysToDayOfWeek, 'd'); + return { + week: Math.ceil(adjustedMoment.dayOfYear() / 7), + year: adjustedMoment.year() + }; + } - this.callback(this.manager, eventType, { - pointers: [ev], - changedPointers: [ev], - pointerType: INPUT_TYPE_MOUSE, - srcEvent: ev - }); + // LOCALES + + function localeWeek (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; } - }); - var POINTER_INPUT_MAP = { - pointerdown: INPUT_START, - pointermove: INPUT_MOVE, - pointerup: INPUT_END, - pointercancel: INPUT_CANCEL, - pointerout: INPUT_CANCEL - }; + var defaultLocaleWeek = { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + }; - // in IE10 the pointer types is defined as an enum - var IE10_POINTER_TYPE_ENUM = { - 2: INPUT_TYPE_TOUCH, - 3: INPUT_TYPE_PEN, - 4: INPUT_TYPE_MOUSE, - 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 - }; + function localeFirstDayOfWeek () { + return this._week.dow; + } - var POINTER_ELEMENT_EVENTS = 'pointerdown'; - var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; + function localeFirstDayOfYear () { + return this._week.doy; + } - // IE10 has prefixed support, and case-sensitive - if (window.MSPointerEvent) { - POINTER_ELEMENT_EVENTS = 'MSPointerDown'; - POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; - } + // MOMENTS - /** - * Pointer events input - * @constructor - * @extends Input - */ - function PointerEventInput() { - this.evEl = POINTER_ELEMENT_EVENTS; - this.evWin = POINTER_WINDOW_EVENTS; + function getSetWeek (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); + } - Input.apply(this, arguments); + function getSetISOWeek (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); + } - this.store = (this.manager.session.pointerEvents = []); - } + addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); - inherit(PointerEventInput, Input, { - /** - * handle mouse events - * @param {Object} ev - */ - handler: function PEhandler(ev) { - var store = this.store; - var removePointer = false; + // ALIASES - var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); - var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; - var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; + addUnitAlias('dayOfYear', 'DDD'); - var isTouch = (pointerType == INPUT_TYPE_TOUCH); + // PARSING - // get index of the event in the store - var storeIndex = inArray(store, ev.pointerId, 'pointerId'); + addRegexToken('DDD', match1to3); + addRegexToken('DDDD', match3); + addParseToken(['DDD', 'DDDD'], function (input, array, config) { + config._dayOfYear = toInt(input); + }); - // start and mouse must be down - if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { - if (storeIndex < 0) { - store.push(ev); - storeIndex = store.length - 1; - } - } else if (eventType & (INPUT_END | INPUT_CANCEL)) { - removePointer = true; - } + // HELPERS - // it not found, so the pointer hasn't been down (so it's probably a hover) - if (storeIndex < 0) { - return; - } + //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { + var d = createUTCDate(year, 0, 1).getUTCDay(); + var daysToAdd; + var dayOfYear; - // update the event in the store - store[storeIndex] = ev; + d = d === 0 ? 7 : d; + weekday = weekday != null ? weekday : firstDayOfWeek; + daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); + dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; - this.callback(this.manager, eventType, { - pointers: store, - changedPointers: [ev], - pointerType: pointerType, - srcEvent: ev - }); + return { + year : dayOfYear > 0 ? year : year - 1, + dayOfYear : dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear + }; + } - if (removePointer) { - // remove from the store - store.splice(storeIndex, 1); + // MOMENTS + + function getSetDayOfYear (input) { + var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); + } + + // Pick the first defined of two or three arguments. + function defaults(a, b, c) { + if (a != null) { + return a; + } + if (b != null) { + return b; } + return c; } - }); - var SINGLE_TOUCH_INPUT_MAP = { - touchstart: INPUT_START, - touchmove: INPUT_MOVE, - touchend: INPUT_END, - touchcancel: INPUT_CANCEL - }; + function currentDateArray(config) { + var now = new Date(); + if (config._useUTC) { + return [now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()]; + } + return [now.getFullYear(), now.getMonth(), now.getDate()]; + } - var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; - var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; + // convert an array to a date. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function configFromArray (config) { + var i, date, input = [], currentDate, yearToUse; - /** - * Touch events input - * @constructor - * @extends Input - */ - function SingleTouchInput() { - this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; - this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; - this.started = false; + if (config._d) { + return; + } - Input.apply(this, arguments); - } + currentDate = currentDateArray(config); - inherit(SingleTouchInput, Input, { - handler: function TEhandler(ev) { - var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } - // should we handle the touch events? - if (type === INPUT_START) { - this.started = true; + //if the day of the year is set, figure out what it is + if (config._dayOfYear) { + yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); + + if (config._dayOfYear > daysInYear(yearToUse)) { + config._pf._overflowDayOfYear = true; + } + + date = createUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); } - if (!this.started) { - return; + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; } - var touches = normalizeSingleTouches.call(this, ev, type); + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } - // when done, reset the started state - if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { - this.started = false; + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; } - this.callback(this.manager, type, { - pointers: touches[0], - changedPointers: touches[1], - pointerType: INPUT_TYPE_TOUCH, - srcEvent: ev - }); + config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } + + if (config._nextDay) { + config._a[HOUR] = 24; + } } - }); - /** - * @this {TouchInput} - * @param {Object} ev - * @param {Number} type flag - * @returns {undefined|Array} [all, changed] - */ - function normalizeSingleTouches(ev, type) { - var all = toArray(ev.touches); - var changed = toArray(ev.changedTouches); + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp; - if (type & (INPUT_END | INPUT_CANCEL)) { - all = uniqueArray(all.concat(changed), 'identifier', true); - } + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; - return [all, changed]; - } + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year); + week = defaults(w.W, 1); + weekday = defaults(w.E, 1); + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; - var TOUCH_INPUT_MAP = { - touchstart: INPUT_START, - touchmove: INPUT_MOVE, - touchend: INPUT_END, - touchcancel: INPUT_CANCEL - }; + weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year); + week = defaults(w.w, 1); - var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < dow) { + ++week; + } + } else if (w.e != null) { + // local weekday -- counting starts from begining of week + weekday = w.e + dow; + } else { + // default to begining of week + weekday = dow; + } + } + temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); - /** - * Multi-user touch events input - * @constructor - * @extends Input - */ - function TouchInput() { - this.evTarget = TOUCH_TARGET_EVENTS; - this.targetIds = {}; + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } - Input.apply(this, arguments); - } + utils_hooks__hooks.ISO_8601 = function () {}; - inherit(TouchInput, Input, { - handler: function MTEhandler(ev) { - var type = TOUCH_INPUT_MAP[ev.type]; - var touches = getTouches.call(this, ev, type); - if (!touches) { + // date from string and format string + function configFromStringAndFormat(config) { + // TODO: Move this to another part of the creation flow to prevent circular deps + if (config._f === utils_hooks__hooks.ISO_8601) { + configFromISO(config); return; } - this.callback(this.manager, type, { - pointers: touches[0], - changedPointers: touches[1], - pointerType: INPUT_TYPE_TOUCH, - srcEvent: ev - }); - } - }); + config._a = []; + config._pf.empty = true; - /** - * @this {TouchInput} - * @param {Object} ev - * @param {Number} type flag - * @returns {undefined|Array} [all, changed] - */ - function getTouches(ev, type) { - var allTouches = toArray(ev.touches); - var targetIds = this.targetIds; + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + config._pf.unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + config._pf.empty = false; + } + else { + config._pf.unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + config._pf.unusedTokens.push(token); + } + } + + // add remaining unparsed input length to the string + config._pf.charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + config._pf.unusedInput.push(string); + } + + // clear _12h flag if hour is <= 12 + if (config._pf.bigHour === true && config._a[HOUR] <= 12) { + config._pf.bigHour = undefined; + } + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); - // when there is only one touch, the process can be simplified - if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { - targetIds[allTouches[0].identifier] = true; - return [allTouches, allTouches]; + configFromArray(config); + checkOverflow(config); } - var i, - targetTouches, - changedTouches = toArray(ev.changedTouches), - changedTargetTouches = [], - target = this.target; - // get target touches from touches - targetTouches = allTouches.filter(function(touch) { - return hasParent(touch.target, target); - }); + function meridiemFixWrap (locale, hour, meridiem) { + var isPm; - // collect touches - if (type === INPUT_START) { - i = 0; - while (i < targetTouches.length) { - targetIds[targetTouches[i].identifier] = true; - i++; + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // this is not supposed to happen + return hour; } } - // filter changed touches to only contain touches that exist in the collected target ids - i = 0; - while (i < changedTouches.length) { - if (targetIds[changedTouches[i].identifier]) { - changedTargetTouches.push(changedTouches[i]); - } + function configFromStringAndArray(config) { + var tempConfig, + bestMoment, - // cleanup removed touches - if (type & (INPUT_END | INPUT_CANCEL)) { - delete targetIds[changedTouches[i].identifier]; + scoreToBeat, + i, + currentScore; + + if (config._f.length === 0) { + config._pf.invalidFormat = true; + config._d = new Date(NaN); + return; } - i++; - } - if (!changedTargetTouches.length) { - return; - } + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._pf = defaultParsingFlags(); + tempConfig._f = config._f[i]; + configFromStringAndFormat(tempConfig); - return [ - // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' - uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), - changedTargetTouches - ]; - } + if (!valid__isValid(tempConfig)) { + continue; + } - /** - * Combined touch and mouse input - * - * Touch has a higher priority then mouse, and while touching no mouse events are allowed. - * This because touch devices also emit mouse events while doing a touch. - * - * @constructor - * @extends Input - */ - function TouchMouseInput() { - Input.apply(this, arguments); + // if there is any input that was not parsed add a penalty for that format + currentScore += tempConfig._pf.charsLeftOver; - var handler = bindFn(this.handler, this); - this.touch = new TouchInput(this.manager, handler); - this.mouse = new MouseInput(this.manager, handler); - } + //or tokens + currentScore += tempConfig._pf.unusedTokens.length * 10; - inherit(TouchMouseInput, Input, { - /** - * handle mouse and touch events - * @param {Hammer} manager - * @param {String} inputEvent - * @param {Object} inputData - */ - handler: function TMEhandler(manager, inputEvent, inputData) { - var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), - isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); + tempConfig._pf.score = currentScore; - // when we're in a touch event, so block all upcoming mouse events - // most mobile browser also emit mouseevents, right after touchstart - if (isTouch) { - this.mouse.allow = false; - } else if (isMouse && !this.mouse.allow) { - return; + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } } - // reset the allowMouse when we're done - if (inputEvent & (INPUT_END | INPUT_CANCEL)) { - this.mouse.allow = true; + extend(config, bestMoment || tempConfig); + } + + function configFromObject(config) { + if (config._d) { + return; } - this.callback(manager, inputEvent, inputData); - }, + var i = normalizeObjectUnits(config._i); + config._a = [i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond]; - /** - * remove the event listeners - */ - destroy: function destroy() { - this.touch.destroy(); - this.mouse.destroy(); + configFromArray(config); } - }); - var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); - var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; + function createFromConfig (config) { + var input = config._i, + format = config._f, + res; - // magical touchAction value - var TOUCH_ACTION_COMPUTE = 'compute'; - var TOUCH_ACTION_AUTO = 'auto'; - var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented - var TOUCH_ACTION_NONE = 'none'; - var TOUCH_ACTION_PAN_X = 'pan-x'; - var TOUCH_ACTION_PAN_Y = 'pan-y'; + config._locale = config._locale || locale_locales__getLocale(config._l); - /** - * Touch Action - * sets the touchAction property or uses the js alternative - * @param {Manager} manager - * @param {String} value - * @constructor - */ - function TouchAction(manager, value) { - this.manager = manager; - this.set(value); - } + if (input === null || (format === undefined && input === '')) { + return valid__createInvalid({nullInput: true}); + } - TouchAction.prototype = { - /** - * set the touchAction value on the element or enable the polyfill - * @param {String} value - */ - set: function(value) { - // find out the touch-action by the event handlers - if (value == TOUCH_ACTION_COMPUTE) { - value = this.compute(); + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); } - if (NATIVE_TOUCH_ACTION) { - this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; + if (isMoment(input)) { + return new Moment(checkOverflow(input)); + } else if (isArray(format)) { + configFromStringAndArray(config); + } else if (format) { + configFromStringAndFormat(config); + } else { + configFromInput(config); } - this.actions = value.toLowerCase().trim(); - }, - /** - * just re-set the touchAction value - */ - update: function() { - this.set(this.manager.options.touchAction); - }, + res = new Moment(checkOverflow(config)); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } - /** - * compute the value for the touchAction property based on the recognizer's settings - * @returns {String} value - */ - compute: function() { - var actions = []; - each(this.manager.recognizers, function(recognizer) { - if (boolOrFn(recognizer.options.enable, [recognizer])) { - actions = actions.concat(recognizer.getTouchAction()); - } - }); - return cleanTouchActions(actions.join(' ')); - }, + return res; + } - /** - * this method is called on each input cycle and provides the preventing of the browser behavior - * @param {Object} input - */ - preventDefaults: function(input) { - // not needed with native support for the touchAction property - if (NATIVE_TOUCH_ACTION) { - return; + function configFromInput(config) { + var input = config._i; + if (input === undefined) { + config._d = new Date(); + } else if (isDate(input)) { + config._d = new Date(+input); + } else if (typeof input === 'string') { + configFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + configFromArray(config); + } else if (typeof(input) === 'object') { + configFromObject(config); + } else if (typeof(input) === 'number') { + // from milliseconds + config._d = new Date(input); + } else { + utils_hooks__hooks.createFromInputFallback(config); } + } - var srcEvent = input.srcEvent; - var direction = input.offsetDirection; + function createLocalOrUTC (input, format, locale, strict, isUTC) { + var c = {}; - // if the touch action did prevented once this session - if (this.manager.session.prevented) { - srcEvent.preventDefault(); - return; + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c._isAMomentObject = true; + c._useUTC = c._isUTC = isUTC; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + c._pf = defaultParsingFlags(); - var actions = this.actions; - var hasNone = inStr(actions, TOUCH_ACTION_NONE); - var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); - var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); + return createFromConfig(c); + } - if (hasNone || - (hasPanY && direction & DIRECTION_HORIZONTAL) || - (hasPanX && direction & DIRECTION_VERTICAL)) { - return this.preventSrc(srcEvent); + function local__createLocal (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, false); + } + + var prototypeMin = deprecate( + 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', + function () { + var other = local__createLocal.apply(null, arguments); + return other < this ? this : other; + } + ); + + var prototypeMax = deprecate( + 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', + function () { + var other = local__createLocal.apply(null, arguments); + return other > this ? this : other; } - }, + ); - /** - * call preventDefault to prevent the browser's default behavior (scrolling in most cases) - * @param {Object} srcEvent - */ - preventSrc: function(srcEvent) { - this.manager.session.prevented = true; - srcEvent.preventDefault(); + // Pick a moment m from moments so that m[fn](other) is true for all + // other. This relies on the function fn to be transitive. + // + // moments should either be an array of moment objects or an array, whose + // first element is an array of moment objects. + function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return local__createLocal(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (moments[i][fn](res)) { + res = moments[i]; + } + } + return res; } - }; - /** - * when the touchActions are collected they are not a valid value, so we need to clean things up. * - * @param {String} actions - * @returns {*} - */ - function cleanTouchActions(actions) { - // none - if (inStr(actions, TOUCH_ACTION_NONE)) { - return TOUCH_ACTION_NONE; + // TODO: Use [].sort instead? + function min () { + var args = [].slice.call(arguments, 0); + + return pickBy('isBefore', args); } - var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); - var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); + function max () { + var args = [].slice.call(arguments, 0); - // pan-x and pan-y can be combined - if (hasPanX && hasPanY) { - return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y; + return pickBy('isAfter', args); } - // pan-x OR pan-y - if (hasPanX || hasPanY) { - return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; + function Duration (duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; + + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 36e5; // 1000 * 60 * 60 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; + + this._data = {}; + + this._locale = locale_locales__getLocale(); + + this._bubble(); } - // manipulation - if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { - return TOUCH_ACTION_MANIPULATION; + function isDuration (obj) { + return obj instanceof Duration; } - return TOUCH_ACTION_AUTO; - } + function offset (token, separator) { + addFormatToken(token, 0, 0, function () { + var offset = this.utcOffset(); + var sign = '+'; + if (offset < 0) { + offset = -offset; + sign = '-'; + } + return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); + }); + } - /** - * Recognizer flow explained; * - * All recognizers have the initial state of POSSIBLE when a input session starts. - * The definition of a input session is from the first input until the last input, with all it's movement in it. * - * Example session for mouse-input: mousedown -> mousemove -> mouseup - * - * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed - * which determines with state it should be. - * - * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to - * POSSIBLE to give it another change on the next cycle. - * - * Possible - * | - * +-----+---------------+ - * | | - * +-----+-----+ | - * | | | - * Failed Cancelled | - * +-------+------+ - * | | - * Recognized Began - * | - * Changed - * | - * Ended/Recognized - */ - var STATE_POSSIBLE = 1; - var STATE_BEGAN = 2; - var STATE_CHANGED = 4; - var STATE_ENDED = 8; - var STATE_RECOGNIZED = STATE_ENDED; - var STATE_CANCELLED = 16; - var STATE_FAILED = 32; + offset('Z', ':'); + offset('ZZ', ''); - /** - * Recognizer - * Every recognizer needs to extend from this class. - * @constructor - * @param {Object} options - */ - function Recognizer(options) { - this.id = uniqueId(); + // PARSING - this.manager = null; - this.options = merge(options || {}, this.defaults); + addRegexToken('Z', matchOffset); + addRegexToken('ZZ', matchOffset); + addParseToken(['Z', 'ZZ'], function (input, array, config) { + config._useUTC = true; + config._tzm = offsetFromString(input); + }); - // default is enable true - this.options.enable = ifUndefined(this.options.enable, true); + // HELPERS - this.state = STATE_POSSIBLE; + // timezone chunker + // '+10:00' > ['10', '00'] + // '-1530' > ['-15', '30'] + var chunkOffset = /([\+\-]|\d\d)/gi; - this.simultaneous = {}; - this.requireFail = []; - } + function offsetFromString(string) { + var matches = ((string || '').match(matchOffset) || []); + var chunk = matches[matches.length - 1] || []; + var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; + var minutes = +(parts[1] * 60) + toInt(parts[2]); - Recognizer.prototype = { - /** - * @virtual - * @type {Object} - */ - defaults: {}, + return parts[0] === '+' ? minutes : -minutes; + } + + // Return a moment from input, that is local/utc/zone equivalent to model. + function cloneWithOffset(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (isMoment(input) || isDate(input) ? +input : +local__createLocal(input)) - (+res); + // Use low-level api, because this fn is low-level api. + res._d.setTime(+res._d + diff); + utils_hooks__hooks.updateOffset(res, false); + return res; + } else { + return local__createLocal(input).local(); + } + return model._isUTC ? local__createLocal(input).zone(model._offset || 0) : local__createLocal(input).local(); + } + + function getDateOffset (m) { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(m._d.getTimezoneOffset() / 15) * 15; + } + + // HOOKS - /** - * set options - * @param {Object} options - * @return {Recognizer} - */ - set: function(options) { - extend(this.options, options); + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + utils_hooks__hooks.updateOffset = function () {}; - // also update the touchAction, in case something changed about the directions/enabled state - this.manager && this.manager.touchAction.update(); - return this; - }, + // MOMENTS - /** - * recognize simultaneous with an other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - recognizeWith: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { + // keepLocalTime = true means only change the timezone, without + // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> + // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset + // +0200, so we adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + function getSetOffset (input, keepLocalTime) { + var offset = this._offset || 0, + localAdjust; + if (input != null) { + if (typeof input === 'string') { + input = offsetFromString(input); + } + if (Math.abs(input) < 16) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = getDateOffset(this); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + utils_hooks__hooks.updateOffset(this, true); + this._changeInProgress = null; + } + } return this; + } else { + return this._isUTC ? offset : getDateOffset(this); } + } - var simultaneous = this.simultaneous; - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - if (!simultaneous[otherRecognizer.id]) { - simultaneous[otherRecognizer.id] = otherRecognizer; - otherRecognizer.recognizeWith(this); - } - return this; - }, + function getSetZone (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } + + this.utcOffset(input, keepLocalTime); - /** - * drop the simultaneous link. it doesnt remove the link on the other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - dropRecognizeWith: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { return this; + } else { + return -this.utcOffset(); } + } - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - delete this.simultaneous[otherRecognizer.id]; - return this; - }, + function setOffsetToUTC (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); + } - /** - * recognizer can only run when an other is failing - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - requireFailure: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { - return this; - } + function setOffsetToLocal (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; - var requireFail = this.requireFail; - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - if (inArray(requireFail, otherRecognizer) === -1) { - requireFail.push(otherRecognizer); - otherRecognizer.requireFailure(this); + if (keepLocalTime) { + this.subtract(getDateOffset(this), 'm'); + } } return this; - }, - - /** - * drop the requireFailure link. it does not remove the link on the other recognizer. - * @param {Recognizer} otherRecognizer - * @returns {Recognizer} this - */ - dropRequireFailure: function(otherRecognizer) { - if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { - return this; - } + } - otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); - var index = inArray(this.requireFail, otherRecognizer); - if (index > -1) { - this.requireFail.splice(index, 1); + function setOffsetToParsedOffset () { + if (this._tzm) { + this.utcOffset(this._tzm); + } else if (typeof this._i === 'string') { + this.utcOffset(offsetFromString(this._i)); } return this; - }, + } - /** - * has require failures boolean - * @returns {boolean} - */ - hasRequireFailures: function() { - return this.requireFail.length > 0; - }, + function hasAlignedHourOffset (input) { + if (!input) { + input = 0; + } + else { + input = local__createLocal(input).utcOffset(); + } - /** - * if the recognizer can recognize simultaneous with an other recognizer - * @param {Recognizer} otherRecognizer - * @returns {Boolean} - */ - canRecognizeWith: function(otherRecognizer) { - return !!this.simultaneous[otherRecognizer.id]; - }, + return (this.utcOffset() - input) % 60 === 0; + } - /** - * You should use `tryEmit` instead of `emit` directly to check - * that all the needed recognizers has failed before emitting. - * @param {Object} input - */ - emit: function(input) { - var self = this; - var state = this.state; + function isDaylightSavingTime () { + return ( + this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset() + ); + } - function emit(withState) { - self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input); + function isDaylightSavingTimeShifted () { + if (this._a) { + var other = this._isUTC ? create_utc__createUTC(this._a) : local__createLocal(this._a); + return this.isValid() && compareArrays(this._a, other.toArray()) > 0; } - // 'panstart' and 'panmove' - if (state < STATE_ENDED) { - emit(true); - } + return false; + } - emit(); // simple 'eventName' events + function isLocal () { + return !this._isUTC; + } - // panend and pancancel - if (state >= STATE_ENDED) { - emit(true); - } - }, + function isUtcOffset () { + return this._isUTC; + } - /** - * Check that all the require failure recognizers has failed, - * if true, it emits a gesture event, - * otherwise, setup the state to FAILED. - * @param {Object} input - */ - tryEmit: function(input) { - if (this.canEmit()) { - return this.emit(input); - } - // it's failing anyway - this.state = STATE_FAILED; - }, + function isUtc () { + return this._isUTC && this._offset === 0; + } - /** - * can we emit? - * @returns {boolean} - */ - canEmit: function() { - var i = 0; - while (i < this.requireFail.length) { - if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { - return false; + var aspNetRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/; + + // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html + // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere + var create__isoRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/; + + function create__createDuration (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + diffRes; + + if (isDuration(input)) { + duration = { + ms : input._milliseconds, + d : input._days, + M : input._months + }; + } else if (typeof input === 'number') { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; } - i++; + } else if (!!(match = aspNetRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : 0, + d : toInt(match[DATE]) * sign, + h : toInt(match[HOUR]) * sign, + m : toInt(match[MINUTE]) * sign, + s : toInt(match[SECOND]) * sign, + ms : toInt(match[MILLISECOND]) * sign + }; + } else if (!!(match = create__isoRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : parseIso(match[2], sign), + M : parseIso(match[3], sign), + d : parseIso(match[4], sign), + h : parseIso(match[5], sign), + m : parseIso(match[6], sign), + s : parseIso(match[7], sign), + w : parseIso(match[8], sign) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to)); + + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; } - return true; - }, - /** - * update the recognizer - * @param {Object} inputData - */ - recognize: function(inputData) { - // make a new copy of the inputData - // so we can change the inputData without messing up the other recognizers - var inputDataClone = extend({}, inputData); + ret = new Duration(duration); - // is is enabled and allow recognizing? - if (!boolOrFn(this.options.enable, [this, inputDataClone])) { - this.reset(); - this.state = STATE_FAILED; - return; + if (isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; } - // reset when we've reached the end - if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { - this.state = STATE_POSSIBLE; - } + return ret; + } - this.state = this.process(inputDataClone); + create__createDuration.fn = Duration.prototype; - // the recognizer has recognized a gesture - // so trigger an event - if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { - this.tryEmit(inputDataClone); - } - }, + function parseIso (inp, sign) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + } - /** - * return the state of the recognizer - * the actual recognizing happens in this method - * @virtual - * @param {Object} inputData - * @returns {Const} STATE - */ - process: function(inputData) { }, // jshint ignore:line + function positiveMomentsDifference(base, other) { + var res = {milliseconds: 0, months: 0}; - /** - * return the preferred touch-action - * @virtual - * @returns {Array} - */ - getTouchAction: function() { }, + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } - /** - * called when the gesture isn't allowed to recognize - * like when another is being recognized or it is disabled - * @virtual - */ - reset: function() { } - }; + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - /** - * get a usable string, used as event postfix - * @param {Const} state - * @returns {String} state - */ - function stateStr(state) { - if (state & STATE_CANCELLED) { - return 'cancel'; - } else if (state & STATE_ENDED) { - return 'end'; - } else if (state & STATE_CHANGED) { - return 'move'; - } else if (state & STATE_BEGAN) { - return 'start'; + return res; } - return ''; - } - /** - * direction cons to string - * @param {Const} direction - * @returns {String} - */ - function directionStr(direction) { - if (direction == DIRECTION_DOWN) { - return 'down'; - } else if (direction == DIRECTION_UP) { - return 'up'; - } else if (direction == DIRECTION_LEFT) { - return 'left'; - } else if (direction == DIRECTION_RIGHT) { - return 'right'; + function momentsDifference(base, other) { + var res; + other = cloneWithOffset(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } + + return res; } - return ''; - } - /** - * get a recognizer by name if it is bound to a manager - * @param {Recognizer|String} otherRecognizer - * @param {Recognizer} recognizer - * @returns {Recognizer} - */ - function getRecognizerByNameIfManager(otherRecognizer, recognizer) { - var manager = recognizer.manager; - if (manager) { - return manager.get(otherRecognizer); + function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); + tmp = val; val = period; period = tmp; + } + + val = typeof val === 'string' ? +val : val; + dur = create__createDuration(val, period); + add_subtract__addSubtract(this, dur, direction); + return this; + }; } - return otherRecognizer; - } - /** - * This recognizer is just used as a base for the simple attribute recognizers. - * @constructor - * @extends Recognizer - */ - function AttrRecognizer() { - Recognizer.apply(this, arguments); - } + function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = duration._days, + months = duration._months; + updateOffset = updateOffset == null ? true : updateOffset; - inherit(AttrRecognizer, Recognizer, { - /** - * @namespace - * @memberof AttrRecognizer - */ - defaults: { - /** - * @type {Number} - * @default 1 - */ - pointers: 1 - }, + if (milliseconds) { + mom._d.setTime(+mom._d + milliseconds * isAdding); + } + if (days) { + get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding); + } + if (months) { + setMonth(mom, get_set__get(mom, 'Month') + months * isAdding); + } + if (updateOffset) { + utils_hooks__hooks.updateOffset(mom, days || months); + } + } - /** - * Used to check if it the recognizer receives valid input, like input.distance > 10. - * @memberof AttrRecognizer - * @param {Object} input - * @returns {Boolean} recognized - */ - attrTest: function(input) { - var optionPointers = this.options.pointers; - return optionPointers === 0 || input.pointers.length === optionPointers; - }, + var add_subtract__add = createAdder(1, 'add'); + var add_subtract__subtract = createAdder(-1, 'subtract'); - /** - * Process the input and return the state for the recognizer - * @memberof AttrRecognizer - * @param {Object} input - * @returns {*} State - */ - process: function(input) { - var state = this.state; - var eventType = input.eventType; + function moment_calendar__calendar (time) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're local/utc/offset or not. + var now = time || local__createLocal(), + sod = cloneWithOffset(now, this).startOf('day'), + diff = this.diff(sod, 'days', true), + format = diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; + return this.format(this.localeData().calendar(format, this, local__createLocal(now))); + } - var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); - var isValid = this.attrTest(input); + function clone () { + return new Moment(this); + } - // on cancel input and we've recognized before, return STATE_CANCELLED - if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { - return state | STATE_CANCELLED; - } else if (isRecognized || isValid) { - if (eventType & INPUT_END) { - return state | STATE_ENDED; - } else if (!(state & STATE_BEGAN)) { - return STATE_BEGAN; - } - return state | STATE_CHANGED; + function isAfter (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = isMoment(input) ? input : local__createLocal(input); + return +this > +input; + } else { + inputMs = isMoment(input) ? +input : +local__createLocal(input); + return inputMs < +this.clone().startOf(units); } - return STATE_FAILED; } - }); - - /** - * Pan - * Recognized when the pointer is down and moved in the allowed direction. - * @constructor - * @extends AttrRecognizer - */ - function PanRecognizer() { - AttrRecognizer.apply(this, arguments); - this.pX = null; - this.pY = null; - } + function isBefore (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = isMoment(input) ? input : local__createLocal(input); + return +this < +input; + } else { + inputMs = isMoment(input) ? +input : +local__createLocal(input); + return +this.clone().endOf(units) < inputMs; + } + } - inherit(PanRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof PanRecognizer - */ - defaults: { - event: 'pan', - threshold: 10, - pointers: 1, - direction: DIRECTION_ALL - }, + function isBetween (from, to, units) { + return this.isAfter(from, units) && this.isBefore(to, units); + } - getTouchAction: function() { - var direction = this.options.direction; - var actions = []; - if (direction & DIRECTION_HORIZONTAL) { - actions.push(TOUCH_ACTION_PAN_Y); + function isSame (input, units) { + var inputMs; + units = normalizeUnits(units || 'millisecond'); + if (units === 'millisecond') { + input = isMoment(input) ? input : local__createLocal(input); + return +this === +input; + } else { + inputMs = +local__createLocal(input); + return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); } - if (direction & DIRECTION_VERTICAL) { - actions.push(TOUCH_ACTION_PAN_X); + } + + function absFloor (number) { + if (number < 0) { + return Math.ceil(number); + } else { + return Math.floor(number); } - return actions; - }, + } - directionTest: function(input) { - var options = this.options; - var hasMoved = true; - var distance = input.distance; - var direction = input.direction; - var x = input.deltaX; - var y = input.deltaY; + function diff (input, units, asFloat) { + var that = cloneWithOffset(input, this), + zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4, + delta, output; - // lock to axis? - if (!(direction & options.direction)) { - if (options.direction & DIRECTION_HORIZONTAL) { - direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; - hasMoved = x != this.pX; - distance = Math.abs(input.deltaX); - } else { - direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; - hasMoved = y != this.pY; - distance = Math.abs(input.deltaY); + units = normalizeUnits(units); + + if (units === 'year' || units === 'month' || units === 'quarter') { + output = monthDiff(this, that); + if (units === 'quarter') { + output = output / 3; + } else if (units === 'year') { + output = output / 12; } + } else { + delta = this - that; + output = units === 'second' ? delta / 1e3 : // 1000 + units === 'minute' ? delta / 6e4 : // 1000 * 60 + units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 + units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst + units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst + delta; } - input.direction = direction; - return hasMoved && distance > options.threshold && direction & options.direction; - }, - - attrTest: function(input) { - return AttrRecognizer.prototype.attrTest.call(this, input) && - (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); - }, + return asFloat ? output : absFloor(output); + } - emit: function(input) { - this.pX = input.deltaX; - this.pY = input.deltaY; + function monthDiff (a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; - var direction = directionStr(input.direction); - if (direction) { - this.manager.emit(this.options.event + direction, input); + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); } - this._super.emit.call(this, input); + return -(wholeMonthDiff + adjust); } - }); - - /** - * Pinch - * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). - * @constructor - * @extends AttrRecognizer - */ - function PinchRecognizer() { - AttrRecognizer.apply(this, arguments); - } - - inherit(PinchRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof PinchRecognizer - */ - defaults: { - event: 'pinch', - threshold: 0, - pointers: 2 - }, - getTouchAction: function() { - return [TOUCH_ACTION_NONE]; - }, + utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; - attrTest: function(input) { - return this._super.attrTest.call(this, input) && - (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); - }, + function toString () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + } - emit: function(input) { - this._super.emit.call(this, input); - if (input.scale !== 1) { - var inOut = input.scale < 1 ? 'in' : 'out'; - this.manager.emit(this.options.event + inOut, input); + function moment_format__toISOString () { + var m = this.clone().utc(); + if (0 < m.year() && m.year() <= 9999) { + if ('function' === typeof Date.prototype.toISOString) { + // native implementation is ~50x faster, use it when we can + return this.toDate().toISOString(); + } else { + return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + } else { + return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } } - }); - - /** - * Press - * Recognized when the pointer is down for x ms without any movement. - * @constructor - * @extends Recognizer - */ - function PressRecognizer() { - Recognizer.apply(this, arguments); - this._timer = null; - this._input = null; - } + function format (inputString) { + var output = formatMoment(this, inputString || utils_hooks__hooks.defaultFormat); + return this.localeData().postformat(output); + } - inherit(PressRecognizer, Recognizer, { - /** - * @namespace - * @memberof PressRecognizer - */ - defaults: { - event: 'press', - pointers: 1, - time: 500, // minimal time of the pointer to be pressed - threshold: 5 // a minimal movement is ok, but keep it low - }, + function from (time, withoutSuffix) { + return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + } - getTouchAction: function() { - return [TOUCH_ACTION_AUTO]; - }, + function fromNow (withoutSuffix) { + return this.from(local__createLocal(), withoutSuffix); + } - process: function(input) { - var options = this.options; - var validPointers = input.pointers.length === options.pointers; - var validMovement = input.distance < options.threshold; - var validTime = input.deltaTime > options.time; + function locale (key) { + var newLocaleData; - this._input = input; + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = locale_locales__getLocale(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } + } - // we only allow little movement - // and we've reached an end event, so a tap is possible - if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { - this.reset(); - } else if (input.eventType & INPUT_START) { - this.reset(); - this._timer = setTimeoutContext(function() { - this.state = STATE_RECOGNIZED; - this.tryEmit(); - }, options.time, this); - } else if (input.eventType & INPUT_END) { - return STATE_RECOGNIZED; + var lang = deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } } - return STATE_FAILED; - }, + ); - reset: function() { - clearTimeout(this._timer); - }, + function localeData () { + return this._locale; + } - emit: function(input) { - if (this.state !== STATE_RECOGNIZED) { - return; + function startOf (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { + case 'year': + this.month(0); + /* falls through */ + case 'quarter': + case 'month': + this.date(1); + /* falls through */ + case 'week': + case 'isoWeek': + case 'day': + this.hours(0); + /* falls through */ + case 'hour': + this.minutes(0); + /* falls through */ + case 'minute': + this.seconds(0); + /* falls through */ + case 'second': + this.milliseconds(0); } - if (input && (input.eventType & INPUT_END)) { - this.manager.emit(this.options.event + 'up', input); - } else { - this._input.timeStamp = now(); - this.manager.emit(this.options.event, this._input); + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } + if (units === 'isoWeek') { + this.isoWeekday(1); } - } - }); - /** - * Rotate - * Recognized when two or more pointer are moving in a circular motion. - * @constructor - * @extends AttrRecognizer - */ - function RotateRecognizer() { - AttrRecognizer.apply(this, arguments); - } + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } - inherit(RotateRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof RotateRecognizer - */ - defaults: { - event: 'rotate', - threshold: 0, - pointers: 2 - }, + return this; + } - getTouchAction: function() { - return [TOUCH_ACTION_NONE]; - }, + function endOf (units) { + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond') { + return this; + } + return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); + } - attrTest: function(input) { - return this._super.attrTest.call(this, input) && - (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); + function to_type__valueOf () { + return +this._d - ((this._offset || 0) * 60000); } - }); - /** - * Swipe - * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. - * @constructor - * @extends AttrRecognizer - */ - function SwipeRecognizer() { - AttrRecognizer.apply(this, arguments); - } + function unix () { + return Math.floor(+this / 1000); + } - inherit(SwipeRecognizer, AttrRecognizer, { - /** - * @namespace - * @memberof SwipeRecognizer - */ - defaults: { - event: 'swipe', - threshold: 10, - velocity: 0.65, - direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, - pointers: 1 - }, + function toDate () { + return this._offset ? new Date(+this) : this._d; + } - getTouchAction: function() { - return PanRecognizer.prototype.getTouchAction.call(this); - }, + function toArray () { + var m = this; + return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; + } - attrTest: function(input) { - var direction = this.options.direction; - var velocity; + function moment_valid__isValid () { + return valid__isValid(this); + } - if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { - velocity = input.velocity; - } else if (direction & DIRECTION_HORIZONTAL) { - velocity = input.velocityX; - } else if (direction & DIRECTION_VERTICAL) { - velocity = input.velocityY; - } + function parsingFlags () { + return extend({}, this._pf); + } - return this._super.attrTest.call(this, input) && - direction & input.direction && - input.distance > this.options.threshold && - abs(velocity) > this.options.velocity && input.eventType & INPUT_END; - }, + function invalidAt () { + return this._pf.overflow; + } - emit: function(input) { - var direction = directionStr(input.direction); - if (direction) { - this.manager.emit(this.options.event + direction, input); - } + addFormatToken(0, ['gg', 2], 0, function () { + return this.weekYear() % 100; + }); - this.manager.emit(this.options.event, input); + addFormatToken(0, ['GG', 2], 0, function () { + return this.isoWeekYear() % 100; + }); + + function addWeekYearFormatToken (token, getter) { + addFormatToken(0, [token, token.length], 0, getter); } - }); - /** - * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur - * between the given interval and position. The delay option can be used to recognize multi-taps without firing - * a single tap. - * - * The eventData from the emitted event contains the property `tapCount`, which contains the amount of - * multi-taps being recognized. - * @constructor - * @extends Recognizer - */ - function TapRecognizer() { - Recognizer.apply(this, arguments); + addWeekYearFormatToken('gggg', 'weekYear'); + addWeekYearFormatToken('ggggg', 'weekYear'); + addWeekYearFormatToken('GGGG', 'isoWeekYear'); + addWeekYearFormatToken('GGGGG', 'isoWeekYear'); - // previous time and center, - // used for tap counting - this.pTime = false; - this.pCenter = false; + // ALIASES - this._timer = null; - this._input = null; - this.count = 0; - } + addUnitAlias('weekYear', 'gg'); + addUnitAlias('isoWeekYear', 'GG'); - inherit(TapRecognizer, Recognizer, { - /** - * @namespace - * @memberof PinchRecognizer - */ - defaults: { - event: 'tap', - pointers: 1, - taps: 1, - interval: 300, // max time between the multi-tap taps - time: 250, // max time of the pointer to be down (like finger on the screen) - threshold: 2, // a minimal movement is ok, but keep it low - posThreshold: 10 // a multi-tap can be a bit off the initial position - }, + // PARSING - getTouchAction: function() { - return [TOUCH_ACTION_MANIPULATION]; - }, + addRegexToken('G', matchSigned); + addRegexToken('g', matchSigned); + addRegexToken('GG', match1to2, match2); + addRegexToken('gg', match1to2, match2); + addRegexToken('GGGG', match1to4, match4); + addRegexToken('gggg', match1to4, match4); + addRegexToken('GGGGG', match1to6, match6); + addRegexToken('ggggg', match1to6, match6); - process: function(input) { - var options = this.options; + addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { + week[token.substr(0, 2)] = toInt(input); + }); - var validPointers = input.pointers.length === options.pointers; - var validMovement = input.distance < options.threshold; - var validTouchTime = input.deltaTime < options.time; + addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { + week[token] = utils_hooks__hooks.parseTwoDigitYear(input); + }); - this.reset(); + // HELPERS - if ((input.eventType & INPUT_START) && (this.count === 0)) { - return this.failTimeout(); - } + function weeksInYear(year, dow, doy) { + return weekOfYear(local__createLocal([year, 11, 31 + dow - doy]), dow, doy).week; + } - // we only allow little movement - // and we've reached an end event, so a tap is possible - if (validMovement && validTouchTime && validPointers) { - if (input.eventType != INPUT_END) { - return this.failTimeout(); - } + // MOMENTS + + function getSetWeekYear (input) { + var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; + return input == null ? year : this.add((input - year), 'y'); + } - var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; - var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; + function getSetISOWeekYear (input) { + var year = weekOfYear(this, 1, 4).year; + return input == null ? year : this.add((input - year), 'y'); + } - this.pTime = input.timeStamp; - this.pCenter = input.center; + function getISOWeeksInYear () { + return weeksInYear(this.year(), 1, 4); + } - if (!validMultiTap || !validInterval) { - this.count = 1; - } else { - this.count += 1; - } + function getWeeksInYear () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + } - this._input = input; + addFormatToken('Q', 0, 0, 'quarter'); - // if tap count matches we have recognized it, - // else it has began recognizing... - var tapCount = this.count % options.taps; - if (tapCount === 0) { - // no failing requirements, immediately trigger the tap event - // or wait as long as the multitap interval to trigger - if (!this.hasRequireFailures()) { - return STATE_RECOGNIZED; - } else { - this._timer = setTimeoutContext(function() { - this.state = STATE_RECOGNIZED; - this.tryEmit(); - }, options.interval, this); - return STATE_BEGAN; - } - } - } - return STATE_FAILED; - }, + // ALIASES - failTimeout: function() { - this._timer = setTimeoutContext(function() { - this.state = STATE_FAILED; - }, this.options.interval, this); - return STATE_FAILED; - }, + addUnitAlias('quarter', 'Q'); - reset: function() { - clearTimeout(this._timer); - }, + // PARSING - emit: function() { - if (this.state == STATE_RECOGNIZED ) { - this._input.tapCount = this.count; - this.manager.emit(this.options.event, this._input); - } - } - }); + addRegexToken('Q', match1); + addParseToken('Q', function (input, array) { + array[MONTH] = (toInt(input) - 1) * 3; + }); - /** - * Simple way to create an manager with a default set of recognizers. - * @param {HTMLElement} element - * @param {Object} [options] - * @constructor - */ - function Hammer(element, options) { - options = options || {}; - options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); - return new Manager(element, options); - } + // MOMENTS - /** - * @const {string} - */ - Hammer.VERSION = '2.0.4'; + function getSetQuarter (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); + } - /** - * default settings - * @namespace - */ - Hammer.defaults = { - /** - * set if DOM events are being triggered. - * But this is slower and unused by simple implementations, so disabled by default. - * @type {Boolean} - * @default false - */ - domEvents: false, + addFormatToken('D', ['DD', 2], 'Do', 'date'); - /** - * The value for the touchAction property/fallback. - * When set to `compute` it will magically set the correct value based on the added recognizers. - * @type {String} - * @default compute - */ - touchAction: TOUCH_ACTION_COMPUTE, + // ALIASES - /** - * @type {Boolean} - * @default true - */ - enable: true, + addUnitAlias('date', 'D'); - /** - * EXPERIMENTAL FEATURE -- can be removed/changed - * Change the parent input target element. - * If Null, then it is being set the to main element. - * @type {Null|EventTarget} - * @default null - */ - inputTarget: null, + // PARSING - /** - * force an input class - * @type {Null|Function} - * @default null - */ - inputClass: null, + addRegexToken('D', match1to2); + addRegexToken('DD', match1to2, match2); + addRegexToken('Do', function (isStrict, locale) { + return isStrict ? locale._ordinalParse : locale._ordinalParseLenient; + }); - /** - * Default recognizer setup when calling `Hammer()` - * When creating a new Manager these will be skipped. - * @type {Array} - */ - preset: [ - // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] - [RotateRecognizer, { enable: false }], - [PinchRecognizer, { enable: false }, ['rotate']], - [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }], - [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], - [TapRecognizer], - [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], - [PressRecognizer] - ], + addParseToken(['D', 'DD'], DATE); + addParseToken('Do', function (input, array) { + array[DATE] = toInt(input.match(match1to2)[0], 10); + }); - /** - * Some CSS properties can be used to improve the working of Hammer. - * Add them to this method and they will be set when creating a new Manager. - * @namespace - */ - cssProps: { - /** - * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. - * @type {String} - * @default 'none' - */ - userSelect: 'none', + // MOMENTS - /** - * Disable the Windows Phone grippers when pressing an element. - * @type {String} - * @default 'none' - */ - touchSelect: 'none', + var getSetDayOfMonth = makeGetSet('Date', true); - /** - * Disables the default callout shown when you touch and hold a touch target. - * On iOS, when you touch and hold a touch target such as a link, Safari displays - * a callout containing information about the link. This property allows you to disable that callout. - * @type {String} - * @default 'none' - */ - touchCallout: 'none', + addFormatToken('d', 0, 'do', 'day'); - /** - * Specifies whether zooming is enabled. Used by IE10> - * @type {String} - * @default 'none' - */ - contentZooming: 'none', + addFormatToken('dd', 0, 0, function (format) { + return this.localeData().weekdaysMin(this, format); + }); - /** - * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. - * @type {String} - * @default 'none' - */ - userDrag: 'none', + addFormatToken('ddd', 0, 0, function (format) { + return this.localeData().weekdaysShort(this, format); + }); - /** - * Overrides the highlight color shown when the user taps a link or a JavaScript - * clickable element in iOS. This property obeys the alpha value, if specified. - * @type {String} - * @default 'rgba(0,0,0,0)' - */ - tapHighlightColor: 'rgba(0,0,0,0)' - } - }; + addFormatToken('dddd', 0, 0, function (format) { + return this.localeData().weekdays(this, format); + }); - var STOP = 1; - var FORCED_STOP = 2; + addFormatToken('e', 0, 0, 'weekday'); + addFormatToken('E', 0, 0, 'isoWeekday'); - /** - * Manager - * @param {HTMLElement} element - * @param {Object} [options] - * @constructor - */ - function Manager(element, options) { - options = options || {}; + // ALIASES - this.options = merge(options, Hammer.defaults); - this.options.inputTarget = this.options.inputTarget || element; + addUnitAlias('day', 'd'); + addUnitAlias('weekday', 'e'); + addUnitAlias('isoWeekday', 'E'); - this.handlers = {}; - this.session = {}; - this.recognizers = []; + // PARSING - this.element = element; - this.input = createInputInstance(this); - this.touchAction = new TouchAction(this, this.options.touchAction); + addRegexToken('d', match1to2); + addRegexToken('e', match1to2); + addRegexToken('E', match1to2); + addRegexToken('dd', matchWord); + addRegexToken('ddd', matchWord); + addRegexToken('dddd', matchWord); - toggleCssProps(this, true); + addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config) { + var weekday = config._locale.weekdaysParse(input); + // if we didn't get a weekday name, mark the date as invalid + if (weekday != null) { + week.d = weekday; + } else { + config._pf.invalidWeekday = input; + } + }); - each(options.recognizers, function(item) { - var recognizer = this.add(new (item[0])(item[1])); - item[2] && recognizer.recognizeWith(item[2]); - item[3] && recognizer.requireFailure(item[3]); - }, this); - } + addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { + week[token] = toInt(input); + }); - Manager.prototype = { - /** - * set options - * @param {Object} options - * @returns {Manager} - */ - set: function(options) { - extend(this.options, options); + // HELPERS - // Options that need a little more setup - if (options.touchAction) { - this.touchAction.update(); - } - if (options.inputTarget) { - // Clean up existing event listeners and reinitialize - this.input.destroy(); - this.input.target = options.inputTarget; - this.input.init(); + function parseWeekday(input, locale) { + if (typeof input === 'string') { + if (!isNaN(input)) { + input = parseInt(input, 10); + } + else { + input = locale.weekdaysParse(input); + if (typeof input !== 'number') { + return null; + } + } } - return this; - }, + return input; + } - /** - * stop recognizing for this session. - * This session will be discarded, when a new [input]start event is fired. - * When forced, the recognizer cycle is stopped immediately. - * @param {Boolean} [force] - */ - stop: function(force) { - this.session.stopped = force ? FORCED_STOP : STOP; - }, + // LOCALES - /** - * run the recognizers! - * called by the inputHandler function on every movement of the pointers (touches) - * it walks through all the recognizers and tries to detect the gesture that is being made - * @param {Object} inputData - */ - recognize: function(inputData) { - var session = this.session; - if (session.stopped) { - return; - } + var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); + function localeWeekdays (m) { + return this._weekdays[m.day()]; + } - // run the touch-action polyfill - this.touchAction.preventDefaults(inputData); + var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); + function localeWeekdaysShort (m) { + return this._weekdaysShort[m.day()]; + } - var recognizer; - var recognizers = this.recognizers; + var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); + function localeWeekdaysMin (m) { + return this._weekdaysMin[m.day()]; + } - // this holds the recognizer that is being recognized. - // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED - // if no recognizer is detecting a thing, it is set to `null` - var curRecognizer = session.curRecognizer; + function localeWeekdaysParse (weekdayName) { + var i, mom, regex; - // reset when the last recognizer is recognized - // or when we're in a new session - if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { - curRecognizer = session.curRecognizer = null; + if (!this._weekdaysParse) { + this._weekdaysParse = []; } - var i = 0; - while (i < recognizers.length) { - recognizer = recognizers[i]; - - // find out if we are allowed try to recognize the input for this one. - // 1. allow if the session is NOT forced stopped (see the .stop() method) - // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one - // that is being recognized. - // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. - // this can be setup with the `recognizeWith()` method on the recognizer. - if (session.stopped !== FORCED_STOP && ( // 1 - !curRecognizer || recognizer == curRecognizer || // 2 - recognizer.canRecognizeWith(curRecognizer))) { // 3 - recognizer.recognize(inputData); - } else { - recognizer.reset(); + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + if (!this._weekdaysParse[i]) { + mom = local__createLocal([2000, 1]).day(i); + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); } - - // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the - // current active recognizer. but only if we don't already have an active recognizer - if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { - curRecognizer = session.curRecognizer = recognizer; + // test the regex + if (this._weekdaysParse[i].test(weekdayName)) { + return i; } - i++; } - }, + } - /** - * get a recognizer by its event name. - * @param {Recognizer|String} recognizer - * @returns {Recognizer|Null} - */ - get: function(recognizer) { - if (recognizer instanceof Recognizer) { - return recognizer; - } + // MOMENTS - var recognizers = this.recognizers; - for (var i = 0; i < recognizers.length; i++) { - if (recognizers[i].options.event == recognizer) { - return recognizers[i]; - } + function getSetDayOfWeek (input) { + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; } - return null; - }, + } - /** - * add a recognizer to the manager - * existing recognizers with the same event name will be removed - * @param {Recognizer} recognizer - * @returns {Recognizer|Manager} - */ - add: function(recognizer) { - if (invokeArrayArg(recognizer, 'add', this)) { - return this; - } + function getSetLocaleDayOfWeek (input) { + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); + } - // remove existing - var existing = this.get(recognizer.options.event); - if (existing) { - this.remove(existing); - } + function getSetISODayOfWeek (input) { + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); + } - this.recognizers.push(recognizer); - recognizer.manager = this; + addFormatToken('H', ['HH', 2], 0, 'hour'); + addFormatToken('h', ['hh', 2], 0, function () { + return this.hours() % 12 || 12; + }); - this.touchAction.update(); - return recognizer; - }, + function meridiem (token, lowercase) { + addFormatToken(token, 0, 0, function () { + return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); + }); + } - /** - * remove a recognizer by name or instance - * @param {Recognizer|String} recognizer - * @returns {Manager} - */ - remove: function(recognizer) { - if (invokeArrayArg(recognizer, 'remove', this)) { - return this; - } + meridiem('a', true); + meridiem('A', false); - var recognizers = this.recognizers; - recognizer = this.get(recognizer); - recognizers.splice(inArray(recognizers, recognizer), 1); + // ALIASES - this.touchAction.update(); - return this; - }, + addUnitAlias('hour', 'h'); - /** - * bind event - * @param {String} events - * @param {Function} handler - * @returns {EventEmitter} this - */ - on: function(events, handler) { - var handlers = this.handlers; - each(splitStr(events), function(event) { - handlers[event] = handlers[event] || []; - handlers[event].push(handler); - }); - return this; - }, + // PARSING - /** - * unbind event, leave emit blank to remove all handlers - * @param {String} events - * @param {Function} [handler] - * @returns {EventEmitter} this - */ - off: function(events, handler) { - var handlers = this.handlers; - each(splitStr(events), function(event) { - if (!handler) { - delete handlers[event]; - } else { - handlers[event].splice(inArray(handlers[event], handler), 1); - } - }); - return this; - }, + function matchMeridiem (isStrict, locale) { + return locale._meridiemParse; + } - /** - * emit event to the listeners - * @param {String} event - * @param {Object} data - */ - emit: function(event, data) { - // we also want to trigger dom events - if (this.options.domEvents) { - triggerDomEvent(event, data); - } + addRegexToken('a', matchMeridiem); + addRegexToken('A', matchMeridiem); + addRegexToken('H', match1to2); + addRegexToken('h', match1to2); + addRegexToken('HH', match1to2, match2); + addRegexToken('hh', match1to2, match2); + + addParseToken(['H', 'HH'], HOUR); + addParseToken(['a', 'A'], function (input, array, config) { + config._isPm = config._locale.isPM(input); + config._meridiem = input; + }); + addParseToken(['h', 'hh'], function (input, array, config) { + array[HOUR] = toInt(input); + config._pf.bigHour = true; + }); - // no handlers, so skip it all - var handlers = this.handlers[event] && this.handlers[event].slice(); - if (!handlers || !handlers.length) { - return; - } + // LOCALES - data.type = event; - data.preventDefault = function() { - data.srcEvent.preventDefault(); - }; + function localeIsPM (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); + } - var i = 0; - while (i < handlers.length) { - handlers[i](data); - i++; + var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; + function localeMeridiem (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; } - }, + } - /** - * destroy the manager and unbinds all events - * it doesn't unbind dom events, that is the user own responsibility - */ - destroy: function() { - this.element && toggleCssProps(this, false); - this.handlers = {}; - this.session = {}; - this.input.destroy(); - this.element = null; - } - }; + // MOMENTS - /** - * add/remove the css properties as defined in manager.options.cssProps - * @param {Manager} manager - * @param {Boolean} add - */ - function toggleCssProps(manager, add) { - var element = manager.element; - each(manager.options.cssProps, function(value, name) { - element.style[prefixed(element.style, name)] = add ? value : ''; - }); - } + // Setting the hour should keep the time, because the user explicitly + // specified which hour he wants. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + var getSetHour = makeGetSet('Hours', true); - /** - * trigger dom event - * @param {String} event - * @param {Object} data - */ - function triggerDomEvent(event, data) { - var gestureEvent = document.createEvent('Event'); - gestureEvent.initEvent(event, true, true); - gestureEvent.gesture = data; - data.target.dispatchEvent(gestureEvent); - } + addFormatToken('m', ['mm', 2], 0, 'minute'); - extend(Hammer, { - INPUT_START: INPUT_START, - INPUT_MOVE: INPUT_MOVE, - INPUT_END: INPUT_END, - INPUT_CANCEL: INPUT_CANCEL, + // ALIASES - STATE_POSSIBLE: STATE_POSSIBLE, - STATE_BEGAN: STATE_BEGAN, - STATE_CHANGED: STATE_CHANGED, - STATE_ENDED: STATE_ENDED, - STATE_RECOGNIZED: STATE_RECOGNIZED, - STATE_CANCELLED: STATE_CANCELLED, - STATE_FAILED: STATE_FAILED, + addUnitAlias('minute', 'm'); - DIRECTION_NONE: DIRECTION_NONE, - DIRECTION_LEFT: DIRECTION_LEFT, - DIRECTION_RIGHT: DIRECTION_RIGHT, - DIRECTION_UP: DIRECTION_UP, - DIRECTION_DOWN: DIRECTION_DOWN, - DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, - DIRECTION_VERTICAL: DIRECTION_VERTICAL, - DIRECTION_ALL: DIRECTION_ALL, + // PARSING - Manager: Manager, - Input: Input, - TouchAction: TouchAction, + addRegexToken('m', match1to2); + addRegexToken('mm', match1to2, match2); + addParseToken(['m', 'mm'], MINUTE); - TouchInput: TouchInput, - MouseInput: MouseInput, - PointerEventInput: PointerEventInput, - TouchMouseInput: TouchMouseInput, - SingleTouchInput: SingleTouchInput, + // MOMENTS - Recognizer: Recognizer, - AttrRecognizer: AttrRecognizer, - Tap: TapRecognizer, - Pan: PanRecognizer, - Swipe: SwipeRecognizer, - Pinch: PinchRecognizer, - Rotate: RotateRecognizer, - Press: PressRecognizer, + var getSetMinute = makeGetSet('Minutes', false); - on: addEventListeners, - off: removeEventListeners, - each: each, - merge: merge, - extend: extend, - inherit: inherit, - bindFn: bindFn, - prefixed: prefixed - }); + addFormatToken('s', ['ss', 2], 0, 'second'); - if ("function" == TYPE_FUNCTION && __webpack_require__(91)) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { - return Hammer; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof module != 'undefined' && module.exports) { - module.exports = Hammer; - } else { - window[exportName] = Hammer; - } + // ALIASES - })(window, document, 'Hammer'); + addUnitAlias('second', 's'); + // PARSING -/***/ }, -/* 73 */ -/***/ function(module, exports, __webpack_require__) { + addRegexToken('s', match1to2); + addRegexToken('ss', match1to2, match2); + addParseToken(['s', 'ss'], SECOND); - 'use strict'; + // MOMENTS - Object.defineProperty(exports, '__esModule', { - value: true - }); + var getSetSecond = makeGetSet('Seconds', false); - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + addFormatToken('S', 0, 0, function () { + return ~~(this.millisecond() / 100); + }); - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + addFormatToken(0, ['SS', 2], 0, function () { + return ~~(this.millisecond() / 10); + }); - var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(49); - var util = __webpack_require__(1); + function millisecond__milliseconds (token) { + addFormatToken(0, [token, 3], 0, 'millisecond'); + } - var ColorPicker = (function () { - function ColorPicker() { - var pixelRatio = arguments[0] === undefined ? 1 : arguments[0]; + millisecond__milliseconds('SSS'); + millisecond__milliseconds('SSSS'); - _classCallCheck(this, ColorPicker); + // ALIASES - this.pixelRatio = pixelRatio; - this.generated = false; - this.centerCoordinates = { x: 289 / 2, y: 289 / 2 }; - this.r = 289 * 0.49; - this.color = { r: 255, g: 255, b: 255, a: 1 }; - this.hueCircle = undefined; - this.initialColor = { r: 255, g: 255, b: 255, a: 1 }; - this.previousColor = undefined; - this.applied = false; + addUnitAlias('millisecond', 'ms'); - // bound by - this.updateCallback = function () {}; + // PARSING - // create all DOM elements - this._create(); - } + addRegexToken('S', match1to3, match1); + addRegexToken('SS', match1to3, match2); + addRegexToken('SSS', match1to3, match3); + addRegexToken('SSSS', matchUnsigned); + addParseToken(['S', 'SS', 'SSS', 'SSSS'], function (input, array) { + array[MILLISECOND] = toInt(('0.' + input) * 1000); + }); - _createClass(ColorPicker, [{ - key: 'insertTo', + // MOMENTS - /** - * this inserts the colorPicker into a div from the DOM - * @param container - */ - value: function insertTo(container) { - if (this.hammer !== undefined) { - this.hammer.destroy(); - this.hammer = undefined; - } - this.container = container; - this.container.appendChild(this.frame); - this._bindHammer(); + var getSetMillisecond = makeGetSet('Milliseconds', false); - this._setSize(); - } - }, { - key: 'setCallback', + addFormatToken('z', 0, 0, 'zoneAbbr'); + addFormatToken('zz', 0, 0, 'zoneName'); - /** - * the callback is executed on apply and save. Bind it to the application - * @param callback - */ - value: function setCallback(callback) { - if (typeof callback === 'function') { - this.updateCallback = callback; - } else { - throw new Error('Function attempted to set as colorPicker callback is not a function.'); - } - } - }, { - key: '_isColorString', - value: function _isColorString(color) { - var htmlColors = { black: '#000000', navy: '#000080', darkblue: '#00008B', mediumblue: '#0000CD', blue: '#0000FF', darkgreen: '#006400', green: '#008000', teal: '#008080', darkcyan: '#008B8B', deepskyblue: '#00BFFF', darkturquoise: '#00CED1', mediumspringgreen: '#00FA9A', lime: '#00FF00', springgreen: '#00FF7F', aqua: '#00FFFF', cyan: '#00FFFF', midnightblue: '#191970', dodgerblue: '#1E90FF', lightseagreen: '#20B2AA', forestgreen: '#228B22', seagreen: '#2E8B57', darkslategray: '#2F4F4F', limegreen: '#32CD32', mediumseagreen: '#3CB371', turquoise: '#40E0D0', royalblue: '#4169E1', steelblue: '#4682B4', darkslateblue: '#483D8B', mediumturquoise: '#48D1CC', indigo: '#4B0082', darkolivegreen: '#556B2F', cadetblue: '#5F9EA0', cornflowerblue: '#6495ED', mediumaquamarine: '#66CDAA', dimgray: '#696969', slateblue: '#6A5ACD', olivedrab: '#6B8E23', slategray: '#708090', lightslategray: '#778899', mediumslateblue: '#7B68EE', lawngreen: '#7CFC00', chartreuse: '#7FFF00', aquamarine: '#7FFFD4', maroon: '#800000', purple: '#800080', olive: '#808000', gray: '#808080', skyblue: '#87CEEB', lightskyblue: '#87CEFA', blueviolet: '#8A2BE2', darkred: '#8B0000', darkmagenta: '#8B008B', saddlebrown: '#8B4513', darkseagreen: '#8FBC8F', lightgreen: '#90EE90', mediumpurple: '#9370D8', darkviolet: '#9400D3', palegreen: '#98FB98', darkorchid: '#9932CC', yellowgreen: '#9ACD32', sienna: '#A0522D', brown: '#A52A2A', darkgray: '#A9A9A9', lightblue: '#ADD8E6', greenyellow: '#ADFF2F', paleturquoise: '#AFEEEE', lightsteelblue: '#B0C4DE', powderblue: '#B0E0E6', firebrick: '#B22222', darkgoldenrod: '#B8860B', mediumorchid: '#BA55D3', rosybrown: '#BC8F8F', darkkhaki: '#BDB76B', silver: '#C0C0C0', mediumvioletred: '#C71585', indianred: '#CD5C5C', peru: '#CD853F', chocolate: '#D2691E', tan: '#D2B48C', lightgrey: '#D3D3D3', palevioletred: '#D87093', thistle: '#D8BFD8', orchid: '#DA70D6', goldenrod: '#DAA520', crimson: '#DC143C', gainsboro: '#DCDCDC', plum: '#DDA0DD', burlywood: '#DEB887', lightcyan: '#E0FFFF', lavender: '#E6E6FA', darksalmon: '#E9967A', violet: '#EE82EE', palegoldenrod: '#EEE8AA', lightcoral: '#F08080', khaki: '#F0E68C', aliceblue: '#F0F8FF', honeydew: '#F0FFF0', azure: '#F0FFFF', sandybrown: '#F4A460', wheat: '#F5DEB3', beige: '#F5F5DC', whitesmoke: '#F5F5F5', mintcream: '#F5FFFA', ghostwhite: '#F8F8FF', salmon: '#FA8072', antiquewhite: '#FAEBD7', linen: '#FAF0E6', lightgoldenrodyellow: '#FAFAD2', oldlace: '#FDF5E6', red: '#FF0000', fuchsia: '#FF00FF', magenta: '#FF00FF', deeppink: '#FF1493', orangered: '#FF4500', tomato: '#FF6347', hotpink: '#FF69B4', coral: '#FF7F50', darkorange: '#FF8C00', lightsalmon: '#FFA07A', orange: '#FFA500', lightpink: '#FFB6C1', pink: '#FFC0CB', gold: '#FFD700', peachpuff: '#FFDAB9', navajowhite: '#FFDEAD', moccasin: '#FFE4B5', bisque: '#FFE4C4', mistyrose: '#FFE4E1', blanchedalmond: '#FFEBCD', papayawhip: '#FFEFD5', lavenderblush: '#FFF0F5', seashell: '#FFF5EE', cornsilk: '#FFF8DC', lemonchiffon: '#FFFACD', floralwhite: '#FFFAF0', snow: '#FFFAFA', yellow: '#FFFF00', lightyellow: '#FFFFE0', ivory: '#FFFFF0', white: '#FFFFFF' }; - if (typeof color === 'string') { - return htmlColors[color]; - } + // MOMENTS + + function getZoneAbbr () { + return this._isUTC ? 'UTC' : ''; } - }, { - key: 'setColor', - /** - * Set the color of the colorPicker - * Supported formats: - * 'red' --> HTML color string - * '#ffffff' --> hex string - * 'rbg(255,255,255)' --> rgb string - * 'rgba(255,255,255,1.0)' --> rgba string - * {r:255,g:255,b:255} --> rgb object - * {r:255,g:255,b:255,a:1.0} --> rgba object - * @param color - * @param setInitial - */ - value: function setColor(color) { - var setInitial = arguments[1] === undefined ? true : arguments[1]; + function getZoneName () { + return this._isUTC ? 'Coordinated Universal Time' : ''; + } - if (color === 'none') { - return; - } + var momentPrototype__proto = Moment.prototype; - var rgba = undefined; + momentPrototype__proto.add = add_subtract__add; + momentPrototype__proto.calendar = moment_calendar__calendar; + momentPrototype__proto.clone = clone; + momentPrototype__proto.diff = diff; + momentPrototype__proto.endOf = endOf; + momentPrototype__proto.format = format; + momentPrototype__proto.from = from; + momentPrototype__proto.fromNow = fromNow; + momentPrototype__proto.get = getSet; + momentPrototype__proto.invalidAt = invalidAt; + momentPrototype__proto.isAfter = isAfter; + momentPrototype__proto.isBefore = isBefore; + momentPrototype__proto.isBetween = isBetween; + momentPrototype__proto.isSame = isSame; + momentPrototype__proto.isValid = moment_valid__isValid; + momentPrototype__proto.lang = lang; + momentPrototype__proto.locale = locale; + momentPrototype__proto.localeData = localeData; + momentPrototype__proto.max = prototypeMax; + momentPrototype__proto.min = prototypeMin; + momentPrototype__proto.parsingFlags = parsingFlags; + momentPrototype__proto.set = getSet; + momentPrototype__proto.startOf = startOf; + momentPrototype__proto.subtract = add_subtract__subtract; + momentPrototype__proto.toArray = toArray; + momentPrototype__proto.toDate = toDate; + momentPrototype__proto.toISOString = moment_format__toISOString; + momentPrototype__proto.toJSON = moment_format__toISOString; + momentPrototype__proto.toString = toString; + momentPrototype__proto.unix = unix; + momentPrototype__proto.valueOf = to_type__valueOf; - // if a html color shorthand is used, convert to hex - var htmlColor = this._isColorString(color); - if (htmlColor !== undefined) { - color = htmlColor; - } + // Year + momentPrototype__proto.year = getSetYear; + momentPrototype__proto.isLeapYear = getIsLeapYear; - // check format - if (util.isString(color) === true) { - if (util.isValidRGB(color) === true) { - var rgbaArray = color.substr(4).substr(0, color.length - 5).split(','); - rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1 }; - } else if (util.isValidRGBA(color) === true) { - var rgbaArray = color.substr(5).substr(0, color.length - 6).split(','); - rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: rgbaArray[3] }; - } else if (util.isValidHex(color) === true) { - var rgbObj = util.hexToRGB(color); - rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1 }; - } - } else { - if (color instanceof Object) { - if (color.r !== undefined && color.g !== undefined && color.b !== undefined) { - var alpha = color.a !== undefined ? color.a : '1.0'; - rgba = { r: color.r, g: color.g, b: color.b, a: alpha }; - } - } - } + // Week Year + momentPrototype__proto.weekYear = getSetWeekYear; + momentPrototype__proto.isoWeekYear = getSetISOWeekYear; - // set color - if (rgba === undefined) { - throw new Error('Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: ' + JSON.stringify(color)); - } else { - this._setColor(rgba, setInitial); - } - } - }, { - key: 'show', + // Quarter + momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter; - /** - * this shows the color picker at a location. The hue circle is constructed once and stored. - * @param x - * @param y - */ - value: function show(x, y) { - this.applied = false; - this.frame.style.display = 'block'; - this.frame.style.top = y + 'px'; - this.frame.style.left = x + 'px'; - this._generateHueCircle(); - } - }, { - key: '_hide', + // Month + momentPrototype__proto.month = getSetMonth; + momentPrototype__proto.daysInMonth = getDaysInMonth; - // ------------------------------------------ PRIVATE ----------------------------- // + // Week + momentPrototype__proto.week = momentPrototype__proto.weeks = getSetWeek; + momentPrototype__proto.isoWeek = momentPrototype__proto.isoWeeks = getSetISOWeek; + momentPrototype__proto.weeksInYear = getWeeksInYear; + momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear; - /** - * Hide the picker. Is called by the cancel button. - * Optional boolean to store the previous color for easy access later on. - * @param storePrevious - * @private - */ - value: function _hide() { - var storePrevious = arguments[0] === undefined ? true : arguments[0]; + // Day + momentPrototype__proto.date = getSetDayOfMonth; + momentPrototype__proto.day = momentPrototype__proto.days = getSetDayOfWeek; + momentPrototype__proto.weekday = getSetLocaleDayOfWeek; + momentPrototype__proto.isoWeekday = getSetISODayOfWeek; + momentPrototype__proto.dayOfYear = getSetDayOfYear; - // store the previous color for next time; - if (storePrevious === true) { - this.previousColor = util.extend({}, this.color); - } + // Hour + momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour; - if (this.applied === true) { - this.updateCallback(this.initialColor); - } + // Minute + momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute; - this.frame.style.display = 'none'; - } - }, { - key: '_save', + // Second + momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond; - /** - * bound to the save button. Saves and hides. - * @private - */ - value: function _save() { - this.updateCallback(this.color); - this.applied = false; - this._hide(); - } - }, { - key: '_apply', + // Millisecond + momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond; - /** - * Bound to apply button. Saves but does not close. Is undone by the cancel button. - * @private - */ - value: function _apply() { - this.applied = true; - this.updateCallback(this.color); - this._updatePicker(this.color); - } - }, { - key: '_loadLast', + // Offset + momentPrototype__proto.utcOffset = getSetOffset; + momentPrototype__proto.utc = setOffsetToUTC; + momentPrototype__proto.local = setOffsetToLocal; + momentPrototype__proto.parseZone = setOffsetToParsedOffset; + momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset; + momentPrototype__proto.isDST = isDaylightSavingTime; + momentPrototype__proto.isDSTShifted = isDaylightSavingTimeShifted; + momentPrototype__proto.isLocal = isLocal; + momentPrototype__proto.isUtcOffset = isUtcOffset; + momentPrototype__proto.isUtc = isUtc; + momentPrototype__proto.isUTC = isUtc; - /** - * load the color from the previous session. - * @private - */ - value: function _loadLast() { - if (this.previousColor !== undefined) { - this.setColor(this.previousColor, false); - } else { - alert('There is no last color to load...'); - } - } - }, { - key: '_setColor', + // Timezone + momentPrototype__proto.zoneAbbr = getZoneAbbr; + momentPrototype__proto.zoneName = getZoneName; - /** - * set the color, place the picker - * @param rgba - * @param setInitial - * @private - */ - value: function _setColor(rgba) { - var setInitial = arguments[1] === undefined ? true : arguments[1]; + // Deprecations + momentPrototype__proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); + momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); + momentPrototype__proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); + momentPrototype__proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779', getSetZone); - // store the initial color - if (setInitial === true) { - this.initialColor = util.extend({}, rgba); - } + var momentPrototype = momentPrototype__proto; - this.color = rgba; - var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + function moment__createUnix (input) { + return local__createLocal(input * 1000); + } - var angleConvert = 2 * Math.PI; - var radius = this.r * hsv.s; - var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h); - var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h); + function moment__createInZone () { + return local__createLocal.apply(null, arguments).parseZone(); + } - this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px'; - this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px'; + var defaultCalendar = { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }; - this._updatePicker(rgba); + function locale_calendar__calendar (key, mom, now) { + var output = this._calendar[key]; + return typeof output === 'function' ? output.call(mom, now) : output; } - }, { - key: '_setOpacity', - /** - * bound to opacity control - * @param value - * @private - */ - value: function _setOpacity(value) { - this.color.a = value / 100; - this._updatePicker(this.color); + var defaultLongDateFormat = { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY LT', + LLLL : 'dddd, MMMM D, YYYY LT' + }; + + function longDateFormat (key) { + var output = this._longDateFormat[key]; + if (!output && this._longDateFormat[key.toUpperCase()]) { + output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + this._longDateFormat[key] = output; + } + return output; } - }, { - key: '_setBrightness', - /** - * bound to brightness control - * @param value - * @private - */ - value: function _setBrightness(value) { - var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); - hsv.v = value / 100; - var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); - rgba['a'] = this.color.a; - this.color = rgba; - this._updatePicker(); - } - }, { - key: '_updatePicker', + var defaultInvalidDate = 'Invalid date'; - /** - * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing. - * @param rgba - * @private - */ - value: function _updatePicker() { - var rgba = arguments[0] === undefined ? this.color : arguments[0]; + function invalidDate () { + return this._invalidDate; + } - var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); - var ctx = this.colorPickerCanvas.getContext('2d'); - if (this.pixelRation === undefined) { - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); - } - ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + var defaultOrdinal = '%d'; + var defaultOrdinalParse = /\d{1,2}/; - // clear the canvas - var w = this.colorPickerCanvas.clientWidth; - var h = this.colorPickerCanvas.clientHeight; - ctx.clearRect(0, 0, w, h); + function ordinal (number) { + return this._ordinal.replace('%d', number); + } - ctx.putImageData(this.hueCircle, 0, 0); - ctx.fillStyle = 'rgba(0,0,0,' + (1 - hsv.v) + ')'; - ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); - ctx.fill(); + function preParsePostFormat (string) { + return string; + } - this.brightnessRange.value = 100 * hsv.v; - this.opacityRange.value = 100 * rgba.a; + var defaultRelativeTime = { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }; - this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; - this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; + function relative__relativeTime (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (typeof output === 'function') ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); } - }, { - key: '_setSize', - /** - * used by create to set the size of the canvas. - * @private - */ - value: function _setSize() { - this.colorPickerCanvas.style.width = '100%'; - this.colorPickerCanvas.style.height = '100%'; + function pastFuture (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); + } - this.colorPickerCanvas.width = 289 * this.pixelRatio; - this.colorPickerCanvas.height = 289 * this.pixelRatio; + function locale_set__set (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (typeof prop === 'function') { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _ordinalParseLenient. + this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); } - }, { - key: '_create', - /** - * create all dom elements - * TODO: cleanup, lots of similar dom elements - * @private - */ - value: function _create() { - this.frame = document.createElement('div'); - this.frame.className = 'vis-color-picker'; + var prototype__proto = Locale.prototype; - this.colorPickerDiv = document.createElement('div'); - this.colorPickerSelector = document.createElement('div'); - this.colorPickerSelector.className = 'vis-selector'; - this.colorPickerDiv.appendChild(this.colorPickerSelector); + prototype__proto._calendar = defaultCalendar; + prototype__proto.calendar = locale_calendar__calendar; + prototype__proto._longDateFormat = defaultLongDateFormat; + prototype__proto.longDateFormat = longDateFormat; + prototype__proto._invalidDate = defaultInvalidDate; + prototype__proto.invalidDate = invalidDate; + prototype__proto._ordinal = defaultOrdinal; + prototype__proto.ordinal = ordinal; + prototype__proto._ordinalParse = defaultOrdinalParse; + prototype__proto.preparse = preParsePostFormat; + prototype__proto.postformat = preParsePostFormat; + prototype__proto._relativeTime = defaultRelativeTime; + prototype__proto.relativeTime = relative__relativeTime; + prototype__proto.pastFuture = pastFuture; + prototype__proto.set = locale_set__set; - this.colorPickerCanvas = document.createElement('canvas'); - this.colorPickerDiv.appendChild(this.colorPickerCanvas); + // Month + prototype__proto.months = localeMonths; + prototype__proto._months = defaultLocaleMonths; + prototype__proto.monthsShort = localeMonthsShort; + prototype__proto._monthsShort = defaultLocaleMonthsShort; + prototype__proto.monthsParse = localeMonthsParse; - if (!this.colorPickerCanvas.getContext) { - var noCanvas = document.createElement('DIV'); - noCanvas.style.color = 'red'; - noCanvas.style.fontWeight = 'bold'; - noCanvas.style.padding = '10px'; - noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; - this.colorPickerCanvas.appendChild(noCanvas); - } else { - var ctx = this.colorPickerCanvas.getContext('2d'); - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); + // Week + prototype__proto.week = localeWeek; + prototype__proto._week = defaultLocaleWeek; + prototype__proto.firstDayOfYear = localeFirstDayOfYear; + prototype__proto.firstDayOfWeek = localeFirstDayOfWeek; - this.colorPickerCanvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); - } + // Day of Week + prototype__proto.weekdays = localeWeekdays; + prototype__proto._weekdays = defaultLocaleWeekdays; + prototype__proto.weekdaysMin = localeWeekdaysMin; + prototype__proto._weekdaysMin = defaultLocaleWeekdaysMin; + prototype__proto.weekdaysShort = localeWeekdaysShort; + prototype__proto._weekdaysShort = defaultLocaleWeekdaysShort; + prototype__proto.weekdaysParse = localeWeekdaysParse; - this.colorPickerDiv.className = 'vis-color'; + // Hours + prototype__proto.isPM = localeIsPM; + prototype__proto._meridiemParse = defaultLocaleMeridiemParse; + prototype__proto.meridiem = localeMeridiem; - this.opacityDiv = document.createElement('div'); - this.opacityDiv.className = 'vis-opacity'; + function lists__get (format, index, field, setter) { + var locale = locale_locales__getLocale(); + var utc = create_utc__createUTC().set(setter, index); + return locale[field](utc, format); + } - this.brightnessDiv = document.createElement('div'); - this.brightnessDiv.className = 'vis-brightness'; + function list (format, index, field, count, setter) { + if (typeof format === 'number') { + index = format; + format = undefined; + } - this.arrowDiv = document.createElement('div'); - this.arrowDiv.className = 'vis-arrow'; + format = format || ''; - this.opacityRange = document.createElement('input'); - this.opacityRange.type = 'range'; - this.opacityRange.min = '0'; - this.opacityRange.max = '100'; - this.opacityRange.value = '100'; - this.opacityRange.className = 'vis-range'; + if (index != null) { + return lists__get(format, index, field, setter); + } - this.brightnessRange = document.createElement('input'); - this.brightnessRange.type = 'range'; - this.brightnessRange.min = '0'; - this.brightnessRange.max = '100'; - this.brightnessRange.value = '100'; - this.brightnessRange.className = 'vis-range'; + var i; + var out = []; + for (i = 0; i < count; i++) { + out[i] = lists__get(format, i, field, setter); + } + return out; + } - this.opacityDiv.appendChild(this.opacityRange); - this.brightnessDiv.appendChild(this.brightnessRange); + function lists__listMonths (format, index) { + return list(format, index, 'months', 12, 'month'); + } - var me = this; - this.opacityRange.onchange = function () { - me._setOpacity(this.value); - }; - this.opacityRange.oninput = function () { - me._setOpacity(this.value); - }; - this.brightnessRange.onchange = function () { - me._setBrightness(this.value); - }; - this.brightnessRange.oninput = function () { - me._setBrightness(this.value); - }; + function lists__listMonthsShort (format, index) { + return list(format, index, 'monthsShort', 12, 'month'); + } - this.brightnessLabel = document.createElement('div'); - this.brightnessLabel.className = 'vis-label vis-brightness'; - this.brightnessLabel.innerHTML = 'brightness:'; + function lists__listWeekdays (format, index) { + return list(format, index, 'weekdays', 7, 'day'); + } - this.opacityLabel = document.createElement('div'); - this.opacityLabel.className = 'vis-label vis-opacity'; - this.opacityLabel.innerHTML = 'opacity:'; + function lists__listWeekdaysShort (format, index) { + return list(format, index, 'weekdaysShort', 7, 'day'); + } - this.newColorDiv = document.createElement('div'); - this.newColorDiv.className = 'vis-new-color'; - this.newColorDiv.innerHTML = 'new'; + function lists__listWeekdaysMin (format, index) { + return list(format, index, 'weekdaysMin', 7, 'day'); + } - this.initialColorDiv = document.createElement('div'); - this.initialColorDiv.className = 'vis-initial-color'; - this.initialColorDiv.innerHTML = 'initial'; + locale_locales__getSetGlobalLocale('en', { + ordinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } + }); - this.cancelButton = document.createElement('div'); - this.cancelButton.className = 'vis-button vis-cancel'; - this.cancelButton.innerHTML = 'cancel'; - this.cancelButton.onclick = this._hide.bind(this, false); + // Side effect imports + utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale); + utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale); - this.applyButton = document.createElement('div'); - this.applyButton.className = 'vis-button vis-apply'; - this.applyButton.innerHTML = 'apply'; - this.applyButton.onclick = this._apply.bind(this); + var mathAbs = Math.abs; - this.saveButton = document.createElement('div'); - this.saveButton.className = 'vis-button vis-save'; - this.saveButton.innerHTML = 'save'; - this.saveButton.onclick = this._save.bind(this); + function duration_abs__abs () { + var data = this._data; - this.loadButton = document.createElement('div'); - this.loadButton.className = 'vis-button vis-load'; - this.loadButton.innerHTML = 'load last'; - this.loadButton.onclick = this._loadLast.bind(this); + this._milliseconds = mathAbs(this._milliseconds); + this._days = mathAbs(this._days); + this._months = mathAbs(this._months); - this.frame.appendChild(this.colorPickerDiv); - this.frame.appendChild(this.arrowDiv); - this.frame.appendChild(this.brightnessLabel); - this.frame.appendChild(this.brightnessDiv); - this.frame.appendChild(this.opacityLabel); - this.frame.appendChild(this.opacityDiv); - this.frame.appendChild(this.newColorDiv); - this.frame.appendChild(this.initialColorDiv); + data.milliseconds = mathAbs(data.milliseconds); + data.seconds = mathAbs(data.seconds); + data.minutes = mathAbs(data.minutes); + data.hours = mathAbs(data.hours); + data.months = mathAbs(data.months); + data.years = mathAbs(data.years); - this.frame.appendChild(this.cancelButton); - this.frame.appendChild(this.applyButton); - this.frame.appendChild(this.saveButton); - this.frame.appendChild(this.loadButton); + return this; } - }, { - key: '_bindHammer', - /** - * bind hammer to the color picker - * @private - */ - value: function _bindHammer() { - var _this = this; + function duration_add_subtract__addSubtract (duration, input, value, direction) { + var other = create__createDuration(input, value); - this.drag = {}; - this.pinch = {}; - this.hammer = new Hammer(this.colorPickerCanvas); - this.hammer.get('pinch').set({ enable: true }); + duration._milliseconds += direction * other._milliseconds; + duration._days += direction * other._days; + duration._months += direction * other._months; - hammerUtil.onTouch(this.hammer, function (event) { - _this._moveSelector(event); - }); - this.hammer.on('tap', function (event) { - _this._moveSelector(event); - }); - this.hammer.on('panstart', function (event) { - _this._moveSelector(event); - }); - this.hammer.on('panmove', function (event) { - _this._moveSelector(event); - }); - this.hammer.on('panend', function (event) { - _this._moveSelector(event); - }); + return duration._bubble(); } - }, { - key: '_generateHueCircle', - /** - * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown. - * @private - */ - value: function _generateHueCircle() { - if (this.generated === false) { - var ctx = this.colorPickerCanvas.getContext('2d'); - if (this.pixelRation === undefined) { - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); - } - ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + // supports only 2.0-style add(1, 's') or add(duration) + function duration_add_subtract__add (input, value) { + return duration_add_subtract__addSubtract(this, input, value, 1); + } - // clear the canvas - var w = this.colorPickerCanvas.clientWidth; - var h = this.colorPickerCanvas.clientHeight; - ctx.clearRect(0, 0, w, h); + // supports only 2.0-style subtract(1, 's') or subtract(duration) + function duration_add_subtract__subtract (input, value) { + return duration_add_subtract__addSubtract(this, input, value, -1); + } - // draw hue circle - var x = undefined, - y = undefined, - hue = undefined, - sat = undefined; - this.centerCoordinates = { x: w * 0.5, y: h * 0.5 }; - this.r = 0.49 * w; - var angleConvert = 2 * Math.PI / 360; - var hfac = 1 / 360; - var sfac = 1 / this.r; - var rgb = undefined; - for (hue = 0; hue < 360; hue++) { - for (sat = 0; sat < this.r; sat++) { - x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue); - y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue); - rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1); - ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; - ctx.fillRect(x - 0.5, y - 0.5, 2, 2); - } - } - ctx.strokeStyle = 'rgba(0,0,0,1)'; - ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); - ctx.stroke(); + function bubble () { + var milliseconds = this._milliseconds; + var days = this._days; + var months = this._months; + var data = this._data; + var seconds, minutes, hours, years = 0; - this.hueCircle = ctx.getImageData(0, 0, w, h); - } - this.generated = true; - } - }, { - key: '_moveSelector', + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; - /** - * move the selector. This is called by hammer functions. - * - * @param event - * @private - */ - value: function _moveSelector(event) { - var rect = this.colorPickerDiv.getBoundingClientRect(); - var left = event.center.x - rect.left; - var top = event.center.y - rect.top; + seconds = absFloor(milliseconds / 1000); + data.seconds = seconds % 60; - var centerY = 0.5 * this.colorPickerDiv.clientHeight; - var centerX = 0.5 * this.colorPickerDiv.clientWidth; + minutes = absFloor(seconds / 60); + data.minutes = minutes % 60; - var x = left - centerX; - var y = top - centerY; + hours = absFloor(minutes / 60); + data.hours = hours % 24; - var angle = Math.atan2(x, y); - var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX); + days += absFloor(hours / 24); - var newTop = Math.cos(angle) * radius + centerY; - var newLeft = Math.sin(angle) * radius + centerX; + // Accurately convert days to years, assume start from year 0. + years = absFloor(daysToYears(days)); + days -= absFloor(yearsToDays(years)); - this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px'; - this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px'; + // 30 days to a month + // TODO (iskren): Use anchor date (like 1st Jan) to compute this. + months += absFloor(days / 30); + days %= 30; - // set color - var h = angle / (2 * Math.PI); - h = h < 0 ? h + 1 : h; - var s = radius / this.r; - var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); - hsv.h = h; - hsv.s = s; - var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); - rgba['a'] = this.color.a; - this.color = rgba; + // 12 months -> 1 year + years += absFloor(months / 12); + months %= 12; - // update previews - this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; - this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; + data.days = days; + data.months = months; + data.years = years; + + return this; } - }]); - return ColorPicker; - })(); + function daysToYears (days) { + // 400 years have 146097 days (taking into account leap year rules) + return days * 400 / 146097; + } - exports['default'] = ColorPicker; - module.exports = exports['default']; + function yearsToDays (years) { + // years * 365 + absFloor(years / 4) - + // absFloor(years / 100) + absFloor(years / 400); + return years * 146097 / 400; + } -/***/ }, -/* 74 */ -/***/ function(module, exports, __webpack_require__) { + function as (units) { + var days; + var months; + var milliseconds = this._milliseconds; + + units = normalizeUnits(units); + + if (units === 'month' || units === 'year') { + days = this._days + milliseconds / 864e5; + months = this._months + daysToYears(days) * 12; + return units === 'month' ? months : months / 12; + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(yearsToDays(this._months / 12)); + switch (units) { + case 'week' : return days / 7 + milliseconds / 6048e5; + case 'day' : return days + milliseconds / 864e5; + case 'hour' : return days * 24 + milliseconds / 36e5; + case 'minute' : return days * 24 * 60 + milliseconds / 6e4; + case 'second' : return days * 24 * 60 * 60 + milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + milliseconds; + default: throw new Error('Unknown unit ' + units); + } + } + } + + // TODO: Use this.as('ms')? + function duration_as__valueOf () { + return ( + this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6 + ); + } - 'use strict'; + function makeAs (alias) { + return function () { + return this.as(alias); + }; + } - Object.defineProperty(exports, '__esModule', { - value: true - }); + var asMilliseconds = makeAs('ms'); + var asSeconds = makeAs('s'); + var asMinutes = makeAs('m'); + var asHours = makeAs('h'); + var asDays = makeAs('d'); + var asWeeks = makeAs('w'); + var asMonths = makeAs('M'); + var asYears = makeAs('y'); - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + function duration_get__get (units) { + units = normalizeUnits(units); + return this[units + 's'](); + } - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + function makeGetter(name) { + return function () { + return this._data[name]; + }; + } - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + var duration_get__milliseconds = makeGetter('milliseconds'); + var seconds = makeGetter('seconds'); + var minutes = makeGetter('minutes'); + var hours = makeGetter('hours'); + var days = makeGetter('days'); + var months = makeGetter('months'); + var years = makeGetter('years'); - var _Label = __webpack_require__(75); + function weeks () { + return absFloor(this.days() / 7); + } - var _Label2 = _interopRequireDefault(_Label); + var round = Math.round; + var thresholds = { + s: 45, // seconds to minute + m: 45, // minutes to hour + h: 22, // hours to day + d: 26, // days to month + M: 11 // months to year + }; - var _Box = __webpack_require__(92); + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); + } - var _Box2 = _interopRequireDefault(_Box); + function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) { + var duration = create__createDuration(posNegDuration).abs(); + var seconds = round(duration.as('s')); + var minutes = round(duration.as('m')); + var hours = round(duration.as('h')); + var days = round(duration.as('d')); + var months = round(duration.as('M')); + var years = round(duration.as('y')); - var _Circle = __webpack_require__(93); + var a = seconds < thresholds.s && ['s', seconds] || + minutes === 1 && ['m'] || + minutes < thresholds.m && ['mm', minutes] || + hours === 1 && ['h'] || + hours < thresholds.h && ['hh', hours] || + days === 1 && ['d'] || + days < thresholds.d && ['dd', days] || + months === 1 && ['M'] || + months < thresholds.M && ['MM', months] || + years === 1 && ['y'] || ['yy', years]; - var _Circle2 = _interopRequireDefault(_Circle); + a[2] = withoutSuffix; + a[3] = +posNegDuration > 0; + a[4] = locale; + return substituteTimeAgo.apply(null, a); + } - var _CircularImage = __webpack_require__(94); + // This function allows you to set a threshold for relative time strings + function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) { + if (thresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return thresholds[threshold]; + } + thresholds[threshold] = limit; + return true; + } - var _CircularImage2 = _interopRequireDefault(_CircularImage); + function humanize (withSuffix) { + var locale = this.localeData(); + var output = duration_humanize__relativeTime(this, !withSuffix, locale); - var _Database = __webpack_require__(95); + if (withSuffix) { + output = locale.pastFuture(+this, output); + } - var _Database2 = _interopRequireDefault(_Database); + return locale.postformat(output); + } - var _Diamond = __webpack_require__(96); + var iso_string__abs = Math.abs; - var _Diamond2 = _interopRequireDefault(_Diamond); + function iso_string__toISOString() { + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var Y = iso_string__abs(this.years()); + var M = iso_string__abs(this.months()); + var D = iso_string__abs(this.days()); + var h = iso_string__abs(this.hours()); + var m = iso_string__abs(this.minutes()); + var s = iso_string__abs(this.seconds() + this.milliseconds() / 1000); + var total = this.asSeconds(); - var _Dot = __webpack_require__(97); + if (!total) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } - var _Dot2 = _interopRequireDefault(_Dot); + return (total < 0 ? '-' : '') + + 'P' + + (Y ? Y + 'Y' : '') + + (M ? M + 'M' : '') + + (D ? D + 'D' : '') + + ((h || m || s) ? 'T' : '') + + (h ? h + 'H' : '') + + (m ? m + 'M' : '') + + (s ? s + 'S' : ''); + } - var _Ellipse = __webpack_require__(98); + var duration_prototype__proto = Duration.prototype; - var _Ellipse2 = _interopRequireDefault(_Ellipse); + duration_prototype__proto.abs = duration_abs__abs; + duration_prototype__proto.add = duration_add_subtract__add; + duration_prototype__proto.subtract = duration_add_subtract__subtract; + duration_prototype__proto.as = as; + duration_prototype__proto.asMilliseconds = asMilliseconds; + duration_prototype__proto.asSeconds = asSeconds; + duration_prototype__proto.asMinutes = asMinutes; + duration_prototype__proto.asHours = asHours; + duration_prototype__proto.asDays = asDays; + duration_prototype__proto.asWeeks = asWeeks; + duration_prototype__proto.asMonths = asMonths; + duration_prototype__proto.asYears = asYears; + duration_prototype__proto.valueOf = duration_as__valueOf; + duration_prototype__proto._bubble = bubble; + duration_prototype__proto.get = duration_get__get; + duration_prototype__proto.milliseconds = duration_get__milliseconds; + duration_prototype__proto.seconds = seconds; + duration_prototype__proto.minutes = minutes; + duration_prototype__proto.hours = hours; + duration_prototype__proto.days = days; + duration_prototype__proto.weeks = weeks; + duration_prototype__proto.months = months; + duration_prototype__proto.years = years; + duration_prototype__proto.humanize = humanize; + duration_prototype__proto.toISOString = iso_string__toISOString; + duration_prototype__proto.toString = iso_string__toISOString; + duration_prototype__proto.toJSON = iso_string__toISOString; + duration_prototype__proto.locale = locale; + duration_prototype__proto.localeData = localeData; - var _Icon = __webpack_require__(99); + // Deprecations + duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString); + duration_prototype__proto.lang = lang; - var _Icon2 = _interopRequireDefault(_Icon); + // Side effect imports - var _Image = __webpack_require__(100); + addFormatToken('X', 0, 0, 'unix'); + addFormatToken('x', 0, 0, 'valueOf'); - var _Image2 = _interopRequireDefault(_Image); + // PARSING - var _Square = __webpack_require__(101); + addRegexToken('x', matchSigned); + addRegexToken('X', matchTimestamp); + addParseToken('X', function (input, array, config) { + config._d = new Date(parseFloat(input, 10) * 1000); + }); + addParseToken('x', function (input, array, config) { + config._d = new Date(toInt(input)); + }); - var _Square2 = _interopRequireDefault(_Square); + // Side effect imports - var _Star = __webpack_require__(102); - var _Star2 = _interopRequireDefault(_Star); + utils_hooks__hooks.version = '2.10.2'; - var _Text = __webpack_require__(103); + setHookCallback(local__createLocal); - var _Text2 = _interopRequireDefault(_Text); + utils_hooks__hooks.fn = momentPrototype; + utils_hooks__hooks.min = min; + utils_hooks__hooks.max = max; + utils_hooks__hooks.utc = create_utc__createUTC; + utils_hooks__hooks.unix = moment__createUnix; + utils_hooks__hooks.months = lists__listMonths; + utils_hooks__hooks.isDate = isDate; + utils_hooks__hooks.locale = locale_locales__getSetGlobalLocale; + utils_hooks__hooks.invalid = valid__createInvalid; + utils_hooks__hooks.duration = create__createDuration; + utils_hooks__hooks.isMoment = isMoment; + utils_hooks__hooks.weekdays = lists__listWeekdays; + utils_hooks__hooks.parseZone = moment__createInZone; + utils_hooks__hooks.localeData = locale_locales__getLocale; + utils_hooks__hooks.isDuration = isDuration; + utils_hooks__hooks.monthsShort = lists__listMonthsShort; + utils_hooks__hooks.weekdaysMin = lists__listWeekdaysMin; + utils_hooks__hooks.defineLocale = defineLocale; + utils_hooks__hooks.weekdaysShort = lists__listWeekdaysShort; + utils_hooks__hooks.normalizeUnits = normalizeUnits; + utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold; - var _Triangle = __webpack_require__(104); + var _moment = utils_hooks__hooks; - var _Triangle2 = _interopRequireDefault(_Triangle); + return _moment; - var _TriangleDown = __webpack_require__(105); + })); + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(108)(module))) - var _TriangleDown2 = _interopRequireDefault(_TriangleDown); +/***/ }, +/* 73 */ +/***/ function(module, exports, __webpack_require__) { - var _Validator = __webpack_require__(46); + 'use strict'; - var _Validator2 = _interopRequireDefault(_Validator); + Object.defineProperty(exports, '__esModule', { + value: true + }); - var util = __webpack_require__(1); + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - /** - * @class Node - * A node. A node can be connected to other nodes via one or multiple edges. - * @param {object} options An object containing options for the node. All - * options are optional, except for the id. - * {number} id Id of the node. Required - * {string} label Text label for the node - * {number} x Horizontal position of the node - * {number} y Vertical position of the node - * {string} shape Node shape, available: - * "database", "circle", "ellipse", - * "box", "image", "text", "dot", - * "star", "triangle", "triangleDown", - * "square", "icon" - * {string} image An image url - * {string} title An title text, can be HTML - * {anytype} group A group name or number - * @param {Network.Images} imagelist A list with images. Only needed - * when the node has an image - * @param {Network.Groups} grouplist A list with groups. Needed for - * retrieving group options - * @param {Object} constants An object with default values for - * example for the color - * - */ + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var Node = (function () { - function Node(options, body, imagelist, grouplist, globalOptions) { - _classCallCheck(this, Node); + var Hammer = __webpack_require__(41); + var hammerUtil = __webpack_require__(48); + var util = __webpack_require__(1); - this.options = util.bridgeObject(globalOptions); - this.body = body; + var ColorPicker = (function () { + function ColorPicker() { + var pixelRatio = arguments[0] === undefined ? 1 : arguments[0]; - this.edges = []; // all edges connected to this node + _classCallCheck(this, ColorPicker); - // set defaults for the options - this.id = undefined; - this.imagelist = imagelist; - this.grouplist = grouplist; + this.pixelRatio = pixelRatio; + this.generated = false; + this.centerCoordinates = { x: 289 / 2, y: 289 / 2 }; + this.r = 289 * 0.49; + this.color = { r: 255, g: 255, b: 255, a: 1 }; + this.hueCircle = undefined; + this.initialColor = { r: 255, g: 255, b: 255, a: 1 }; + this.previousColor = undefined; + this.applied = false; - // state options - this.x = undefined; - this.y = undefined; - this.baseSize = this.options.size; - this.baseFontSize = this.options.font.size; - this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate - this.selected = false; - this.hover = false; + // bound by + this.updateCallback = function () {}; - this.labelModule = new _Label2['default'](this.body, this.options); - this.setOptions(options); + // create all DOM elements + this._create(); } - _createClass(Node, [{ - key: 'attachEdge', + _createClass(ColorPicker, [{ + key: 'insertTo', /** - * Attach a edge to the node - * @param {Edge} edge + * this inserts the colorPicker into a div from the DOM + * @param container */ - value: function attachEdge(edge) { - if (this.edges.indexOf(edge) === -1) { - this.edges.push(edge); + value: function insertTo(container) { + if (this.hammer !== undefined) { + this.hammer.destroy(); + this.hammer = undefined; } + this.container = container; + this.container.appendChild(this.frame); + this._bindHammer(); + + this._setSize(); } }, { - key: 'detachEdge', + key: 'setCallback', /** - * Detach a edge from the node - * @param {Edge} edge + * the callback is executed on apply and save. Bind it to the application + * @param callback */ - value: function detachEdge(edge) { - var index = this.edges.indexOf(edge); - if (index != -1) { - this.edges.splice(index, 1); + value: function setCallback(callback) { + if (typeof callback === 'function') { + this.updateCallback = callback; + } else { + throw new Error('Function attempted to set as colorPicker callback is not a function.'); } } }, { - key: 'togglePhysics', - - /** - * Enable or disable the physics. - * @param status - */ - value: function togglePhysics(status) { - this.options.physics = status; + key: '_isColorString', + value: function _isColorString(color) { + var htmlColors = { black: '#000000', navy: '#000080', darkblue: '#00008B', mediumblue: '#0000CD', blue: '#0000FF', darkgreen: '#006400', green: '#008000', teal: '#008080', darkcyan: '#008B8B', deepskyblue: '#00BFFF', darkturquoise: '#00CED1', mediumspringgreen: '#00FA9A', lime: '#00FF00', springgreen: '#00FF7F', aqua: '#00FFFF', cyan: '#00FFFF', midnightblue: '#191970', dodgerblue: '#1E90FF', lightseagreen: '#20B2AA', forestgreen: '#228B22', seagreen: '#2E8B57', darkslategray: '#2F4F4F', limegreen: '#32CD32', mediumseagreen: '#3CB371', turquoise: '#40E0D0', royalblue: '#4169E1', steelblue: '#4682B4', darkslateblue: '#483D8B', mediumturquoise: '#48D1CC', indigo: '#4B0082', darkolivegreen: '#556B2F', cadetblue: '#5F9EA0', cornflowerblue: '#6495ED', mediumaquamarine: '#66CDAA', dimgray: '#696969', slateblue: '#6A5ACD', olivedrab: '#6B8E23', slategray: '#708090', lightslategray: '#778899', mediumslateblue: '#7B68EE', lawngreen: '#7CFC00', chartreuse: '#7FFF00', aquamarine: '#7FFFD4', maroon: '#800000', purple: '#800080', olive: '#808000', gray: '#808080', skyblue: '#87CEEB', lightskyblue: '#87CEFA', blueviolet: '#8A2BE2', darkred: '#8B0000', darkmagenta: '#8B008B', saddlebrown: '#8B4513', darkseagreen: '#8FBC8F', lightgreen: '#90EE90', mediumpurple: '#9370D8', darkviolet: '#9400D3', palegreen: '#98FB98', darkorchid: '#9932CC', yellowgreen: '#9ACD32', sienna: '#A0522D', brown: '#A52A2A', darkgray: '#A9A9A9', lightblue: '#ADD8E6', greenyellow: '#ADFF2F', paleturquoise: '#AFEEEE', lightsteelblue: '#B0C4DE', powderblue: '#B0E0E6', firebrick: '#B22222', darkgoldenrod: '#B8860B', mediumorchid: '#BA55D3', rosybrown: '#BC8F8F', darkkhaki: '#BDB76B', silver: '#C0C0C0', mediumvioletred: '#C71585', indianred: '#CD5C5C', peru: '#CD853F', chocolate: '#D2691E', tan: '#D2B48C', lightgrey: '#D3D3D3', palevioletred: '#D87093', thistle: '#D8BFD8', orchid: '#DA70D6', goldenrod: '#DAA520', crimson: '#DC143C', gainsboro: '#DCDCDC', plum: '#DDA0DD', burlywood: '#DEB887', lightcyan: '#E0FFFF', lavender: '#E6E6FA', darksalmon: '#E9967A', violet: '#EE82EE', palegoldenrod: '#EEE8AA', lightcoral: '#F08080', khaki: '#F0E68C', aliceblue: '#F0F8FF', honeydew: '#F0FFF0', azure: '#F0FFFF', sandybrown: '#F4A460', wheat: '#F5DEB3', beige: '#F5F5DC', whitesmoke: '#F5F5F5', mintcream: '#F5FFFA', ghostwhite: '#F8F8FF', salmon: '#FA8072', antiquewhite: '#FAEBD7', linen: '#FAF0E6', lightgoldenrodyellow: '#FAFAD2', oldlace: '#FDF5E6', red: '#FF0000', fuchsia: '#FF00FF', magenta: '#FF00FF', deeppink: '#FF1493', orangered: '#FF4500', tomato: '#FF6347', hotpink: '#FF69B4', coral: '#FF7F50', darkorange: '#FF8C00', lightsalmon: '#FFA07A', orange: '#FFA500', lightpink: '#FFB6C1', pink: '#FFC0CB', gold: '#FFD700', peachpuff: '#FFDAB9', navajowhite: '#FFDEAD', moccasin: '#FFE4B5', bisque: '#FFE4C4', mistyrose: '#FFE4E1', blanchedalmond: '#FFEBCD', papayawhip: '#FFEFD5', lavenderblush: '#FFF0F5', seashell: '#FFF5EE', cornsilk: '#FFF8DC', lemonchiffon: '#FFFACD', floralwhite: '#FFFAF0', snow: '#FFFAFA', yellow: '#FFFF00', lightyellow: '#FFFFE0', ivory: '#FFFFF0', white: '#FFFFFF' }; + if (typeof color === 'string') { + return htmlColors[color]; + } } }, { - key: 'setOptions', + key: 'setColor', /** - * Set or overwrite options for the node - * @param {Object} options an object with options - * @param {Object} constants and object with default, global options + * Set the color of the colorPicker + * Supported formats: + * 'red' --> HTML color string + * '#ffffff' --> hex string + * 'rbg(255,255,255)' --> rgb string + * 'rgba(255,255,255,1.0)' --> rgba string + * {r:255,g:255,b:255} --> rgb object + * {r:255,g:255,b:255,a:1.0} --> rgba object + * @param color + * @param setInitial */ - value: function setOptions(options) { - if (!options) { - return; - } - - // basic options - if (options.id !== undefined) { - this.id = options.id; - } - - if (this.id === undefined) { - throw 'Node must have an id'; - } + value: function setColor(color) { + var setInitial = arguments[1] === undefined ? true : arguments[1]; - if (options.x !== undefined) { - this.x = parseInt(options.x);this.predefinedPosition = true; - } - if (options.y !== undefined) { - this.y = parseInt(options.y);this.predefinedPosition = true; - } - if (options.size !== undefined) { - this.baseSize = options.size; - } - if (options.value !== undefined) { - options.value = parseInt(options.value); + if (color === 'none') { + return; } - // this transforms all shorthands into fully defined options - Node.parseOptions(this.options, options, true); + var rgba = undefined; - // copy group options - if (typeof options.group === 'number' || typeof options.group === 'string' && options.group != '') { - var groupObj = this.grouplist.get(options.group); - util.deepExtend(this.options, groupObj); - // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case. - this.options.color = util.parseColor(this.options.color); + // if a html color shorthand is used, convert to hex + var htmlColor = this._isColorString(color); + if (htmlColor !== undefined) { + color = htmlColor; } - // load the images - if (this.options.image !== undefined && this.options.image != '') { - if (this.imagelist) { - this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); - } else { - throw 'No imagelist provided'; + // check format + if (util.isString(color) === true) { + if (util.isValidRGB(color) === true) { + var rgbaArray = color.substr(4).substr(0, color.length - 5).split(','); + rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1 }; + } else if (util.isValidRGBA(color) === true) { + var rgbaArray = color.substr(5).substr(0, color.length - 6).split(','); + rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: rgbaArray[3] }; + } else if (util.isValidHex(color) === true) { + var rgbObj = util.hexToRGB(color); + rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1 }; + } + } else { + if (color instanceof Object) { + if (color.r !== undefined && color.g !== undefined && color.b !== undefined) { + var alpha = color.a !== undefined ? color.a : '1.0'; + rgba = { r: color.r, g: color.g, b: color.b, a: alpha }; + } } } - this.updateShape(); - this.updateLabelModule(); - - // reset the size of the node, this can be changed - this._reset(); - } - }, { - key: 'updateLabelModule', - value: function updateLabelModule() { - if (this.options.label === undefined || this.options.label === null) { - this.options.label = ''; - } - this.labelModule.setOptions(this.options, true); - if (this.labelModule.baseSize !== undefined) { - this.baseFontSize = this.labelModule.baseSize; - } - } - }, { - key: 'updateShape', - value: function updateShape() { - // choose draw method depending on the shape - switch (this.options.shape) { - case 'box': - this.shape = new _Box2['default'](this.options, this.body, this.labelModule); - break; - case 'circle': - this.shape = new _Circle2['default'](this.options, this.body, this.labelModule); - break; - case 'circularImage': - this.shape = new _CircularImage2['default'](this.options, this.body, this.labelModule, this.imageObj); - break; - case 'database': - this.shape = new _Database2['default'](this.options, this.body, this.labelModule); - break; - case 'diamond': - this.shape = new _Diamond2['default'](this.options, this.body, this.labelModule); - break; - case 'dot': - this.shape = new _Dot2['default'](this.options, this.body, this.labelModule); - break; - case 'ellipse': - this.shape = new _Ellipse2['default'](this.options, this.body, this.labelModule); - break; - case 'icon': - this.shape = new _Icon2['default'](this.options, this.body, this.labelModule); - break; - case 'image': - this.shape = new _Image2['default'](this.options, this.body, this.labelModule, this.imageObj); - break; - case 'square': - this.shape = new _Square2['default'](this.options, this.body, this.labelModule); - break; - case 'star': - this.shape = new _Star2['default'](this.options, this.body, this.labelModule); - break; - case 'text': - this.shape = new _Text2['default'](this.options, this.body, this.labelModule); - break; - case 'triangle': - this.shape = new _Triangle2['default'](this.options, this.body, this.labelModule); - break; - case 'triangleDown': - this.shape = new _TriangleDown2['default'](this.options, this.body, this.labelModule); - break; - default: - this.shape = new _Ellipse2['default'](this.options, this.body, this.labelModule); - break; + // set color + if (rgba === undefined) { + throw new Error('Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: ' + JSON.stringify(color)); + } else { + this._setColor(rgba, setInitial); } - this._reset(); } }, { - key: 'select', + key: 'show', /** - * select this node + * this shows the color picker at a location. The hue circle is constructed once and stored. + * @param x + * @param y */ - value: function select() { - this.selected = true; - this._reset(); + value: function show(x, y) { + this.applied = false; + this.frame.style.display = 'block'; + this.frame.style.top = y + 'px'; + this.frame.style.left = x + 'px'; + this._generateHueCircle(); } }, { - key: 'unselect', + key: '_hide', - /** - * unselect this node - */ - value: function unselect() { - this.selected = false; - this._reset(); - } - }, { - key: '_reset', + // ------------------------------------------ PRIVATE ----------------------------- // /** - * Reset the calculated size of the node, forces it to recalculate its size + * Hide the picker. Is called by the cancel button. + * Optional boolean to store the previous color for easy access later on. + * @param storePrevious * @private */ - value: function _reset() { - this.shape.width = undefined; - this.shape.height = undefined; - } - }, { - key: 'getTitle', - - /** - * get the title of this node. - * @return {string} title The title of the node, or undefined when no title - * has been set. - */ - value: function getTitle() { - return this.options.title; - } - }, { - key: 'distanceToBorder', + value: function _hide() { + var storePrevious = arguments[0] === undefined ? true : arguments[0]; - /** - * Calculate the distance to the border of the Node - * @param {CanvasRenderingContext2D} ctx - * @param {Number} angle Angle in radians - * @returns {number} distance Distance to the border in pixels - */ - value: function distanceToBorder(ctx, angle) { - return this.shape.distanceToBorder(ctx, angle); - } - }, { - key: 'isFixed', + // store the previous color for next time; + if (storePrevious === true) { + this.previousColor = util.extend({}, this.color); + } - /** - * Check if this node has a fixed x and y position - * @return {boolean} true if fixed, false if not - */ - value: function isFixed() { - return this.options.fixed.x && this.options.fixed.y; + if (this.applied === true) { + this.updateCallback(this.initialColor); + } + + this.frame.style.display = 'none'; } }, { - key: 'isSelected', + key: '_save', /** - * check if this node is selecte - * @return {boolean} selected True if node is selected, else false + * bound to the save button. Saves and hides. + * @private */ - value: function isSelected() { - return this.selected; + value: function _save() { + this.updateCallback(this.color); + this.applied = false; + this._hide(); } }, { - key: 'getValue', + key: '_apply', /** - * Retrieve the value of the node. Can be undefined - * @return {Number} value + * Bound to apply button. Saves but does not close. Is undone by the cancel button. + * @private */ - value: function getValue() { - return this.options.value; + value: function _apply() { + this.applied = true; + this.updateCallback(this.color); + this._updatePicker(this.color); } }, { - key: 'setValueRange', + key: '_loadLast', /** - * Adjust the value range of the node. The node will adjust it's size - * based on its value. - * @param {Number} min - * @param {Number} max + * load the color from the previous session. + * @private */ - value: function setValueRange(min, max, total) { - if (this.options.value !== undefined) { - var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); - var sizeDiff = this.options.scaling.max - this.options.scaling.min; - if (this.options.scaling.label.enabled === true) { - var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; - this.options.font.size = this.options.scaling.label.min + scale * fontDiff; - } - this.options.size = this.options.scaling.min + scale * sizeDiff; + value: function _loadLast() { + if (this.previousColor !== undefined) { + this.setColor(this.previousColor, false); } else { - this.options.size = this.baseSize; - this.options.font.size = this.baseFontSize; + alert('There is no last color to load...'); } } }, { - key: 'draw', + key: '_setColor', /** - * Draw this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx + * set the color, place the picker + * @param rgba + * @param setInitial + * @private */ - value: function draw(ctx) { - this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); + value: function _setColor(rgba) { + var setInitial = arguments[1] === undefined ? true : arguments[1]; + + // store the initial color + if (setInitial === true) { + this.initialColor = util.extend({}, rgba); + } + + this.color = rgba; + var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + + var angleConvert = 2 * Math.PI; + var radius = this.r * hsv.s; + var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h); + var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h); + + this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px'; + this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px'; + + this._updatePicker(rgba); } }, { - key: 'updateBoundingBox', + key: '_setOpacity', /** - * Update the bounding box of the shape + * bound to opacity control + * @param value + * @private */ - value: function updateBoundingBox() { - this.shape.updateBoundingBox(this.x, this.y); + value: function _setOpacity(value) { + this.color.a = value / 100; + this._updatePicker(this.color); } }, { - key: 'resize', + key: '_setBrightness', /** - * Recalculate the size of this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx + * bound to brightness control + * @param value + * @private */ - value: function resize(ctx) { - this.shape.resize(ctx); + value: function _setBrightness(value) { + var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); + hsv.v = value / 100; + var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); + rgba['a'] = this.color.a; + this.color = rgba; + this._updatePicker(); } }, { - key: 'isOverlappingWith', + key: '_updatePicker', /** - * Check if this object is overlapping with the provided object - * @param {Object} obj an object with parameters left, top, right, bottom - * @return {boolean} True if location is located on node + * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing. + * @param rgba + * @private */ - value: function isOverlappingWith(obj) { - return this.shape.left < obj.right && this.shape.left + this.shape.width > obj.left && this.shape.top < obj.bottom && this.shape.top + this.shape.height > obj.top; + value: function _updatePicker() { + var rgba = arguments[0] === undefined ? this.color : arguments[0]; + + var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + var ctx = this.colorPickerCanvas.getContext('2d'); + if (this.pixelRation === undefined) { + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); + } + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + + // clear the canvas + var w = this.colorPickerCanvas.clientWidth; + var h = this.colorPickerCanvas.clientHeight; + ctx.clearRect(0, 0, w, h); + + ctx.putImageData(this.hueCircle, 0, 0); + ctx.fillStyle = 'rgba(0,0,0,' + (1 - hsv.v) + ')'; + ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); + ctx.fill(); + + this.brightnessRange.value = 100 * hsv.v; + this.opacityRange.value = 100 * rgba.a; + + this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; + this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; } }, { - key: 'isBoundingBoxOverlappingWith', + key: '_setSize', /** - * Check if this object is overlapping with the provided object - * @param {Object} obj an object with parameters left, top, right, bottom - * @return {boolean} True if location is located on node + * used by create to set the size of the canvas. + * @private */ - value: function isBoundingBoxOverlappingWith(obj) { - return this.shape.boundingBox.left < obj.right && this.shape.boundingBox.right > obj.left && this.shape.boundingBox.top < obj.bottom && this.shape.boundingBox.bottom > obj.top; + value: function _setSize() { + this.colorPickerCanvas.style.width = '100%'; + this.colorPickerCanvas.style.height = '100%'; + + this.colorPickerCanvas.width = 289 * this.pixelRatio; + this.colorPickerCanvas.height = 289 * this.pixelRatio; } - }], [{ - key: 'parseOptions', + }, { + key: '_create', /** - * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. - * Static so it can also be used by the handler. - * @param parentOptions - * @param newOptions + * create all dom elements + * TODO: cleanup, lots of similar dom elements + * @private */ - value: function parseOptions(parentOptions, newOptions) { - var allowDeletion = arguments[2] === undefined ? false : arguments[2]; + value: function _create() { + this.frame = document.createElement('div'); + this.frame.className = 'vis-color-picker'; - var fields = ['color', 'fixed', 'shadow']; - util.selectiveNotDeepExtend(fields, parentOptions, newOptions); + this.colorPickerDiv = document.createElement('div'); + this.colorPickerSelector = document.createElement('div'); + this.colorPickerSelector.className = 'vis-selector'; + this.colorPickerDiv.appendChild(this.colorPickerSelector); - // merge the shadow options into the parent. - util.mergeOptions(parentOptions, newOptions, 'shadow'); + this.colorPickerCanvas = document.createElement('canvas'); + this.colorPickerDiv.appendChild(this.colorPickerCanvas); - // individual shape newOptions - if (newOptions.color !== undefined && newOptions.color !== null) { - var parsedColor = util.parseColor(newOptions.color); - util.fillIfDefined(parentOptions.color, parsedColor); - } else if (allowDeletion === true) { - parentOptions.color = undefined; - delete parentOptions.color; - } + if (!this.colorPickerCanvas.getContext) { + var noCanvas = document.createElement('DIV'); + noCanvas.style.color = 'red'; + noCanvas.style.fontWeight = 'bold'; + noCanvas.style.padding = '10px'; + noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; + this.colorPickerCanvas.appendChild(noCanvas); + } else { + var ctx = this.colorPickerCanvas.getContext('2d'); + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); - if (newOptions.fixed !== undefined && newOptions.fixed !== null) { - if (typeof newOptions.fixed === 'boolean') { - parentOptions.fixed.x = newOptions.fixed; - parentOptions.fixed.y = newOptions.fixed; - } else { - if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') { - parentOptions.fixed.x = newOptions.fixed.x; - } - if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') { - parentOptions.fixed.y = newOptions.fixed.y; - } - } + this.colorPickerCanvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } - } - }]); - - return Node; - })(); - - exports['default'] = Node; - module.exports = exports['default']; - -/***/ }, -/* 75 */ -/***/ function(module, exports, __webpack_require__) { - 'use strict'; + this.colorPickerDiv.className = 'vis-color'; - Object.defineProperty(exports, '__esModule', { - value: true - }); + this.opacityDiv = document.createElement('div'); + this.opacityDiv.className = 'vis-opacity'; - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + this.brightnessDiv = document.createElement('div'); + this.brightnessDiv.className = 'vis-brightness'; - function _slicedToArray(arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } } + this.arrowDiv = document.createElement('div'); + this.arrowDiv.className = 'vis-arrow'; - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + this.opacityRange = document.createElement('input'); + this.opacityRange.type = 'range'; + this.opacityRange.min = '0'; + this.opacityRange.max = '100'; + this.opacityRange.value = '100'; + this.opacityRange.className = 'vis-range'; - var util = __webpack_require__(1); + this.brightnessRange = document.createElement('input'); + this.brightnessRange.type = 'range'; + this.brightnessRange.min = '0'; + this.brightnessRange.max = '100'; + this.brightnessRange.value = '100'; + this.brightnessRange.className = 'vis-range'; - var Label = (function () { - function Label(body, options) { - _classCallCheck(this, Label); + this.opacityDiv.appendChild(this.opacityRange); + this.brightnessDiv.appendChild(this.brightnessRange); - this.body = body; + var me = this; + this.opacityRange.onchange = function () { + me._setOpacity(this.value); + }; + this.opacityRange.oninput = function () { + me._setOpacity(this.value); + }; + this.brightnessRange.onchange = function () { + me._setBrightness(this.value); + }; + this.brightnessRange.oninput = function () { + me._setBrightness(this.value); + }; - this.baseSize = undefined; - this.setOptions(options); - this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 }; // could be cached - } + this.brightnessLabel = document.createElement('div'); + this.brightnessLabel.className = 'vis-label vis-brightness'; + this.brightnessLabel.innerHTML = 'brightness:'; - _createClass(Label, [{ - key: 'setOptions', - value: function setOptions(options) { - var allowDeletion = arguments[1] === undefined ? false : arguments[1]; + this.opacityLabel = document.createElement('div'); + this.opacityLabel.className = 'vis-label vis-opacity'; + this.opacityLabel.innerHTML = 'opacity:'; - this.options = options; + this.newColorDiv = document.createElement('div'); + this.newColorDiv.className = 'vis-new-color'; + this.newColorDiv.innerHTML = 'new'; - if (options.label !== undefined) { - this.labelDirty = true; - } + this.initialColorDiv = document.createElement('div'); + this.initialColorDiv.className = 'vis-initial-color'; + this.initialColorDiv.innerHTML = 'initial'; - if (options.font !== undefined) { - Label.parseOptions(this.options.font, options, allowDeletion); - if (typeof options.font === 'string') { - this.baseSize = this.options.font.size; - } else if (typeof options.font === 'object') { - if (options.font.size !== undefined) { - this.baseSize = options.font.size; - } - } - } - } - }, { - key: 'draw', + this.cancelButton = document.createElement('div'); + this.cancelButton.className = 'vis-button vis-cancel'; + this.cancelButton.innerHTML = 'cancel'; + this.cancelButton.onclick = this._hide.bind(this, false); - /** - * Main function. This is called from anything that wants to draw a label. - * @param ctx - * @param x - * @param y - * @param selected - * @param baseline - */ - value: function draw(ctx, x, y, selected) { - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; + this.applyButton = document.createElement('div'); + this.applyButton.className = 'vis-button vis-apply'; + this.applyButton.innerHTML = 'apply'; + this.applyButton.onclick = this._apply.bind(this); - // if no label, return - if (this.options.label === undefined) return; + this.saveButton = document.createElement('div'); + this.saveButton.className = 'vis-button vis-save'; + this.saveButton.innerHTML = 'save'; + this.saveButton.onclick = this._save.bind(this); - // check if we have to render the label - var viewFontSize = this.options.font.size * this.body.view.scale; - if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1) return; + this.loadButton = document.createElement('div'); + this.loadButton.className = 'vis-button vis-load'; + this.loadButton.innerHTML = 'load last'; + this.loadButton.onclick = this._loadLast.bind(this); - // update the size cache if required - this.calculateLabelSize(ctx, selected, x, y, baseline); + this.frame.appendChild(this.colorPickerDiv); + this.frame.appendChild(this.arrowDiv); + this.frame.appendChild(this.brightnessLabel); + this.frame.appendChild(this.brightnessDiv); + this.frame.appendChild(this.opacityLabel); + this.frame.appendChild(this.opacityDiv); + this.frame.appendChild(this.newColorDiv); + this.frame.appendChild(this.initialColorDiv); - // create the fontfill background - this._drawBackground(ctx); - // draw text - this._drawText(ctx, selected, x, y, baseline); + this.frame.appendChild(this.cancelButton); + this.frame.appendChild(this.applyButton); + this.frame.appendChild(this.saveButton); + this.frame.appendChild(this.loadButton); } }, { - key: '_drawBackground', + key: '_bindHammer', /** - * Draws the label background - * @param {CanvasRenderingContext2D} ctx + * bind hammer to the color picker * @private */ - value: function _drawBackground(ctx) { - if (this.options.font.background !== undefined && this.options.font.background !== 'none') { - ctx.fillStyle = this.options.font.background; + value: function _bindHammer() { + var _this = this; - var lineMargin = 2; + this.drag = {}; + this.pinch = {}; + this.hammer = new Hammer(this.colorPickerCanvas); + this.hammer.get('pinch').set({ enable: true }); - switch (this.options.font.align) { - case 'middle': - ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); - break; - case 'top': - ctx.fillRect(-this.size.width * 0.5, -(this.size.height + lineMargin), this.size.width, this.size.height); - break; - case 'bottom': - ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height); - break; - default: - ctx.fillRect(this.size.left, this.size.top, this.size.width, this.size.height); - break; - } - } + hammerUtil.onTouch(this.hammer, function (event) { + _this._moveSelector(event); + }); + this.hammer.on('tap', function (event) { + _this._moveSelector(event); + }); + this.hammer.on('panstart', function (event) { + _this._moveSelector(event); + }); + this.hammer.on('panmove', function (event) { + _this._moveSelector(event); + }); + this.hammer.on('panend', function (event) { + _this._moveSelector(event); + }); } }, { - key: '_drawText', + key: '_generateHueCircle', /** - * - * @param ctx - * @param x - * @param baseline + * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown. * @private */ - value: function _drawText(ctx, selected, x, y) { - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; - - var fontSize = this.options.font.size; - var viewFontSize = fontSize * this.body.view.scale; - // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel) - if (viewFontSize >= this.options.scaling.label.maxVisible) { - fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale; - } - - var yLine = this.size.yLine; - - var _getColor = this._getColor(viewFontSize); - - var _getColor2 = _slicedToArray(_getColor, 2); - - var fontColor = _getColor2[0]; - var strokeColor = _getColor2[1]; - - var _setAlignment = this._setAlignment(ctx, x, yLine, baseline); - - var _setAlignment2 = _slicedToArray(_setAlignment, 2); - - x = _setAlignment2[0]; - yLine = _setAlignment2[1]; - - // configure context for drawing the text - ctx.font = (selected ? 'bold ' : '') + fontSize + 'px ' + this.options.font.face; - ctx.fillStyle = fontColor; - ctx.textAlign = 'center'; - - // set the strokeWidth - if (this.options.font.strokeWidth > 0) { - ctx.lineWidth = this.options.font.strokeWidth; - ctx.strokeStyle = strokeColor; - ctx.lineJoin = 'round'; - } - - // draw the text - for (var i = 0; i < this.lineCount; i++) { - if (this.options.font.strokeWidth > 0) { - ctx.strokeText(this.lines[i], x, yLine); + value: function _generateHueCircle() { + if (this.generated === false) { + var ctx = this.colorPickerCanvas.getContext('2d'); + if (this.pixelRation === undefined) { + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); } - ctx.fillText(this.lines[i], x, yLine); - yLine += fontSize; - } - } - }, { - key: '_setAlignment', - value: function _setAlignment(ctx, x, yLine, baseline) { - // check for label alignment (for edges) - // TODO: make alignment for nodes - if (this.options.font.align !== 'horizontal') { - x = 0; - yLine = 0; + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); - var lineMargin = 2; - if (this.options.font.align === 'top') { - ctx.textBaseline = 'alphabetic'; - yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers - } else if (this.options.font.align === 'bottom') { - ctx.textBaseline = 'hanging'; - yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers - } else { - ctx.textBaseline = 'middle'; + // clear the canvas + var w = this.colorPickerCanvas.clientWidth; + var h = this.colorPickerCanvas.clientHeight; + ctx.clearRect(0, 0, w, h); + + // draw hue circle + var x = undefined, + y = undefined, + hue = undefined, + sat = undefined; + this.centerCoordinates = { x: w * 0.5, y: h * 0.5 }; + this.r = 0.49 * w; + var angleConvert = 2 * Math.PI / 360; + var hfac = 1 / 360; + var sfac = 1 / this.r; + var rgb = undefined; + for (hue = 0; hue < 360; hue++) { + for (sat = 0; sat < this.r; sat++) { + x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue); + y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue); + rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1); + ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; + ctx.fillRect(x - 0.5, y - 0.5, 2, 2); + } } - } else { - ctx.textBaseline = baseline; - } - - return [x, yLine]; - } - }, { - key: '_getColor', + ctx.strokeStyle = 'rgba(0,0,0,1)'; + ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); + ctx.stroke(); - /** - * fade in when relative scale is between threshold and threshold - 1. - * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here. - * - * @param viewFontSize - * @returns {*[]} - * @private - */ - value: function _getColor(viewFontSize) { - var fontColor = this.options.font.color || '#000000'; - var strokeColor = this.options.font.strokeColor || '#ffffff'; - if (viewFontSize <= this.options.scaling.label.drawThreshold) { - var opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize))); - fontColor = util.overrideOpacity(fontColor, opacity); - strokeColor = util.overrideOpacity(strokeColor, opacity); + this.hueCircle = ctx.getImageData(0, 0, w, h); } - return [fontColor, strokeColor]; + this.generated = true; } }, { - key: 'getTextSize', + key: '_moveSelector', /** + * move the selector. This is called by hammer functions. * - * @param ctx - * @param selected - * @returns {{width: number, height: number}} + * @param event + * @private */ - value: function getTextSize(ctx) { - var selected = arguments[1] === undefined ? false : arguments[1]; + value: function _moveSelector(event) { + var rect = this.colorPickerDiv.getBoundingClientRect(); + var left = event.center.x - rect.left; + var top = event.center.y - rect.top; - var size = { - width: this._processLabel(ctx, selected), - height: this.options.font.size * this.lineCount, - lineCount: this.lineCount - }; - return size; - } - }, { - key: 'calculateLabelSize', + var centerY = 0.5 * this.colorPickerDiv.clientHeight; + var centerX = 0.5 * this.colorPickerDiv.clientWidth; - /** - * - * @param ctx - * @param selected - * @param x - * @param y - * @param baseline - */ - value: function calculateLabelSize(ctx, selected) { - var x = arguments[2] === undefined ? 0 : arguments[2]; - var y = arguments[3] === undefined ? 0 : arguments[3]; - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; + var x = left - centerX; + var y = top - centerY; - if (this.labelDirty === true) { - this.size.width = this._processLabel(ctx, selected); - } - this.size.height = this.options.font.size * this.lineCount; - this.size.left = x - this.size.width * 0.5; - this.size.top = y - this.size.height * 0.5; - this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.options.font.size; - if (baseline === 'hanging') { - this.size.top += 0.5 * this.options.font.size; - this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers - this.size.yLine += 4; // distance from node - } + var angle = Math.atan2(x, y); + var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX); - this.labelDirty = false; - } - }, { - key: '_processLabel', + var newTop = Math.cos(angle) * radius + centerY; + var newLeft = Math.sin(angle) * radius + centerX; - /** - * This calculates the width as well as explodes the label string and calculates the amount of lines. - * @param ctx - * @param selected - * @returns {number} - * @private - */ - value: function _processLabel(ctx, selected) { - var width = 0; - var lines = ['']; - var lineCount = 0; - if (this.options.label !== undefined) { - lines = String(this.options.label).split('\n'); - lineCount = lines.length; - ctx.font = (selected ? 'bold ' : '') + this.options.font.size + 'px ' + this.options.font.face; - width = ctx.measureText(lines[0]).width; - for (var i = 1; i < lineCount; i++) { - var lineWidth = ctx.measureText(lines[i]).width; - width = lineWidth > width ? lineWidth : width; - } - } - this.lines = lines; - this.lineCount = lineCount; + this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px'; + this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px'; - return width; - } - }], [{ - key: 'parseOptions', - value: function parseOptions(parentOptions, newOptions) { - var allowDeletion = arguments[2] === undefined ? false : arguments[2]; + // set color + var h = angle / (2 * Math.PI); + h = h < 0 ? h + 1 : h; + var s = radius / this.r; + var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); + hsv.h = h; + hsv.s = s; + var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); + rgba['a'] = this.color.a; + this.color = rgba; - if (typeof newOptions.font === 'string') { - var newOptionsArray = newOptions.font.split(' '); - parentOptions.size = newOptionsArray[0].replace('px', ''); - parentOptions.face = newOptionsArray[1]; - parentOptions.color = newOptionsArray[2]; - } else if (typeof newOptions.font === 'object') { - util.fillIfDefined(parentOptions, newOptions.font, allowDeletion); - } - parentOptions.size = Number(parentOptions.size); + // update previews + this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; + this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; } }]); - return Label; + return ColorPicker; })(); - exports['default'] = Label; + exports['default'] = ColorPicker; module.exports = exports['default']; /***/ }, -/* 76 */ +/* 74 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -35334,21 +34574,21 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _Label = __webpack_require__(75); + var _sharedLabel = __webpack_require__(75); - var _Label2 = _interopRequireDefault(_Label); + var _sharedLabel2 = _interopRequireDefault(_sharedLabel); - var _BezierEdgeDynamic = __webpack_require__(106); + var _edgesBezierEdgeDynamic = __webpack_require__(89); - var _BezierEdgeDynamic2 = _interopRequireDefault(_BezierEdgeDynamic); + var _edgesBezierEdgeDynamic2 = _interopRequireDefault(_edgesBezierEdgeDynamic); - var _BezierEdgeStatic = __webpack_require__(107); + var _edgesBezierEdgeStatic = __webpack_require__(90); - var _BezierEdgeStatic2 = _interopRequireDefault(_BezierEdgeStatic); + var _edgesBezierEdgeStatic2 = _interopRequireDefault(_edgesBezierEdgeStatic); - var _StraightEdge = __webpack_require__(108); + var _edgesStraightEdge = __webpack_require__(91); - var _StraightEdge2 = _interopRequireDefault(_StraightEdge); + var _edgesStraightEdge2 = _interopRequireDefault(_edgesStraightEdge); var util = __webpack_require__(1); @@ -35397,7 +34637,7 @@ return /******/ (function(modules) { // webpackBootstrap this.connected = false; - this.labelModule = new _Label2['default'](this.body, this.options); + this.labelModule = new _sharedLabel2['default'](this.body, this.options); this.setOptions(options); } @@ -35470,13 +34710,13 @@ return /******/ (function(modules) { // webpackBootstrap var dataChanged = false; var changeInType = true; if (this.edgeType !== undefined) { - if (this.edgeType instanceof _BezierEdgeDynamic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type === 'dynamic') { + if (this.edgeType instanceof _edgesBezierEdgeDynamic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type === 'dynamic') { changeInType = false; } - if (this.edgeType instanceof _BezierEdgeStatic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type !== 'dynamic') { + if (this.edgeType instanceof _edgesBezierEdgeStatic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type !== 'dynamic') { changeInType = false; } - if (this.edgeType instanceof _StraightEdge2['default'] && this.options.smooth.enabled === false) { + if (this.edgeType instanceof _edgesStraightEdge2['default'] && this.options.smooth.enabled === false) { changeInType = false; } @@ -35489,12 +34729,12 @@ return /******/ (function(modules) { // webpackBootstrap if (this.options.smooth.enabled === true) { if (this.options.smooth.type === 'dynamic') { dataChanged = true; - this.edgeType = new _BezierEdgeDynamic2['default'](this.options, this.body, this.labelModule); + this.edgeType = new _edgesBezierEdgeDynamic2['default'](this.options, this.body, this.labelModule); } else { - this.edgeType = new _BezierEdgeStatic2['default'](this.options, this.body, this.labelModule); + this.edgeType = new _edgesBezierEdgeStatic2['default'](this.options, this.body, this.labelModule); } } else { - this.edgeType = new _StraightEdge2['default'](this.options, this.body, this.labelModule); + this.edgeType = new _edgesStraightEdge2['default'](this.options, this.body, this.labelModule); } } else { // if nothing changes, we just set the options. @@ -35772,7 +35012,7 @@ return /******/ (function(modules) { // webpackBootstrap value: function parseOptions(parentOptions, newOptions) { var allowDeletion = arguments[2] === undefined ? false : arguments[2]; - var fields = ['id', 'font', 'from', 'hidden', 'hoverWidth', 'label', 'length', 'line', 'opacity', 'physics', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width']; + var fields = ['id', 'from', 'hidden', 'hoverWidth', 'label', 'length', 'line', 'opacity', 'physics', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width']; // only deep extend the items in the field array. These do not have shorthand. util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion); @@ -35868,6 +35108,368 @@ return /******/ (function(modules) { // webpackBootstrap exports['default'] = Edge; module.exports = exports['default']; +/***/ }, +/* 75 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + function _slicedToArray(arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + var util = __webpack_require__(1); + + var Label = (function () { + function Label(body, options) { + _classCallCheck(this, Label); + + this.body = body; + + this.baseSize = undefined; + this.setOptions(options); + this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 }; // could be cached + } + + _createClass(Label, [{ + key: 'setOptions', + value: function setOptions(options) { + var allowDeletion = arguments[1] === undefined ? false : arguments[1]; + + this.options = options; + + if (options.label !== undefined) { + this.labelDirty = true; + } + + if (options.font !== undefined) { + Label.parseOptions(this.options.font, options, allowDeletion); + if (typeof options.font === 'string') { + this.baseSize = this.options.font.size; + } else if (typeof options.font === 'object') { + if (options.font.size !== undefined) { + this.baseSize = options.font.size; + } + } + } + } + }, { + key: 'draw', + + /** + * Main function. This is called from anything that wants to draw a label. + * @param ctx + * @param x + * @param y + * @param selected + * @param baseline + */ + value: function draw(ctx, x, y, selected) { + var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; + + // if no label, return + if (this.options.label === undefined) return; + + // check if we have to render the label + var viewFontSize = this.options.font.size * this.body.view.scale; + if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1) return; + + // update the size cache if required + this.calculateLabelSize(ctx, selected, x, y, baseline); + + // create the fontfill background + this._drawBackground(ctx); + // draw text + this._drawText(ctx, selected, x, y, baseline); + } + }, { + key: '_drawBackground', + + /** + * Draws the label background + * @param {CanvasRenderingContext2D} ctx + * @private + */ + value: function _drawBackground(ctx) { + if (this.options.font.background !== undefined && this.options.font.background !== 'none') { + ctx.fillStyle = this.options.font.background; + + var lineMargin = 2; + + switch (this.options.font.align) { + case 'middle': + ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); + break; + case 'top': + ctx.fillRect(-this.size.width * 0.5, -(this.size.height + lineMargin), this.size.width, this.size.height); + break; + case 'bottom': + ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height); + break; + default: + ctx.fillRect(this.size.left, this.size.top - 0.5 * lineMargin, this.size.width, this.size.height); + break; + } + } + } + }, { + key: '_drawText', + + /** + * + * @param ctx + * @param x + * @param baseline + * @private + */ + value: function _drawText(ctx, selected, x, y) { + var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; + + var fontSize = this.options.font.size; + var viewFontSize = fontSize * this.body.view.scale; + // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel) + if (viewFontSize >= this.options.scaling.label.maxVisible) { + fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale; + } + + var yLine = this.size.yLine; + + var _getColor = this._getColor(viewFontSize); + + var _getColor2 = _slicedToArray(_getColor, 2); + + var fontColor = _getColor2[0]; + var strokeColor = _getColor2[1]; + + var _setAlignment = this._setAlignment(ctx, x, yLine, baseline); + + var _setAlignment2 = _slicedToArray(_setAlignment, 2); + + x = _setAlignment2[0]; + yLine = _setAlignment2[1]; + + // configure context for drawing the text + ctx.font = (selected ? 'bold ' : '') + fontSize + 'px ' + this.options.font.face; + ctx.fillStyle = fontColor; + ctx.textAlign = 'center'; + + // set the strokeWidth + if (this.options.font.strokeWidth > 0) { + ctx.lineWidth = this.options.font.strokeWidth; + ctx.strokeStyle = strokeColor; + ctx.lineJoin = 'round'; + } + + // draw the text + for (var i = 0; i < this.lineCount; i++) { + if (this.options.font.strokeWidth > 0) { + ctx.strokeText(this.lines[i], x, yLine); + } + ctx.fillText(this.lines[i], x, yLine); + yLine += fontSize; + } + } + }, { + key: '_setAlignment', + value: function _setAlignment(ctx, x, yLine, baseline) { + // check for label alignment (for edges) + // TODO: make alignment for nodes + if (this.options.font.align !== 'horizontal') { + x = 0; + yLine = 0; + + var lineMargin = 2; + if (this.options.font.align === 'top') { + ctx.textBaseline = 'alphabetic'; + yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers + } else if (this.options.font.align === 'bottom') { + ctx.textBaseline = 'hanging'; + yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers + } else { + ctx.textBaseline = 'middle'; + } + } else { + ctx.textBaseline = baseline; + } + + return [x, yLine]; + } + }, { + key: '_getColor', + + /** + * fade in when relative scale is between threshold and threshold - 1. + * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here. + * + * @param viewFontSize + * @returns {*[]} + * @private + */ + value: function _getColor(viewFontSize) { + var fontColor = this.options.font.color || '#000000'; + var strokeColor = this.options.font.strokeColor || '#ffffff'; + if (viewFontSize <= this.options.scaling.label.drawThreshold) { + var opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize))); + fontColor = util.overrideOpacity(fontColor, opacity); + strokeColor = util.overrideOpacity(strokeColor, opacity); + } + return [fontColor, strokeColor]; + } + }, { + key: 'getTextSize', + + /** + * + * @param ctx + * @param selected + * @returns {{width: number, height: number}} + */ + value: function getTextSize(ctx) { + var selected = arguments[1] === undefined ? false : arguments[1]; + + var size = { + width: this._processLabel(ctx, selected), + height: this.options.font.size * this.lineCount, + lineCount: this.lineCount + }; + return size; + } + }, { + key: 'calculateLabelSize', + + /** + * + * @param ctx + * @param selected + * @param x + * @param y + * @param baseline + */ + value: function calculateLabelSize(ctx, selected) { + var x = arguments[2] === undefined ? 0 : arguments[2]; + var y = arguments[3] === undefined ? 0 : arguments[3]; + var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; + + if (this.labelDirty === true) { + this.size.width = this._processLabel(ctx, selected); + } + this.size.height = this.options.font.size * this.lineCount; + this.size.left = x - this.size.width * 0.5; + this.size.top = y - this.size.height * 0.5; + this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.options.font.size; + if (baseline === 'hanging') { + this.size.top += 0.5 * this.options.font.size; + this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers + this.size.yLine += 4; // distance from node + } + + this.labelDirty = false; + } + }, { + key: '_processLabel', + + /** + * This calculates the width as well as explodes the label string and calculates the amount of lines. + * @param ctx + * @param selected + * @returns {number} + * @private + */ + value: function _processLabel(ctx, selected) { + var width = 0; + var lines = ['']; + var lineCount = 0; + if (this.options.label !== undefined) { + lines = String(this.options.label).split('\n'); + lineCount = lines.length; + ctx.font = (selected ? 'bold ' : '') + this.options.font.size + 'px ' + this.options.font.face; + width = ctx.measureText(lines[0]).width; + for (var i = 1; i < lineCount; i++) { + var lineWidth = ctx.measureText(lines[i]).width; + width = lineWidth > width ? lineWidth : width; + } + } + this.lines = lines; + this.lineCount = lineCount; + + return width; + } + }], [{ + key: 'parseOptions', + value: function parseOptions(parentOptions, newOptions) { + var allowDeletion = arguments[2] === undefined ? false : arguments[2]; + + if (typeof newOptions.font === 'string') { + var newOptionsArray = newOptions.font.split(' '); + parentOptions.size = newOptionsArray[0].replace('px', ''); + parentOptions.face = newOptionsArray[1]; + parentOptions.color = newOptionsArray[2]; + } else if (typeof newOptions.font === 'object') { + util.fillIfDefined(parentOptions, newOptions.font, allowDeletion); + } + parentOptions.size = Number(parentOptions.size); + } + }]); + + return Label; + })(); + + exports['default'] = Label; + module.exports = exports['default']; + +/***/ }, +/* 76 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + + var _Node2 = __webpack_require__(87); + + var _Node3 = _interopRequireDefault(_Node2); + + /** + * + */ + + var Cluster = (function (_Node) { + function Cluster(options, body, imagelist, grouplist, globalOptions) { + _classCallCheck(this, Cluster); + + _get(Object.getPrototypeOf(Cluster.prototype), 'constructor', this).call(this, options, body, imagelist, grouplist, globalOptions); + + this.isCluster = true; + this.containedNodes = {}; + this.containedEdges = {}; + } + + _inherits(Cluster, _Node); + + return Cluster; + })(_Node3['default']); + + exports['default'] = Cluster; + module.exports = exports['default']; + /***/ }, /* 77 */ /***/ function(module, exports, __webpack_require__) { @@ -35897,6 +35499,7 @@ return /******/ (function(modules) { // webpackBootstrap value: function setOptions(options) { this.options = options; this.thetaInversed = 1 / this.options.theta; + this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius } }, { key: "solve", @@ -35996,6 +35599,11 @@ return /******/ (function(modules) { // webpackBootstrap distance = 0.1 * Math.random(); dx = distance; } + + if (this.overlapAvoidanceFactor < 1) { + distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); + } + // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance, 3); @@ -36567,6 +36175,7 @@ return /******/ (function(modules) { // webpackBootstrap key: "setOptions", value: function setOptions(options) { this.options = options; + this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius } }, { key: "solve", @@ -36621,8 +36230,17 @@ return /******/ (function(modules) { // webpackBootstrap * @private */ value: function _calculateSpringForce(node1, node2, edgeLength) { - var dx = node1.x - node2.x; - var dy = node1.y - node2.y; + var dx = undefined, + dy = undefined; + if (this.overlapAvoidanceFactor < 1) { + var nodesWidth = this.overlapAvoidanceFactor * (node1.shape.radius + node2.shape.radius); + dx = node1.x - node2.x - nodesWidth; + dy = node1.y - node2.y - nodesWidth; + } else { + dx = node1.x - node2.x; + dy = node1.y - node2.y; + } + var distance = Math.sqrt(dx * dx + dy * dy); distance = distance === 0 ? 0.01 : distance; @@ -36851,567 +36469,1033 @@ return /******/ (function(modules) { // webpackBootstrap value: true }); - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + + var _BarnesHutSolver2 = __webpack_require__(77); + + var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); + + var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { + function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); + + _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); + } + + _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); + + _createClass(ForceAtlas2BasedRepulsionSolver, [{ + key: "_calculateForces", + + /** + * Calculate the forces based on the distance. + * + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch + * @private + */ + value: function _calculateForces(distance, dx, dy, node, parentBranch) { + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; + } + + if (this.overlapAvoidanceFactor < 1) { + distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); + } + + var degree = node.edges.length + 1; + // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines + // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce + var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + + this.physicsBody.forces[node.id].x += fx; + this.physicsBody.forces[node.id].y += fy; + } + }]); + + return ForceAtlas2BasedRepulsionSolver; + })(_BarnesHutSolver3["default"]); + + exports["default"] = ForceAtlas2BasedRepulsionSolver; + module.exports = exports["default"]; + +/***/ }, +/* 84 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + + var _CentralGravitySolver2 = __webpack_require__(82); + + var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); + + var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) { + function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); + + _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); + } + + _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); + + _createClass(ForceAtlas2BasedCentralGravitySolver, [{ + key: "_calculateForces", + + /** + * Calculate the forces based on the distance. + * @private + */ + value: function _calculateForces(distance, dx, dy, forces, node) { + if (distance > 0) { + var degree = node.edges.length + 1; + var gravityForce = this.options.centralGravity * degree * node.options.mass; + forces[node.id].x = dx * gravityForce; + forces[node.id].y = dy * gravityForce; + } + } + }]); + + return ForceAtlas2BasedCentralGravitySolver; + })(_CentralGravitySolver3["default"]); + + exports["default"] = ForceAtlas2BasedCentralGravitySolver; + module.exports = exports["default"]; + +/***/ }, +/* 85 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + var util = __webpack_require__(1); + var Hammer = __webpack_require__(41); + var hammerUtil = __webpack_require__(48); + var keycharm = __webpack_require__(88); + + var NavigationHandler = (function () { + function NavigationHandler(body, canvas) { + var _this = this; + + _classCallCheck(this, NavigationHandler); + + this.body = body; + this.canvas = canvas; + + this.iconsCreated = false; + this.navigationHammers = []; + this.boundFunctions = {}; + this.touchTime = 0; + this.activated = false; + + this.body.emitter.on('release', function () { + _this._stopMovement(); + }); + this.body.emitter.on('activate', function () { + _this.activated = true;_this.configureKeyboardBindings(); + }); + this.body.emitter.on('deactivate', function () { + _this.activated = false;_this.configureKeyboardBindings(); + }); + this.body.emitter.on('destroy', function () { + if (_this.keycharm !== undefined) { + _this.keycharm.destroy(); + } + }); + + this.options = {}; + } + + _createClass(NavigationHandler, [{ + key: 'setOptions', + value: function setOptions(options) { + if (options !== undefined) { + this.options = options; + this.create(); + } + } + }, { + key: 'create', + value: function create() { + if (this.options.navigationButtons === true) { + if (this.iconsCreated === false) { + this.loadNavigationElements(); + } + } else if (this.iconsCreated === true) { + this.cleanNavigation(); + } + + this.configureKeyboardBindings(); + } + }, { + key: 'cleanNavigation', + value: function cleanNavigation() { + // clean hammer bindings + if (this.navigationHammers.length != 0) { + for (var i = 0; i < this.navigationHammers.length; i++) { + this.navigationHammers[i].destroy(); + } + this.navigationHammers = []; + } + + this._navigationReleaseOverload = function () {}; + + // clean up previous navigation items + if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) { + this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']); + } + + this.iconsCreated = false; + } + }, { + key: 'loadNavigationElements', + + /** + * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation + * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent + * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. + * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. + * + * @private + */ + value: function loadNavigationElements() { + this.cleanNavigation(); + + this.navigationDOM = {}; + var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends']; + var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit']; + + this.navigationDOM['wrapper'] = document.createElement('div'); + this.navigationDOM['wrapper'].className = 'vis-navigation'; + this.canvas.frame.appendChild(this.navigationDOM['wrapper']); + + for (var i = 0; i < navigationDivs.length; i++) { + this.navigationDOM[navigationDivs[i]] = document.createElement('div'); + this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i]; + this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); + + var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); + if (navigationDivActions[i] === '_fit') { + hammerUtil.onTouch(hammer, this._fit.bind(this)); + } else { + hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); + } + + this.navigationHammers.push(hammer); + } + + this.iconsCreated = true; + } + }, { + key: 'bindToRedraw', + value: function bindToRedraw(action) { + if (this.boundFunctions[action] === undefined) { + this.boundFunctions[action] = this[action].bind(this); + this.body.emitter.on('initRedraw', this.boundFunctions[action]); + this.body.emitter.emit('_startRendering'); + } + } + }, { + key: 'unbindFromRedraw', + value: function unbindFromRedraw(action) { + if (this.boundFunctions[action] !== undefined) { + this.body.emitter.off('initRedraw', this.boundFunctions[action]); + this.body.emitter.emit('_stopRendering'); + delete this.boundFunctions[action]; + } + } + }, { + key: '_fit', + + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + value: function _fit() { + if (new Date().valueOf() - this.touchTime > 700) { + // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?) + this.body.emitter.emit('fit', { duration: 700 }); + this.touchTime = new Date().valueOf(); + } + } + }, { + key: '_stopMovement', + + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + value: function _stopMovement() { + for (var boundAction in this.boundFunctions) { + if (this.boundFunctions.hasOwnProperty(boundAction)) { + this.body.emitter.off('initRedraw', this.boundFunctions[boundAction]); + this.body.emitter.emit('_stopRendering'); + } + } + this.boundFunctions = {}; + } + }, { + key: '_moveUp', + value: function _moveUp() { + this.body.view.translation.y += this.options.keyboard.speed.y; + } + }, { + key: '_moveDown', + value: function _moveDown() { + this.body.view.translation.y -= this.options.keyboard.speed.y; + } + }, { + key: '_moveLeft', + value: function _moveLeft() { + this.body.view.translation.x += this.options.keyboard.speed.x; + } + }, { + key: '_moveRight', + value: function _moveRight() { + this.body.view.translation.x -= this.options.keyboard.speed.x; + } + }, { + key: '_zoomIn', + value: function _zoomIn() { + this.body.view.scale *= 1 + this.options.keyboard.speed.zoom; + } + }, { + key: '_zoomOut', + value: function _zoomOut() { + this.body.view.scale /= 1 + this.options.keyboard.speed.zoom; + } + }, { + key: 'configureKeyboardBindings', + + /** + * bind all keys using keycharm. + */ + value: function configureKeyboardBindings() { + if (this.keycharm !== undefined) { + this.keycharm.destroy(); + } + + if (this.options.keyboard.enabled === true) { + + if (this.options.keyboard.bindToWindow === true) { + this.keycharm = keycharm({ container: window, preventDefault: true }); + } else { + this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true }); + } + + this.keycharm.reset(); + + if (this.activated === true) { + this.keycharm.bind('up', this.bindToRedraw.bind(this, '_moveUp'), 'keydown'); + this.keycharm.bind('down', this.bindToRedraw.bind(this, '_moveDown'), 'keydown'); + this.keycharm.bind('left', this.bindToRedraw.bind(this, '_moveLeft'), 'keydown'); + this.keycharm.bind('right', this.bindToRedraw.bind(this, '_moveRight'), 'keydown'); + this.keycharm.bind('=', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('num+', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('num-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind('-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind('[', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind(']', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('pageup', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('pagedown', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + + this.keycharm.bind('up', this.unbindFromRedraw.bind(this, '_moveUp'), 'keyup'); + this.keycharm.bind('down', this.unbindFromRedraw.bind(this, '_moveDown'), 'keyup'); + this.keycharm.bind('left', this.unbindFromRedraw.bind(this, '_moveLeft'), 'keyup'); + this.keycharm.bind('right', this.unbindFromRedraw.bind(this, '_moveRight'), 'keyup'); + this.keycharm.bind('=', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('num+', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('num-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind('-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind('[', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind(']', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('pageup', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('pagedown', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + } + } + } + }]); + + return NavigationHandler; + })(); + + exports['default'] = NavigationHandler; + module.exports = exports['default']; + +/***/ }, +/* 86 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + /** + * Popup is a class to create a popup window with some text + * @param {Element} container The container object. + * @param {Number} [x] + * @param {Number} [y] + * @param {String} [text] + * @param {Object} [style] An object containing borderColor, + * backgroundColor, etc. + */ + + var Popup = (function () { + function Popup(container) { + _classCallCheck(this, Popup); + + this.container = container; - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + this.x = 0; + this.y = 0; + this.padding = 5; + this.hidden = false; - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + // create the frame + this.frame = document.createElement('div'); + this.frame.className = 'vis-network-tooltip'; + this.container.appendChild(this.frame); + } - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + _createClass(Popup, [{ + key: 'setPosition', - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + /** + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window + */ + value: function setPosition(x, y) { + this.x = parseInt(x); + this.y = parseInt(y); + } + }, { + key: 'setText', - var _BarnesHutSolver2 = __webpack_require__(77); + /** + * Set the content for the popup window. This can be HTML code or text. + * @param {string | Element} content + */ + value: function setText(content) { + if (content instanceof Element) { + this.frame.innerHTML = ''; + this.frame.appendChild(content); + } else { + this.frame.innerHTML = content; // string containing text or HTML + } + } + }, { + key: 'show', - var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); + /** + * Show the popup window + * @param {boolean} [doShow] Show or hide the window + */ + value: function show(doShow) { + if (doShow === undefined) { + doShow = true; + } - var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { - function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { - _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); + if (doShow === true) { + var height = this.frame.clientHeight; + var width = this.frame.clientWidth; + var maxHeight = this.frame.parentNode.clientHeight; + var maxWidth = this.frame.parentNode.clientWidth; - _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); - } + var top = this.y - height; + if (top + height + this.padding > maxHeight) { + top = maxHeight - height - this.padding; + } + if (top < this.padding) { + top = this.padding; + } - _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); + var left = this.x; + if (left + width + this.padding > maxWidth) { + left = maxWidth - width - this.padding; + } + if (left < this.padding) { + left = this.padding; + } - _createClass(ForceAtlas2BasedRepulsionSolver, [{ - key: "_calculateForces", + this.frame.style.left = left + 'px'; + this.frame.style.top = top + 'px'; + this.frame.style.visibility = 'visible'; + this.hidden = false; + } else { + this.hide(); + } + } + }, { + key: 'hide', /** - * Calculate the forces based on the distance. - * - * @param distance - * @param dx - * @param dy - * @param node - * @param parentBranch - * @private + * Hide the popup window */ - value: function _calculateForces(distance, dx, dy, node, parentBranch) { - if (distance === 0) { - distance = 0.1 * Math.random(); - dx = distance; - } - var degree = node.edges.length + 1; - // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines - // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce - var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - - this.physicsBody.forces[node.id].x += fx; - this.physicsBody.forces[node.id].y += fy; + value: function hide() { + this.hidden = true; + this.frame.style.visibility = 'hidden'; } }]); - return ForceAtlas2BasedRepulsionSolver; - })(_BarnesHutSolver3["default"]); + return Popup; + })(); - exports["default"] = ForceAtlas2BasedRepulsionSolver; - module.exports = exports["default"]; + exports['default'] = Popup; + module.exports = exports['default']; /***/ }, -/* 84 */ +/* 87 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + 'use strict'; - Object.defineProperty(exports, "__esModule", { + Object.defineProperty(exports, '__esModule', { value: true }); - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var _sharedLabel = __webpack_require__(75); - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + var _sharedLabel2 = _interopRequireDefault(_sharedLabel); - var _CentralGravitySolver2 = __webpack_require__(82); + var _nodesShapesBox = __webpack_require__(93); - var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); + var _nodesShapesBox2 = _interopRequireDefault(_nodesShapesBox); - var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) { - function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { - _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); + var _nodesShapesCircle = __webpack_require__(94); - _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); - } + var _nodesShapesCircle2 = _interopRequireDefault(_nodesShapesCircle); - _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); + var _nodesShapesCircularImage = __webpack_require__(95); - _createClass(ForceAtlas2BasedCentralGravitySolver, [{ - key: "_calculateForces", + var _nodesShapesCircularImage2 = _interopRequireDefault(_nodesShapesCircularImage); - /** - * Calculate the forces based on the distance. - * @private - */ - value: function _calculateForces(distance, dx, dy, forces, node) { - if (distance > 0) { - var degree = node.edges.length + 1; - var gravityForce = this.options.centralGravity * degree * node.options.mass; - forces[node.id].x = dx * gravityForce; - forces[node.id].y = dy * gravityForce; - } - } - }]); + var _nodesShapesDatabase = __webpack_require__(96); - return ForceAtlas2BasedCentralGravitySolver; - })(_CentralGravitySolver3["default"]); + var _nodesShapesDatabase2 = _interopRequireDefault(_nodesShapesDatabase); - exports["default"] = ForceAtlas2BasedCentralGravitySolver; - module.exports = exports["default"]; + var _nodesShapesDiamond = __webpack_require__(97); -/***/ }, -/* 85 */ -/***/ function(module, exports, __webpack_require__) { + var _nodesShapesDiamond2 = _interopRequireDefault(_nodesShapesDiamond); - 'use strict'; + var _nodesShapesDot = __webpack_require__(98); - Object.defineProperty(exports, '__esModule', { - value: true - }); + var _nodesShapesDot2 = _interopRequireDefault(_nodesShapesDot); - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + var _nodesShapesEllipse = __webpack_require__(99); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + var _nodesShapesEllipse2 = _interopRequireDefault(_nodesShapesEllipse); - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + var _nodesShapesIcon = __webpack_require__(100); - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + var _nodesShapesIcon2 = _interopRequireDefault(_nodesShapesIcon); - var _Node2 = __webpack_require__(74); + var _nodesShapesImage = __webpack_require__(101); - var _Node3 = _interopRequireDefault(_Node2); + var _nodesShapesImage2 = _interopRequireDefault(_nodesShapesImage); - /** - * - */ + var _nodesShapesSquare = __webpack_require__(102); - var Cluster = (function (_Node) { - function Cluster(options, body, imagelist, grouplist, globalOptions) { - _classCallCheck(this, Cluster); + var _nodesShapesSquare2 = _interopRequireDefault(_nodesShapesSquare); - _get(Object.getPrototypeOf(Cluster.prototype), 'constructor', this).call(this, options, body, imagelist, grouplist, globalOptions); + var _nodesShapesStar = __webpack_require__(103); - this.isCluster = true; - this.containedNodes = {}; - this.containedEdges = {}; - } + var _nodesShapesStar2 = _interopRequireDefault(_nodesShapesStar); - _inherits(Cluster, _Node); + var _nodesShapesText = __webpack_require__(104); - return Cluster; - })(_Node3['default']); + var _nodesShapesText2 = _interopRequireDefault(_nodesShapesText); - exports['default'] = Cluster; - module.exports = exports['default']; + var _nodesShapesTriangle = __webpack_require__(105); -/***/ }, -/* 86 */ -/***/ function(module, exports, __webpack_require__) { + var _nodesShapesTriangle2 = _interopRequireDefault(_nodesShapesTriangle); - 'use strict'; + var _nodesShapesTriangleDown = __webpack_require__(106); - Object.defineProperty(exports, '__esModule', { - value: true - }); + var _nodesShapesTriangleDown2 = _interopRequireDefault(_nodesShapesTriangleDown); - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + var _sharedValidator = __webpack_require__(45); - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + var _sharedValidator2 = _interopRequireDefault(_sharedValidator); var util = __webpack_require__(1); - var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(49); - var keycharm = __webpack_require__(88); - var NavigationHandler = (function () { - function NavigationHandler(body, canvas) { - var _this = this; + /** + * @class Node + * A node. A node can be connected to other nodes via one or multiple edges. + * @param {object} options An object containing options for the node. All + * options are optional, except for the id. + * {number} id Id of the node. Required + * {string} label Text label for the node + * {number} x Horizontal position of the node + * {number} y Vertical position of the node + * {string} shape Node shape, available: + * "database", "circle", "ellipse", + * "box", "image", "text", "dot", + * "star", "triangle", "triangleDown", + * "square", "icon" + * {string} image An image url + * {string} title An title text, can be HTML + * {anytype} group A group name or number + * @param {Network.Images} imagelist A list with images. Only needed + * when the node has an image + * @param {Network.Groups} grouplist A list with groups. Needed for + * retrieving group options + * @param {Object} constants An object with default values for + * example for the color + * + */ - _classCallCheck(this, NavigationHandler); + var Node = (function () { + function Node(options, body, imagelist, grouplist, globalOptions) { + _classCallCheck(this, Node); + this.options = util.bridgeObject(globalOptions); this.body = body; - this.canvas = canvas; - this.iconsCreated = false; - this.navigationHammers = []; - this.boundFunctions = {}; - this.touchTime = 0; - this.activated = false; + this.edges = []; // all edges connected to this node - this.body.emitter.on('release', function () { - _this._stopMovement(); - }); - this.body.emitter.on('activate', function () { - _this.activated = true;_this.configureKeyboardBindings(); - }); - this.body.emitter.on('deactivate', function () { - _this.activated = false;_this.configureKeyboardBindings(); - }); - this.body.emitter.on('destroy', function () { - if (_this.keycharm !== undefined) { - _this.keycharm.destroy(); - } - }); + // set defaults for the options + this.id = undefined; + this.imagelist = imagelist; + this.grouplist = grouplist; - this.options = {}; + // state options + this.x = undefined; + this.y = undefined; + this.baseSize = this.options.size; + this.baseFontSize = this.options.font.size; + this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate + this.selected = false; + this.hover = false; + + this.labelModule = new _sharedLabel2['default'](this.body, this.options); + this.setOptions(options); } - _createClass(NavigationHandler, [{ - key: 'setOptions', - value: function setOptions(options) { - if (options !== undefined) { - this.options = options; - this.create(); + _createClass(Node, [{ + key: 'attachEdge', + + /** + * Attach a edge to the node + * @param {Edge} edge + */ + value: function attachEdge(edge) { + if (this.edges.indexOf(edge) === -1) { + this.edges.push(edge); } } }, { - key: 'create', - value: function create() { - if (this.options.navigationButtons === true) { - if (this.iconsCreated === false) { - this.loadNavigationElements(); - } - } else if (this.iconsCreated === true) { - this.cleanNavigation(); - } + key: 'detachEdge', - this.configureKeyboardBindings(); + /** + * Detach a edge from the node + * @param {Edge} edge + */ + value: function detachEdge(edge) { + var index = this.edges.indexOf(edge); + if (index != -1) { + this.edges.splice(index, 1); + } } }, { - key: 'cleanNavigation', - value: function cleanNavigation() { - // clean hammer bindings - if (this.navigationHammers.length != 0) { - for (var i = 0; i < this.navigationHammers.length; i++) { - this.navigationHammers[i].destroy(); - } - this.navigationHammers = []; - } - - this._navigationReleaseOverload = function () {}; - - // clean up previous navigation items - if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) { - this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']); - } + key: 'togglePhysics', - this.iconsCreated = false; + /** + * Enable or disable the physics. + * @param status + */ + value: function togglePhysics(status) { + this.options.physics = status; } }, { - key: 'loadNavigationElements', + key: 'setOptions', /** - * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation - * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent - * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. - * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. - * - * @private + * Set or overwrite options for the node + * @param {Object} options an object with options + * @param {Object} constants and object with default, global options */ - value: function loadNavigationElements() { - this.cleanNavigation(); + value: function setOptions(options) { + if (!options) { + return; + } - this.navigationDOM = {}; - var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends']; - var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit']; + // basic options + if (options.id !== undefined) { + this.id = options.id; + } - this.navigationDOM['wrapper'] = document.createElement('div'); - this.navigationDOM['wrapper'].className = 'vis-navigation'; - this.canvas.frame.appendChild(this.navigationDOM['wrapper']); + if (this.id === undefined) { + throw 'Node must have an id'; + } - for (var i = 0; i < navigationDivs.length; i++) { - this.navigationDOM[navigationDivs[i]] = document.createElement('div'); - this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i]; - this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); + if (options.x !== undefined) { + this.x = parseInt(options.x);this.predefinedPosition = true; + } + if (options.y !== undefined) { + this.y = parseInt(options.y);this.predefinedPosition = true; + } + if (options.size !== undefined) { + this.baseSize = options.size; + } + if (options.value !== undefined) { + options.value = parseInt(options.value); + } - var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); - if (navigationDivActions[i] === '_fit') { - hammerUtil.onTouch(hammer, this._fit.bind(this)); + // this transforms all shorthands into fully defined options + Node.parseOptions(this.options, options, true); + + // copy group options + if (typeof options.group === 'number' || typeof options.group === 'string' && options.group != '') { + var groupObj = this.grouplist.get(options.group); + util.deepExtend(this.options, groupObj); + // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case. + this.options.color = util.parseColor(this.options.color); + } + + // load the images + if (this.options.image !== undefined && this.options.image != '') { + if (this.imagelist) { + this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); } else { - hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); + throw 'No imagelist provided'; } - - this.navigationHammers.push(hammer); } - this.iconsCreated = true; + this.updateShape(); + this.updateLabelModule(); + + // reset the size of the node, this can be changed + this._reset(); } }, { - key: 'bindToRedraw', - value: function bindToRedraw(action) { - if (this.boundFunctions[action] === undefined) { - this.boundFunctions[action] = this[action].bind(this); - this.body.emitter.on('initRedraw', this.boundFunctions[action]); - this.body.emitter.emit('_startRendering'); + key: 'updateLabelModule', + value: function updateLabelModule() { + if (this.options.label === undefined || this.options.label === null) { + this.options.label = ''; + } + this.labelModule.setOptions(this.options, true); + if (this.labelModule.baseSize !== undefined) { + this.baseFontSize = this.labelModule.baseSize; } } }, { - key: 'unbindFromRedraw', - value: function unbindFromRedraw(action) { - if (this.boundFunctions[action] !== undefined) { - this.body.emitter.off('initRedraw', this.boundFunctions[action]); - this.body.emitter.emit('_stopRendering'); - delete this.boundFunctions[action]; + key: 'updateShape', + value: function updateShape() { + // choose draw method depending on the shape + switch (this.options.shape) { + case 'box': + this.shape = new _nodesShapesBox2['default'](this.options, this.body, this.labelModule); + break; + case 'circle': + this.shape = new _nodesShapesCircle2['default'](this.options, this.body, this.labelModule); + break; + case 'circularImage': + this.shape = new _nodesShapesCircularImage2['default'](this.options, this.body, this.labelModule, this.imageObj); + break; + case 'database': + this.shape = new _nodesShapesDatabase2['default'](this.options, this.body, this.labelModule); + break; + case 'diamond': + this.shape = new _nodesShapesDiamond2['default'](this.options, this.body, this.labelModule); + break; + case 'dot': + this.shape = new _nodesShapesDot2['default'](this.options, this.body, this.labelModule); + break; + case 'ellipse': + this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule); + break; + case 'icon': + this.shape = new _nodesShapesIcon2['default'](this.options, this.body, this.labelModule); + break; + case 'image': + this.shape = new _nodesShapesImage2['default'](this.options, this.body, this.labelModule, this.imageObj); + break; + case 'square': + this.shape = new _nodesShapesSquare2['default'](this.options, this.body, this.labelModule); + break; + case 'star': + this.shape = new _nodesShapesStar2['default'](this.options, this.body, this.labelModule); + break; + case 'text': + this.shape = new _nodesShapesText2['default'](this.options, this.body, this.labelModule); + break; + case 'triangle': + this.shape = new _nodesShapesTriangle2['default'](this.options, this.body, this.labelModule); + break; + case 'triangleDown': + this.shape = new _nodesShapesTriangleDown2['default'](this.options, this.body, this.labelModule); + break; + default: + this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule); + break; } + this._reset(); } }, { - key: '_fit', + key: 'select', + + /** + * select this node + */ + value: function select() { + this.selected = true; + this._reset(); + } + }, { + key: 'unselect', + + /** + * unselect this node + */ + value: function unselect() { + this.selected = false; + this._reset(); + } + }, { + key: '_reset', + + /** + * Reset the calculated size of the node, forces it to recalculate its size + * @private + */ + value: function _reset() { + this.shape.width = undefined; + this.shape.height = undefined; + } + }, { + key: 'getTitle', + + /** + * get the title of this node. + * @return {string} title The title of the node, or undefined when no title + * has been set. + */ + value: function getTitle() { + return this.options.title; + } + }, { + key: 'distanceToBorder', /** - * this stops all movement induced by the navigation buttons - * - * @private + * Calculate the distance to the border of the Node + * @param {CanvasRenderingContext2D} ctx + * @param {Number} angle Angle in radians + * @returns {number} distance Distance to the border in pixels */ - value: function _fit() { - if (new Date().valueOf() - this.touchTime > 700) { - // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?) - this.body.emitter.emit('fit', { duration: 700 }); - this.touchTime = new Date().valueOf(); - } + value: function distanceToBorder(ctx, angle) { + return this.shape.distanceToBorder(ctx, angle); } }, { - key: '_stopMovement', + key: 'isFixed', /** - * this stops all movement induced by the navigation buttons - * - * @private + * Check if this node has a fixed x and y position + * @return {boolean} true if fixed, false if not */ - value: function _stopMovement() { - for (var boundAction in this.boundFunctions) { - if (this.boundFunctions.hasOwnProperty(boundAction)) { - this.body.emitter.off('initRedraw', this.boundFunctions[boundAction]); - this.body.emitter.emit('_stopRendering'); - } - } - this.boundFunctions = {}; - } - }, { - key: '_moveUp', - value: function _moveUp() { - this.body.view.translation.y += this.options.keyboard.speed.y; + value: function isFixed() { + return this.options.fixed.x && this.options.fixed.y; } }, { - key: '_moveDown', - value: function _moveDown() { - this.body.view.translation.y -= this.options.keyboard.speed.y; + key: 'isSelected', + + /** + * check if this node is selecte + * @return {boolean} selected True if node is selected, else false + */ + value: function isSelected() { + return this.selected; } }, { - key: '_moveLeft', - value: function _moveLeft() { - this.body.view.translation.x += this.options.keyboard.speed.x; + key: 'getValue', + + /** + * Retrieve the value of the node. Can be undefined + * @return {Number} value + */ + value: function getValue() { + return this.options.value; } }, { - key: '_moveRight', - value: function _moveRight() { - this.body.view.translation.x -= this.options.keyboard.speed.x; + key: 'setValueRange', + + /** + * Adjust the value range of the node. The node will adjust it's size + * based on its value. + * @param {Number} min + * @param {Number} max + */ + value: function setValueRange(min, max, total) { + if (this.options.value !== undefined) { + var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); + var sizeDiff = this.options.scaling.max - this.options.scaling.min; + if (this.options.scaling.label.enabled === true) { + var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; + this.options.font.size = this.options.scaling.label.min + scale * fontDiff; + } + this.options.size = this.options.scaling.min + scale * sizeDiff; + } else { + this.options.size = this.baseSize; + this.options.font.size = this.baseFontSize; + } } }, { - key: '_zoomIn', - value: function _zoomIn() { - this.body.view.scale *= 1 + this.options.keyboard.speed.zoom; + key: 'draw', + + /** + * Draw this node in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + */ + value: function draw(ctx) { + this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); } }, { - key: '_zoomOut', - value: function _zoomOut() { - this.body.view.scale /= 1 + this.options.keyboard.speed.zoom; + key: 'updateBoundingBox', + + /** + * Update the bounding box of the shape + */ + value: function updateBoundingBox() { + this.shape.updateBoundingBox(this.x, this.y); } }, { - key: 'configureKeyboardBindings', + key: 'resize', /** - * bind all keys using keycharm. + * Recalculate the size of this node in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx */ - value: function configureKeyboardBindings() { - if (this.keycharm !== undefined) { - this.keycharm.destroy(); - } - - if (this.options.keyboard.enabled === true) { - - if (this.options.keyboard.bindToWindow === true) { - this.keycharm = keycharm({ container: window, preventDefault: true }); - } else { - this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true }); - } - - this.keycharm.reset(); - - if (this.activated === true) { - this.keycharm.bind('up', this.bindToRedraw.bind(this, '_moveUp'), 'keydown'); - this.keycharm.bind('down', this.bindToRedraw.bind(this, '_moveDown'), 'keydown'); - this.keycharm.bind('left', this.bindToRedraw.bind(this, '_moveLeft'), 'keydown'); - this.keycharm.bind('right', this.bindToRedraw.bind(this, '_moveRight'), 'keydown'); - this.keycharm.bind('=', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('num+', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('num-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind('-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind('[', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind(']', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('pageup', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('pagedown', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - - this.keycharm.bind('up', this.unbindFromRedraw.bind(this, '_moveUp'), 'keyup'); - this.keycharm.bind('down', this.unbindFromRedraw.bind(this, '_moveDown'), 'keyup'); - this.keycharm.bind('left', this.unbindFromRedraw.bind(this, '_moveLeft'), 'keyup'); - this.keycharm.bind('right', this.unbindFromRedraw.bind(this, '_moveRight'), 'keyup'); - this.keycharm.bind('=', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('num+', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('num-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind('-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind('[', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind(']', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('pageup', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('pagedown', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - } - } + value: function resize(ctx) { + this.shape.resize(ctx); } - }]); - - return NavigationHandler; - })(); - - exports['default'] = NavigationHandler; - module.exports = exports['default']; - -/***/ }, -/* 87 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - Object.defineProperty(exports, '__esModule', { - value: true - }); - - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - - /** - * Popup is a class to create a popup window with some text - * @param {Element} container The container object. - * @param {Number} [x] - * @param {Number} [y] - * @param {String} [text] - * @param {Object} [style] An object containing borderColor, - * backgroundColor, etc. - */ - - var Popup = (function () { - function Popup(container) { - _classCallCheck(this, Popup); - - this.container = container; - - this.x = 0; - this.y = 0; - this.padding = 5; - this.hidden = false; - - // create the frame - this.frame = document.createElement('div'); - this.frame.className = 'vis-network-tooltip'; - this.container.appendChild(this.frame); - } - - _createClass(Popup, [{ - key: 'setPosition', + }, { + key: 'isOverlappingWith', /** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window + * Check if this object is overlapping with the provided object + * @param {Object} obj an object with parameters left, top, right, bottom + * @return {boolean} True if location is located on node */ - value: function setPosition(x, y) { - this.x = parseInt(x); - this.y = parseInt(y); + value: function isOverlappingWith(obj) { + return this.shape.left < obj.right && this.shape.left + this.shape.width > obj.left && this.shape.top < obj.bottom && this.shape.top + this.shape.height > obj.top; } }, { - key: 'setText', + key: 'isBoundingBoxOverlappingWith', /** - * Set the content for the popup window. This can be HTML code or text. - * @param {string | Element} content + * Check if this object is overlapping with the provided object + * @param {Object} obj an object with parameters left, top, right, bottom + * @return {boolean} True if location is located on node */ - value: function setText(content) { - if (content instanceof Element) { - this.frame.innerHTML = ''; - this.frame.appendChild(content); - } else { - this.frame.innerHTML = content; // string containing text or HTML - } + value: function isBoundingBoxOverlappingWith(obj) { + return this.shape.boundingBox.left < obj.right && this.shape.boundingBox.right > obj.left && this.shape.boundingBox.top < obj.bottom && this.shape.boundingBox.bottom > obj.top; } - }, { - key: 'show', + }], [{ + key: 'parseOptions', /** - * Show the popup window - * @param {boolean} [doShow] Show or hide the window + * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. + * Static so it can also be used by the handler. + * @param parentOptions + * @param newOptions */ - value: function show(doShow) { - if (doShow === undefined) { - doShow = true; - } + value: function parseOptions(parentOptions, newOptions) { + var allowDeletion = arguments[2] === undefined ? false : arguments[2]; - if (doShow === true) { - var height = this.frame.clientHeight; - var width = this.frame.clientWidth; - var maxHeight = this.frame.parentNode.clientHeight; - var maxWidth = this.frame.parentNode.clientWidth; + var fields = ['color', 'font', 'fixed', 'shadow']; + util.selectiveNotDeepExtend(fields, parentOptions, newOptions); - var top = this.y - height; - if (top + height + this.padding > maxHeight) { - top = maxHeight - height - this.padding; - } - if (top < this.padding) { - top = this.padding; - } + // merge the shadow options into the parent. + util.mergeOptions(parentOptions, newOptions, 'shadow'); - var left = this.x; - if (left + width + this.padding > maxWidth) { - left = maxWidth - width - this.padding; - } - if (left < this.padding) { - left = this.padding; - } + // individual shape newOptions + if (newOptions.color !== undefined && newOptions.color !== null) { + var parsedColor = util.parseColor(newOptions.color); + util.fillIfDefined(parentOptions.color, parsedColor); + } else if (allowDeletion === true) { + parentOptions.color = undefined; + delete parentOptions.color; + } - this.frame.style.left = left + 'px'; - this.frame.style.top = top + 'px'; - this.frame.style.visibility = 'visible'; - this.hidden = false; - } else { - this.hide(); + if (newOptions.fixed !== undefined && newOptions.fixed !== null) { + if (typeof newOptions.fixed === 'boolean') { + parentOptions.fixed.x = newOptions.fixed; + parentOptions.fixed.y = newOptions.fixed; + } else { + if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') { + parentOptions.fixed.x = newOptions.fixed.x; + } + if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') { + parentOptions.fixed.y = newOptions.fixed.y; + } + } } - } - }, { - key: 'hide', - /** - * Hide the popup window - */ - value: function hide() { - this.hidden = true; - this.frame.style.visibility = 'hidden'; + if (newOptions.font !== undefined) { + _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions); + } } }]); - return Popup; + return Node; })(); - exports['default'] = Popup; + exports['default'] = Node; module.exports = exports['default']; /***/ }, @@ -37444,7 +37528,6 @@ return /******/ (function(modules) { // webpackBootstrap var preventDefault = options && options.preventDefault || false; var container = options && options.container || window; - var _exportFunctions = {}; var _bound = {keydown:{}, keyup:{}}; var _keys = {}; @@ -37578,281 +37661,43 @@ return /******/ (function(modules) { // webpackBootstrap if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { newBindings.push(_bound[type][_keys[key].code][i]); } - } - } - _bound[type][_keys[key].code] = newBindings; - } - else { - _bound[type][_keys[key].code] = []; - } - }; - - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; - - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); - }; - - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); - - // return the public functions. - return _exportFunctions; - } - - return keycharm; - })); - - - - -/***/ }, -/* 89 */ -/***/ function(module, exports, __webpack_require__) { - - function webpackContext(req) { - throw new Error("Cannot find module '" + req + "'."); - } - webpackContext.keys = function() { return []; }; - webpackContext.resolve = webpackContext; - module.exports = webpackContext; - webpackContext.id = 89; - - -/***/ }, -/* 90 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; - } - - -/***/ }, -/* 91 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; - - /* WEBPACK VAR INJECTION */}.call(exports, {})) - -/***/ }, -/* 92 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - Object.defineProperty(exports, '__esModule', { - value: true - }); - - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - - var _NodeBase2 = __webpack_require__(109); - - var _NodeBase3 = _interopRequireDefault(_NodeBase2); - - 'use strict'; - - var Box = (function (_NodeBase) { - function Box(options, body, labelModule) { - _classCallCheck(this, Box); - - _get(Object.getPrototypeOf(Box.prototype), 'constructor', this).call(this, options, body, labelModule); - } - - _inherits(Box, _NodeBase); - - _createClass(Box, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; - } - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = selected ? selectionLineWidth : borderWidth; - ctx.lineWidth /= this.body.view.scale; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - - var borderRadius = 6; - ctx.roundRect(this.left, this.top, this.width, this.height, borderRadius); - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); - - // disable shadows for other elements. - this.disableShadow(ctx); - - ctx.stroke(); - - this.updateBoundingBox(x, y); - this.labelModule.draw(ctx, x, y, selected); - } - }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; - - this.boundingBox.left = this.left; - this.boundingBox.top = this.top; - this.boundingBox.bottom = this.top + this.height; - this.boundingBox.right = this.left + this.width; - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); - } - }]); - - return Box; - })(_NodeBase3['default']); - - exports['default'] = Box; - module.exports = exports['default']; - -/***/ }, -/* 93 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - Object.defineProperty(exports, '__esModule', { - value: true - }); - - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - - var _CircleImageBase2 = __webpack_require__(110); + } + } + _bound[type][_keys[key].code] = newBindings; + } + else { + _bound[type][_keys[key].code] = []; + } + }; - var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2); + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; - 'use strict'; + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; - var Circle = (function (_CircleImageBase) { - function Circle(options, body, labelModule) { - _classCallCheck(this, Circle); + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,true); - _get(Object.getPrototypeOf(Circle.prototype), 'constructor', this).call(this, options, body, labelModule); + // return the public functions. + return _exportFunctions; } - _inherits(Circle, _CircleImageBase); - - _createClass(Circle, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; - this.options.size = diameter / 2; - - this.width = diameter; - this.height = diameter; - } - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - this._drawRawCircle(ctx, x, y, selected, hover, this.options.size); - - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; + return keycharm; + })); - this.updateBoundingBox(x, y); - this.labelModule.draw(ctx, x, y, selected); - } - }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); - } - }]); - return Circle; - })(_CircleImageBase3['default']); - exports['default'] = Circle; - module.exports = exports['default']; /***/ }, -/* 94 */ +/* 89 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37873,93 +37718,144 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _CircleImageBase2 = __webpack_require__(110); - - var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2); + var _utilBezierEdgeBase = __webpack_require__(109); - 'use strict'; + var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); - var CircularImage = (function (_CircleImageBase) { - function CircularImage(options, body, labelModule, imageObj) { - _classCallCheck(this, CircularImage); + var BezierEdgeDynamic = (function (_BezierEdgeBase) { + function BezierEdgeDynamic(options, body, labelModule) { + _classCallCheck(this, BezierEdgeDynamic); - _get(Object.getPrototypeOf(CircularImage.prototype), 'constructor', this).call(this, options, body, labelModule); - this.imageObj = imageObj; - this._swapToImageResizeWhenImageLoaded = true; + //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. + _get(Object.getPrototypeOf(BezierEdgeDynamic.prototype), 'constructor', this).call(this, options, body, labelModule); // --> this calls the setOptions below } - _inherits(CircularImage, _CircleImageBase); + _inherits(BezierEdgeDynamic, _BezierEdgeBase); - _createClass(CircularImage, [{ - key: 'resize', - value: function resize() { - if (this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined) { - if (!this.width) { - var diameter = this.options.size * 2; - this.width = diameter; - this.height = diameter; - this._swapToImageResizeWhenImageLoaded = true; - } + _createClass(BezierEdgeDynamic, [{ + key: 'setOptions', + value: function setOptions(options) { + this.options = options; + this.from = this.body.nodes[this.options.from]; + this.to = this.body.nodes[this.options.to]; + this.id = this.options.id; + this.setupSupportNode(); + + // fix weird behaviour + if (this.from.id === this.to.id) { + this.via.setOptions({ physics: false }); } else { - if (this._swapToImageResizeWhenImageLoaded) { - this.width = undefined; - this.height = undefined; - this._swapToImageResizeWhenImageLoaded = false; - } - this._resizeImage(); + this.via.setOptions({ physics: true }); } } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(); - - this.left = x - this.width / 2; - this.top = y - this.height / 2; + key: 'cleanup', + value: function cleanup() { + if (this.via !== undefined) { + delete this.body.nodes[this.via.id]; + this.via = undefined; + return true; + } + return false; + } + }, { + key: 'togglePhysics', + value: function togglePhysics(status) { + this.via.setOptions({ physics: status }); + } + }, { + key: 'setupSupportNode', - var size = Math.min(0.5 * this.height, 0.5 * this.width); - this._drawRawCircle(ctx, x, y, selected, hover, size); + /** + * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but + * are used for the force calculation. + * + * The changed data is not called, if needed, it is returned by the main edge constructor. + * @private + */ + value: function setupSupportNode() { + if (this.via === undefined) { + var nodeId = 'edgeId:' + this.id; + var node = this.body.functions.createNode({ + id: nodeId, + shape: 'circle', + physics: true, + hidden: true + }); + this.body.nodes[nodeId] = node; + this.via = node; + this.via.parentEdgeId = this.id; + this.positionBezierNode(); + } + } + }, { + key: 'positionBezierNode', + value: function positionBezierNode() { + if (this.via !== undefined && this.from !== undefined && this.to !== undefined) { + this.via.x = 0.5 * (this.from.x + this.to.x); + this.via.y = 0.5 * (this.from.y + this.to.y); + } else if (this.via !== undefined) { + this.via.x = 0; + this.via.y = 0; + } + } + }, { + key: '_line', - ctx.save(); - ctx.circle(x, y, size); + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + value: function _line(ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y); + // draw shadow if enabled + this.enableShadow(ctx); ctx.stroke(); - ctx.clip(); - - this._drawImageAtPosition(ctx); - - ctx.restore(); + this.disableShadow(ctx); + return this.via; + } + }, { + key: 'getPoint', - this._drawImageLabel(ctx, x, y, selected); + /** + * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way + * @param percentage + * @param via + * @returns {{x: number, y: number}} + * @private + */ + value: function getPoint(percentage) { + var t = percentage; + var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * this.via.x + Math.pow(t, 2) * this.to.x; + var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * this.via.y + Math.pow(t, 2) * this.to.y; - this.updateBoundingBox(x, y); + return { x: x, y: y }; } }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); + key: '_findBorderPosition', + value: function _findBorderPosition(nearNode, ctx) { + return this._findBorderPositionBezier(nearNode, ctx, this.via); } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); + key: '_getDistanceToEdge', + value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { + // x3,y3 is the point + return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via); } }]); - return CircularImage; - })(_CircleImageBase3['default']); + return BezierEdgeDynamic; + })(_utilBezierEdgeBase2['default']); - exports['default'] = CircularImage; + exports['default'] = BezierEdgeDynamic; module.exports = exports['default']; /***/ }, -/* 95 */ +/* 90 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37970,9 +37866,9 @@ return /******/ (function(modules) { // webpackBootstrap var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x4, + property = _x5, + receiver = _x6; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -37980,94 +37876,252 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _NodeBase2 = __webpack_require__(109); + var _utilBezierEdgeBase = __webpack_require__(109); - var _NodeBase3 = _interopRequireDefault(_NodeBase2); - - 'use strict'; + var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); - var Database = (function (_NodeBase) { - function Database(options, body, labelModule) { - _classCallCheck(this, Database); + var BezierEdgeStatic = (function (_BezierEdgeBase) { + function BezierEdgeStatic(options, body, labelModule) { + _classCallCheck(this, BezierEdgeStatic); - _get(Object.getPrototypeOf(Database.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Database, _NodeBase); + _inherits(BezierEdgeStatic, _BezierEdgeBase); - _createClass(Database, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - var size = textSize.width + 2 * margin; - this.width = size; - this.height = size; - } + _createClass(BezierEdgeStatic, [{ + key: 'cleanup', + value: function cleanup() { + return false; } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = this.selected ? selectionLineWidth : borderWidth; - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + key: '_line', - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height); + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + value: function _line(ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + var via = this._getViaCoordinates(); + var returnValue = via; + // fallback to normal straight edges + if (via.x === undefined) { + ctx.lineTo(this.to.x, this.to.y); + returnValue = undefined; + } else { + ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y); + } // draw shadow if enabled this.enableShadow(ctx); - ctx.fill(); - - // disable shadows for other elements. + ctx.stroke(); this.disableShadow(ctx); + return returnValue; + } + }, { + key: '_getViaCoordinates', + value: function _getViaCoordinates() { + var xVia = undefined; + var yVia = undefined; + var factor = this.options.smooth.roundness; + var type = this.options.smooth.type; + var dx = Math.abs(this.from.x - this.to.x); + var dy = Math.abs(this.from.y - this.to.y); + if (type === 'discrete' || type === 'diagonalCross') { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + } + } else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + } + } + if (type === 'discrete') { + xVia = dx < factor * dy ? this.from.x : xVia; + } + } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + } + } else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + } + } + if (type === 'discrete') { + yVia = dy < factor * dx ? this.from.y : yVia; + } + } + } else if (type === 'straightCross') { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + // up - down + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } else { + yVia = this.to.y + (1 - factor) * dy; + } + } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + // left - right + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } + } else if (type === 'horizontal') { + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } else if (type === 'vertical') { + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } else { + yVia = this.to.y + (1 - factor) * dy; + } + } else if (type === 'curvedCW') { + dx = this.to.x - this.from.x; + dy = this.from.y - this.to.y; + var radius = Math.sqrt(dx * dx + dy * dy); + var pi = Math.PI; - ctx.stroke(); + var originalAngle = Math.atan2(dy, dx); + var myAngle = (originalAngle + (factor * 0.5 + 0.5) * pi) % (2 * pi); - this.updateBoundingBox(x, y); + xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); + yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); + } else if (type === 'curvedCCW') { + dx = this.to.x - this.from.x; + dy = this.from.y - this.to.y; + var radius = Math.sqrt(dx * dx + dy * dy); + var pi = Math.PI; - this.labelModule.draw(ctx, x, y, selected); + var originalAngle = Math.atan2(dy, dx); + var myAngle = (originalAngle + (-factor * 0.5 + 0.5) * pi) % (2 * pi); + + xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); + yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); + } else { + // continuous + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + } else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } + } + } + } + return { x: xVia, y: yVia }; } }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; + key: '_findBorderPosition', + value: function _findBorderPosition(nearNode, ctx) { + var options = arguments[2] === undefined ? {} : arguments[2]; + + return this._findBorderPositionBezier(nearNode, ctx, options.via); + } + }, { + key: '_getDistanceToEdge', + value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { + var via = arguments[6] === undefined ? this._getViaCoordinates() : arguments[6]; + // x3,y3 is the point + return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via); + } + }, { + key: 'getPoint', + + /** + * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way + * @param percentage + * @param via + * @returns {{x: number, y: number}} + * @private + */ + value: function getPoint(percentage) { + var via = arguments[1] === undefined ? this._getViaCoordinates() : arguments[1]; + + var t = percentage; + var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * this.to.x; + var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * this.to.y; - this.boundingBox.left = this.left; - this.boundingBox.top = this.top; - this.boundingBox.bottom = this.top + this.height; - this.boundingBox.right = this.left + this.width; - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + return { x: x, y: y }; } }]); - return Database; - })(_NodeBase3['default']); + return BezierEdgeStatic; + })(_utilBezierEdgeBase2['default']); - exports['default'] = Database; + exports['default'] = BezierEdgeStatic; module.exports = exports['default']; /***/ }, -/* 96 */ +/* 91 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38088,106 +38142,111 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _ShapeBase2 = __webpack_require__(111); + var _utilEdgeBase = __webpack_require__(110); - var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); - - 'use strict'; + var _utilEdgeBase2 = _interopRequireDefault(_utilEdgeBase); - var Diamond = (function (_ShapeBase) { - function Diamond(options, body, labelModule) { - _classCallCheck(this, Diamond); + var StraightEdge = (function (_EdgeBase) { + function StraightEdge(options, body, labelModule) { + _classCallCheck(this, StraightEdge); - _get(Object.getPrototypeOf(Diamond.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(StraightEdge.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Diamond, _ShapeBase); + _inherits(StraightEdge, _EdgeBase); - _createClass(Diamond, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); + _createClass(StraightEdge, [{ + key: 'cleanup', + value: function cleanup() { + return false; } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'diamond', 4, x, y, selected, hover); + key: '_line', + + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + value: function _line(ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + ctx.lineTo(this.to.x, this.to.y); + // draw shadow if enabled + this.enableShadow(ctx); + ctx.stroke(); + this.disableShadow(ctx); + return undefined; } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); - } - }]); - - return Diamond; - })(_ShapeBase3['default']); - - exports['default'] = Diamond; - module.exports = exports['default']; - -/***/ }, -/* 97 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - Object.defineProperty(exports, '__esModule', { - value: true - }); - - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - - var _ShapeBase2 = __webpack_require__(111); - - var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); - - 'use strict'; + key: 'getPoint', - var Dot = (function (_ShapeBase) { - function Dot(options, body, labelModule) { - _classCallCheck(this, Dot); + /** + * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way + * @param percentage + * @param via + * @returns {{x: number, y: number}} + * @private + */ + value: function getPoint(percentage) { + return { + x: (1 - percentage) * this.from.x + percentage * this.to.x, + y: (1 - percentage) * this.from.y + percentage * this.to.y + }; + } + }, { + key: '_findBorderPosition', + value: function _findBorderPosition(nearNode, ctx) { + var node1 = this.to; + var node2 = this.from; + if (nearNode.id === this.from.id) { + node1 = this.from; + node2 = this.to; + } - _get(Object.getPrototypeOf(Dot.prototype), 'constructor', this).call(this, options, body, labelModule); - } + var angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); + var dx = node1.x - node2.x; + var dy = node1.y - node2.y; + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = nearNode.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - _inherits(Dot, _ShapeBase); + var borderPos = {}; + borderPos.x = (1 - toBorderPoint) * node2.x + toBorderPoint * node1.x; + borderPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y; - _createClass(Dot, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'circle', 2, x, y, selected, hover); + return borderPos; } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - return this.options.size + this.options.borderWidth; + key: '_getDistanceToEdge', + value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { + // x3,y3 is the point + return this._getDistanceToLine(x1, y1, x2, y2, x3, y3); } }]); - return Dot; - })(_ShapeBase3['default']); + return StraightEdge; + })(_utilEdgeBase2['default']); - exports['default'] = Dot; + exports['default'] = StraightEdge; module.exports = exports['default']; /***/ }, -/* 98 */ +/* 92 */ +/***/ function(module, exports, __webpack_require__) { + + function webpackContext(req) { + throw new Error("Cannot find module '" + req + "'."); + } + webpackContext.keys = function() { return []; }; + webpackContext.resolve = webpackContext; + module.exports = webpackContext; + webpackContext.id = 92; + + +/***/ }, +/* 93 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38208,52 +38267,51 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _NodeBase2 = __webpack_require__(109); + var _utilNodeBase = __webpack_require__(111); - var _NodeBase3 = _interopRequireDefault(_NodeBase2); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); 'use strict'; - var Ellipse = (function (_NodeBase) { - function Ellipse(options, body, labelModule) { - _classCallCheck(this, Ellipse); + var Box = (function (_NodeBase) { + function Box(options, body, labelModule) { + _classCallCheck(this, Box); - _get(Object.getPrototypeOf(Ellipse.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Box.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Ellipse, _NodeBase); + _inherits(Box, _NodeBase); - _createClass(Ellipse, [{ + _createClass(Box, [{ key: 'resize', value: function resize(ctx, selected) { if (this.width === undefined) { + var margin = 5; var textSize = this.labelModule.getTextSize(ctx, selected); - - this.width = textSize.width * 1.5; - this.height = textSize.height * 2; - if (this.width < this.height) { - this.width = this.height; - } + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + this.radius = 0.5 * this.width; } } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { this.resize(ctx, selected); - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; + this.left = x - this.width / 2; + this.top = y - this.height / 2; var borderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = selected ? selectionLineWidth : borderWidth; ctx.lineWidth /= this.body.view.scale; ctx.lineWidth = Math.min(this.width, ctx.lineWidth); ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.ellipse(this.left, this.top, this.width, this.height); + + var borderRadius = 6; + ctx.roundRect(this.left, this.top, this.width, this.height, borderRadius); // draw shadow if enabled this.enableShadow(ctx); @@ -38282,22 +38340,22 @@ return /******/ (function(modules) { // webpackBootstrap key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { this.resize(ctx); - var a = this.width * 0.5; - var b = this.height * 0.5; + var a = this.width / 2; + var b = this.height / 2; var w = Math.sin(angle) * a; var h = Math.cos(angle) * b; return a * b / Math.sqrt(w * w + h * h); } }]); - return Ellipse; - })(_NodeBase3['default']); + return Box; + })(_utilNodeBase2['default']); - exports['default'] = Ellipse; + exports['default'] = Box; module.exports = exports['default']; /***/ }, -/* 99 */ +/* 94 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38318,175 +38376,59 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _NodeBase2 = __webpack_require__(109); + var _utilCircleImageBase = __webpack_require__(112); - var _NodeBase3 = _interopRequireDefault(_NodeBase2); + var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); 'use strict'; - var Icon = (function (_NodeBase) { - function Icon(options, body, labelModule) { - _classCallCheck(this, Icon); + var Circle = (function (_CircleImageBase) { + function Circle(options, body, labelModule) { + _classCallCheck(this, Circle); - _get(Object.getPrototypeOf(Icon.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Circle.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Icon, _NodeBase); + _inherits(Circle, _CircleImageBase); - _createClass(Icon, [{ + _createClass(Circle, [{ key: 'resize', - value: function resize(ctx) { + value: function resize(ctx, selected) { if (this.width === undefined) { var margin = 5; - var iconSize = { - width: Number(this.options.icon.size), - height: Number(this.options.icon.size) - }; - this.width = iconSize.width + 2 * margin; - this.height = iconSize.height + 2 * margin; - } - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx); - this.options.icon.size = this.options.icon.size || 50; - - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; - this._icon(ctx, x, y, selected); - - if (this.options.label !== undefined) { - var iconTextSpacing = 5; - this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); - } - - this.updateBoundingBox(x, y); - } - }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.boundingBox.top = y - this.options.icon.size * 0.5; - this.boundingBox.left = x - this.options.icon.size * 0.5; - this.boundingBox.right = x + this.options.icon.size * 0.5; - this.boundingBox.bottom = y + this.options.icon.size * 0.5; - - if (this.options.label !== undefined) { - var iconTextSpacing = 5; - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing); - } - } - }, { - key: '_icon', - value: function _icon(ctx, x, y, selected) { - var iconSize = Number(this.options.icon.size); - - if (this.options.icon.code !== undefined) { - ctx.font = (selected ? 'bold ' : '') + iconSize + 'px ' + this.options.icon.face; - - // draw icon - ctx.fillStyle = this.options.icon.color || 'black'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fillText(this.options.icon.code, x, y); + var textSize = this.labelModule.getTextSize(ctx, selected); + var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; + this.options.size = diameter / 2; - // disable shadows for other elements. - this.disableShadow(ctx); - } else { - console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.'); + this.width = diameter; + this.height = diameter; + this.radius = 0.5 * this.width; } } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - this._distanceToBorder(angle); - } - }]); - - return Icon; - })(_NodeBase3['default']); - - exports['default'] = Icon; - module.exports = exports['default']; - -/***/ }, -/* 100 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - Object.defineProperty(exports, '__esModule', { - value: true - }); - - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - - var _CircleImageBase2 = __webpack_require__(110); - - var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2); - - 'use strict'; - - var Image = (function (_CircleImageBase) { - function Image(options, body, labelModule, imageObj) { - _classCallCheck(this, Image); - - _get(Object.getPrototypeOf(Image.prototype), 'constructor', this).call(this, options, body, labelModule); - this.imageObj = imageObj; - } - - _inherits(Image, _CircleImageBase); - - _createClass(Image, [{ - key: 'resize', - value: function resize() { - this._resizeImage(); - } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this.resize(); + this.resize(ctx, selected); this.left = x - this.width / 2; this.top = y - this.height / 2; - this._drawImageAtPosition(ctx); + this._drawRawCircle(ctx, x, y, selected, hover, this.options.size); - this._drawImageLabel(ctx, x, y, selected || hover); + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; this.updateBoundingBox(x, y); + this.labelModule.draw(ctx, x, y, selected); } }, { key: 'updateBoundingBox', value: function updateBoundingBox(x, y) { - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - - if (this.options.label !== undefined) { - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); - } + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; } }, { key: 'distanceToBorder', @@ -38500,14 +38442,14 @@ return /******/ (function(modules) { // webpackBootstrap } }]); - return Image; - })(_CircleImageBase3['default']); + return Circle; + })(_utilCircleImageBase2['default']); - exports['default'] = Image; + exports['default'] = Circle; module.exports = exports['default']; /***/ }, -/* 101 */ +/* 95 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38528,107 +38470,94 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _ShapeBase2 = __webpack_require__(111); + var _utilCircleImageBase = __webpack_require__(112); - var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); + var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); 'use strict'; - var Square = (function (_ShapeBase) { - function Square(options, body, labelModule) { - _classCallCheck(this, Square); + var CircularImage = (function (_CircleImageBase) { + function CircularImage(options, body, labelModule, imageObj) { + _classCallCheck(this, CircularImage); - _get(Object.getPrototypeOf(Square.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(CircularImage.prototype), 'constructor', this).call(this, options, body, labelModule); + this.imageObj = imageObj; + this._swapToImageResizeWhenImageLoaded = true; } - _inherits(Square, _ShapeBase); + _inherits(CircularImage, _CircleImageBase); - _createClass(Square, [{ + _createClass(CircularImage, [{ key: 'resize', value: function resize() { - this._resizeShape(); + if (this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined) { + if (!this.width) { + var diameter = this.options.size * 2; + this.width = diameter; + this.height = diameter; + this._swapToImageResizeWhenImageLoaded = true; + this.radius = 0.5 * this.width; + } + } else { + if (this._swapToImageResizeWhenImageLoaded) { + this.width = undefined; + this.height = undefined; + this._swapToImageResizeWhenImageLoaded = false; + } + this._resizeImage(); + } } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'square', 2, x, y, selected, hover); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); - } - }]); - - return Square; - })(_ShapeBase3['default']); - - exports['default'] = Square; - module.exports = exports['default']; - -/***/ }, -/* 102 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - Object.defineProperty(exports, '__esModule', { - value: true - }); - - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, - property = _x2, - receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + this.resize(); - var _ShapeBase2 = __webpack_require__(111); + this.left = x - this.width / 2; + this.top = y - this.height / 2; - var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); + var size = Math.min(0.5 * this.height, 0.5 * this.width); + this._drawRawCircle(ctx, x, y, selected, hover, size); - 'use strict'; + ctx.save(); + ctx.circle(x, y, size); + ctx.stroke(); + ctx.clip(); - var Star = (function (_ShapeBase) { - function Star(options, body, labelModule) { - _classCallCheck(this, Star); + this._drawImageAtPosition(ctx); - _get(Object.getPrototypeOf(Star.prototype), 'constructor', this).call(this, options, body, labelModule); - } + ctx.restore(); - _inherits(Star, _ShapeBase); + this._drawImageLabel(ctx, x, y, selected); - _createClass(Star, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); + this.updateBoundingBox(x, y); } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'star', 4, x, y, selected, hover); + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { + this.resize(ctx); return this._distanceToBorder(angle); } }]); - return Star; - })(_ShapeBase3['default']); + return CircularImage; + })(_utilCircleImageBase2['default']); - exports['default'] = Star; + exports['default'] = CircularImage; module.exports = exports['default']; /***/ }, -/* 103 */ +/* 96 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38649,74 +38578,95 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _NodeBase2 = __webpack_require__(109); + var _utilNodeBase = __webpack_require__(111); - var _NodeBase3 = _interopRequireDefault(_NodeBase2); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); 'use strict'; - var Text = (function (_NodeBase) { - function Text(options, body, labelModule) { - _classCallCheck(this, Text); + var Database = (function (_NodeBase) { + function Database(options, body, labelModule) { + _classCallCheck(this, Database); - _get(Object.getPrototypeOf(Text.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Database.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Text, _NodeBase); + _inherits(Database, _NodeBase); - _createClass(Text, [{ + _createClass(Database, [{ key: 'resize', value: function resize(ctx, selected) { if (this.width === undefined) { var margin = 5; var textSize = this.labelModule.getTextSize(ctx, selected); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; + var size = textSize.width + 2 * margin; + this.width = size; + this.height = size; + this.radius = 0.5 * this.width; } } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected || hover); + this.resize(ctx, selected); this.left = x - this.width / 2; this.top = y - this.height / 2; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.lineWidth = this.selected ? selectionLineWidth : borderWidth; + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height); + // draw shadow if enabled this.enableShadow(ctx); - this.labelModule.draw(ctx, x, y, selected || hover); + ctx.fill(); // disable shadows for other elements. this.disableShadow(ctx); + ctx.stroke(); + this.updateBoundingBox(x, y); + + this.labelModule.draw(ctx, x, y, selected); } }, { key: 'updateBoundingBox', value: function updateBoundingBox(x, y) { - this.left = x - this.width / 2; - this.top = y - this.height / 2; + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; - this.boundingBox.top = this.top; this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; + this.boundingBox.top = this.top; this.boundingBox.bottom = this.top + this.height; + this.boundingBox.right = this.left + this.width; } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { this.resize(ctx); - return this._distanceToBorder(angle); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); } }]); - return Text; - })(_NodeBase3['default']); + return Database; + })(_utilNodeBase2['default']); - exports['default'] = Text; + exports['default'] = Database; module.exports = exports['default']; /***/ }, -/* 104 */ +/* 97 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38737,22 +38687,22 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _ShapeBase2 = __webpack_require__(111); + var _utilShapeBase = __webpack_require__(113); - var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); 'use strict'; - var Triangle = (function (_ShapeBase) { - function Triangle(options, body, labelModule) { - _classCallCheck(this, Triangle); + var Diamond = (function (_ShapeBase) { + function Diamond(options, body, labelModule) { + _classCallCheck(this, Diamond); - _get(Object.getPrototypeOf(Triangle.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Diamond.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Triangle, _ShapeBase); + _inherits(Diamond, _ShapeBase); - _createClass(Triangle, [{ + _createClass(Diamond, [{ key: 'resize', value: function resize(ctx) { this._resizeShape(); @@ -38760,7 +38710,7 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'triangle', 3, x, y, selected, hover); + this._drawShape(ctx, 'diamond', 4, x, y, selected, hover); } }, { key: 'distanceToBorder', @@ -38769,14 +38719,14 @@ return /******/ (function(modules) { // webpackBootstrap } }]); - return Triangle; - })(_ShapeBase3['default']); + return Diamond; + })(_utilShapeBase2['default']); - exports['default'] = Triangle; + exports['default'] = Diamond; module.exports = exports['default']; /***/ }, -/* 105 */ +/* 98 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38797,22 +38747,22 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _ShapeBase2 = __webpack_require__(111); + var _utilShapeBase = __webpack_require__(113); - var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); 'use strict'; - var TriangleDown = (function (_ShapeBase) { - function TriangleDown(options, body, labelModule) { - _classCallCheck(this, TriangleDown); + var Dot = (function (_ShapeBase) { + function Dot(options, body, labelModule) { + _classCallCheck(this, Dot); - _get(Object.getPrototypeOf(TriangleDown.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Dot.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(TriangleDown, _ShapeBase); + _inherits(Dot, _ShapeBase); - _createClass(TriangleDown, [{ + _createClass(Dot, [{ key: 'resize', value: function resize(ctx) { this._resizeShape(); @@ -38820,23 +38770,23 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover); + this._drawShape(ctx, 'circle', 2, x, y, selected, hover); } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + return this.options.size + this.options.borderWidth; } }]); - return TriangleDown; - })(_ShapeBase3['default']); + return Dot; + })(_utilShapeBase2['default']); - exports['default'] = TriangleDown; + exports['default'] = Dot; module.exports = exports['default']; /***/ }, -/* 106 */ +/* 99 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38857,144 +38807,97 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _BezierEdgeBase2 = __webpack_require__(112); + var _utilNodeBase = __webpack_require__(111); - var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - var BezierEdgeDynamic = (function (_BezierEdgeBase) { - function BezierEdgeDynamic(options, body, labelModule) { - _classCallCheck(this, BezierEdgeDynamic); + 'use strict'; - //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. - _get(Object.getPrototypeOf(BezierEdgeDynamic.prototype), 'constructor', this).call(this, options, body, labelModule); // --> this calls the setOptions below + var Ellipse = (function (_NodeBase) { + function Ellipse(options, body, labelModule) { + _classCallCheck(this, Ellipse); + + _get(Object.getPrototypeOf(Ellipse.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(BezierEdgeDynamic, _BezierEdgeBase); + _inherits(Ellipse, _NodeBase); - _createClass(BezierEdgeDynamic, [{ - key: 'setOptions', - value: function setOptions(options) { - this.options = options; - this.from = this.body.nodes[this.options.from]; - this.to = this.body.nodes[this.options.to]; - this.id = this.options.id; - this.setupSupportNode(); + _createClass(Ellipse, [{ + key: 'resize', + value: function resize(ctx, selected) { + if (this.width === undefined) { + var textSize = this.labelModule.getTextSize(ctx, selected); - // fix weird behaviour - if (this.from.id === this.to.id) { - this.via.setOptions({ physics: false }); - } else { - this.via.setOptions({ physics: true }); - } - } - }, { - key: 'cleanup', - value: function cleanup() { - if (this.via !== undefined) { - delete this.body.nodes[this.via.id]; - this.via = undefined; - return true; + this.width = textSize.width * 1.5; + this.height = textSize.height * 2; + if (this.width < this.height) { + this.width = this.height; + } + this.radius = 0.5 * this.width; } - return false; - } - }, { - key: 'togglePhysics', - value: function togglePhysics(status) { - this.via.setOptions({ physics: status }); } }, { - key: 'setupSupportNode', + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx, selected); + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; - /** - * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but - * are used for the force calculation. - * - * The changed data is not called, if needed, it is returned by the main edge constructor. - * @private - */ - value: function setupSupportNode() { - if (this.via === undefined) { - var nodeId = 'edgeId:' + this.id; - var node = this.body.functions.createNode({ - id: nodeId, - shape: 'circle', - physics: true, - hidden: true - }); - this.body.nodes[nodeId] = node; - this.via = node; - this.via.parentEdgeId = this.id; - this.positionBezierNode(); - } - } - }, { - key: 'positionBezierNode', - value: function positionBezierNode() { - if (this.via !== undefined && this.from !== undefined && this.to !== undefined) { - this.via.x = 0.5 * (this.from.x + this.to.x); - this.via.y = 0.5 * (this.from.y + this.to.y); - } else if (this.via !== undefined) { - this.via.x = 0; - this.via.y = 0; - } - } - }, { - key: '_line', + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + + ctx.lineWidth = selected ? selectionLineWidth : borderWidth; + ctx.lineWidth /= this.body.view.scale; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.ellipse(this.left, this.top, this.width, this.height); - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _line(ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y); // draw shadow if enabled this.enableShadow(ctx); - ctx.stroke(); + ctx.fill(); + + // disable shadows for other elements. this.disableShadow(ctx); - return this.via; - } - }, { - key: 'getPoint', - /** - * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way - * @param percentage - * @param via - * @returns {{x: number, y: number}} - * @private - */ - value: function getPoint(percentage) { - var t = percentage; - var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * this.via.x + Math.pow(t, 2) * this.to.x; - var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * this.via.y + Math.pow(t, 2) * this.to.y; + ctx.stroke(); - return { x: x, y: y }; + this.updateBoundingBox(x, y); + this.labelModule.draw(ctx, x, y, selected); } }, { - key: '_findBorderPosition', - value: function _findBorderPosition(nearNode, ctx) { - return this._findBorderPositionBezier(nearNode, ctx, this.via); + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + + this.boundingBox.left = this.left; + this.boundingBox.top = this.top; + this.boundingBox.bottom = this.top + this.height; + this.boundingBox.right = this.left + this.width; } }, { - key: '_getDistanceToEdge', - value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { - // x3,y3 is the point - return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via); + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + var a = this.width * 0.5; + var b = this.height * 0.5; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); } }]); - return BezierEdgeDynamic; - })(_BezierEdgeBase3['default']); + return Ellipse; + })(_utilNodeBase2['default']); - exports['default'] = BezierEdgeDynamic; + exports['default'] = Ellipse; module.exports = exports['default']; /***/ }, -/* 107 */ +/* 100 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39005,9 +38908,9 @@ return /******/ (function(modules) { // webpackBootstrap var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x4, - property = _x5, - receiver = _x6; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -39015,252 +38918,106 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _BezierEdgeBase2 = __webpack_require__(112); + var _utilNodeBase = __webpack_require__(111); - var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - var BezierEdgeStatic = (function (_BezierEdgeBase) { - function BezierEdgeStatic(options, body, labelModule) { - _classCallCheck(this, BezierEdgeStatic); + 'use strict'; - _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule); + var Icon = (function (_NodeBase) { + function Icon(options, body, labelModule) { + _classCallCheck(this, Icon); + + _get(Object.getPrototypeOf(Icon.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(BezierEdgeStatic, _BezierEdgeBase); + _inherits(Icon, _NodeBase); - _createClass(BezierEdgeStatic, [{ - key: 'cleanup', - value: function cleanup() { - return false; + _createClass(Icon, [{ + key: 'resize', + value: function resize(ctx) { + if (this.width === undefined) { + var margin = 5; + var iconSize = { + width: Number(this.options.icon.size), + height: Number(this.options.icon.size) + }; + this.width = iconSize.width + 2 * margin; + this.height = iconSize.height + 2 * margin; + this.radius = 0.5 * this.width; + } } }, { - key: '_line', + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx); + this.options.icon.size = this.options.icon.size || 50; - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _line(ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - var via = this._getViaCoordinates(); - var returnValue = via; + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + this._icon(ctx, x, y, selected); - // fallback to normal straight edges - if (via.x === undefined) { - ctx.lineTo(this.to.x, this.to.y); - returnValue = undefined; - } else { - ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y); + if (this.options.label !== undefined) { + var iconTextSpacing = 5; + this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); } - // draw shadow if enabled - this.enableShadow(ctx); - ctx.stroke(); - this.disableShadow(ctx); - return returnValue; + + this.updateBoundingBox(x, y); } }, { - key: '_getViaCoordinates', - value: function _getViaCoordinates() { - var xVia = undefined; - var yVia = undefined; - var factor = this.options.smooth.roundness; - var type = this.options.smooth.type; - var dx = Math.abs(this.from.x - this.to.x); - var dy = Math.abs(this.from.y - this.to.y); - if (type === 'discrete' || type === 'diagonalCross') { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - } - } else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - } - } - if (type === 'discrete') { - xVia = dx < factor * dy ? this.from.x : xVia; - } - } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - } - } else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - } - } - if (type === 'discrete') { - yVia = dy < factor * dx ? this.from.y : yVia; - } - } - } else if (type === 'straightCross') { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - // up - down - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } else { - yVia = this.to.y + (1 - factor) * dy; - } - } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - // left - right - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; - } - } else if (type === 'horizontal') { - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; - } else if (type === 'vertical') { - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } else { - yVia = this.to.y + (1 - factor) * dy; - } - } else if (type === 'curvedCW') { - dx = this.to.x - this.from.x; - dy = this.from.y - this.to.y; - var radius = Math.sqrt(dx * dx + dy * dy); - var pi = Math.PI; - - var originalAngle = Math.atan2(dy, dx); - var myAngle = (originalAngle + (factor * 0.5 + 0.5) * pi) % (2 * pi); - - xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); - yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); - } else if (type === 'curvedCCW') { - dx = this.to.x - this.from.x; - dy = this.from.y - this.to.y; - var radius = Math.sqrt(dx * dx + dy * dy); - var pi = Math.PI; - - var originalAngle = Math.atan2(dy, dx); - var myAngle = (originalAngle + (-factor * 0.5 + 0.5) * pi) % (2 * pi); + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.icon.size * 0.5; + this.boundingBox.left = x - this.options.icon.size * 0.5; + this.boundingBox.right = x + this.options.icon.size * 0.5; + this.boundingBox.bottom = y + this.options.icon.size * 0.5; - xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); - yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); - } else { - // continuous - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } - } else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } - } - } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } - } else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } - } - } + if (this.options.label !== undefined) { + var iconTextSpacing = 5; + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing); } - return { x: xVia, y: yVia }; } }, { - key: '_findBorderPosition', - value: function _findBorderPosition(nearNode, ctx) { - var options = arguments[2] === undefined ? {} : arguments[2]; + key: '_icon', + value: function _icon(ctx, x, y, selected) { + var iconSize = Number(this.options.icon.size); - return this._findBorderPositionBezier(nearNode, ctx, options.via); - } - }, { - key: '_getDistanceToEdge', - value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { - var via = arguments[6] === undefined ? this._getViaCoordinates() : arguments[6]; - // x3,y3 is the point - return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via); - } - }, { - key: 'getPoint', + if (this.options.icon.code !== undefined) { + ctx.font = (selected ? 'bold ' : '') + iconSize + 'px ' + this.options.icon.face; - /** - * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way - * @param percentage - * @param via - * @returns {{x: number, y: number}} - * @private - */ - value: function getPoint(percentage) { - var via = arguments[1] === undefined ? this._getViaCoordinates() : arguments[1]; + // draw icon + ctx.fillStyle = this.options.icon.color || 'black'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; - var t = percentage; - var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * this.to.x; - var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * this.to.y; + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fillText(this.options.icon.code, x, y); - return { x: x, y: y }; + // disable shadows for other elements. + this.disableShadow(ctx); + } else { + console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.'); + } + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + this._distanceToBorder(angle); } }]); - return BezierEdgeStatic; - })(_BezierEdgeBase3['default']); + return Icon; + })(_utilNodeBase2['default']); - exports['default'] = BezierEdgeStatic; + exports['default'] = Icon; module.exports = exports['default']; /***/ }, -/* 108 */ +/* 101 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39281,98 +39038,138 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _EdgeBase2 = __webpack_require__(113); + var _utilCircleImageBase = __webpack_require__(112); - var _EdgeBase3 = _interopRequireDefault(_EdgeBase2); + var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); - var StraightEdge = (function (_EdgeBase) { - function StraightEdge(options, body, labelModule) { - _classCallCheck(this, StraightEdge); + 'use strict'; - _get(Object.getPrototypeOf(StraightEdge.prototype), 'constructor', this).call(this, options, body, labelModule); + var Image = (function (_CircleImageBase) { + function Image(options, body, labelModule, imageObj) { + _classCallCheck(this, Image); + + _get(Object.getPrototypeOf(Image.prototype), 'constructor', this).call(this, options, body, labelModule); + this.imageObj = imageObj; } - _inherits(StraightEdge, _EdgeBase); + _inherits(Image, _CircleImageBase); - _createClass(StraightEdge, [{ - key: 'cleanup', - value: function cleanup() { - return false; + _createClass(Image, [{ + key: 'resize', + value: function resize() { + this._resizeImage(); } }, { - key: '_line', + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(); + this.left = x - this.width / 2; + this.top = y - this.height / 2; - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _line(ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - ctx.lineTo(this.to.x, this.to.y); - // draw shadow if enabled - this.enableShadow(ctx); - ctx.stroke(); - this.disableShadow(ctx); - return undefined; + this._drawImageAtPosition(ctx); + + this._drawImageLabel(ctx, x, y, selected || hover); + + this.updateBoundingBox(x, y); } }, { - key: 'getPoint', + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width / 2; + this.top = y - this.height / 2; - /** - * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way - * @param percentage - * @param via - * @returns {{x: number, y: number}} - * @private - */ - value: function getPoint(percentage) { - return { - x: (1 - percentage) * this.from.x + percentage * this.to.x, - y: (1 - percentage) * this.from.y + percentage * this.to.y - }; + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; + + if (this.options.label !== undefined) { + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); + } } }, { - key: '_findBorderPosition', - value: function _findBorderPosition(nearNode, ctx) { - var node1 = this.to; - var node2 = this.from; - if (nearNode.id === this.from.id) { - node1 = this.from; - node2 = this.to; - } + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); + } + }]); - var angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); - var dx = node1.x - node2.x; - var dy = node1.y - node2.y; - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = nearNode.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + return Image; + })(_utilCircleImageBase2['default']); - var borderPos = {}; - borderPos.x = (1 - toBorderPoint) * node2.x + toBorderPoint * node1.x; - borderPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y; + exports['default'] = Image; + module.exports = exports['default']; - return borderPos; +/***/ }, +/* 102 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + + var _utilShapeBase = __webpack_require__(113); + + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + + 'use strict'; + + var Square = (function (_ShapeBase) { + function Square(options, body, labelModule) { + _classCallCheck(this, Square); + + _get(Object.getPrototypeOf(Square.prototype), 'constructor', this).call(this, options, body, labelModule); + } + + _inherits(Square, _ShapeBase); + + _createClass(Square, [{ + key: 'resize', + value: function resize() { + this._resizeShape(); } }, { - key: '_getDistanceToEdge', - value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { - // x3,y3 is the point - return this._getDistanceToLine(x1, y1, x2, y2, x3, y3); + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'square', 2, x, y, selected, hover); + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + return this._distanceToBorder(angle); } }]); - return StraightEdge; - })(_EdgeBase3['default']); + return Square; + })(_utilShapeBase2['default']); - exports['default'] = StraightEdge; + exports['default'] = Square; module.exports = exports['default']; /***/ }, -/* 109 */ +/* 103 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39383,62 +39180,56 @@ return /******/ (function(modules) { // webpackBootstrap var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var NodeBase = (function () { - function NodeBase(options, body, labelModule) { - _classCallCheck(this, NodeBase); + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - this.body = body; - this.labelModule = labelModule; - this.setOptions(options); - this.top = undefined; - this.left = undefined; - this.height = undefined; - this.boundingBox = { top: 0, left: 0, right: 0, bottom: 0 }; + var _utilShapeBase = __webpack_require__(113); + + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + + 'use strict'; + + var Star = (function (_ShapeBase) { + function Star(options, body, labelModule) { + _classCallCheck(this, Star); + + _get(Object.getPrototypeOf(Star.prototype), 'constructor', this).call(this, options, body, labelModule); } - _createClass(NodeBase, [{ - key: 'setOptions', - value: function setOptions(options) { - this.options = options; - } - }, { - key: '_distanceToBorder', - value: function _distanceToBorder(angle) { - var borderWidth = 1; - return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; + _inherits(Star, _ShapeBase); + + _createClass(Star, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); } }, { - key: 'enableShadow', - value: function enableShadow(ctx) { - if (this.options.shadow.enabled === true) { - ctx.shadowColor = 'rgba(0,0,0,0.5)'; - ctx.shadowBlur = this.options.shadow.size; - ctx.shadowOffsetX = this.options.shadow.x; - ctx.shadowOffsetY = this.options.shadow.y; - } + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'star', 4, x, y, selected, hover); } }, { - key: 'disableShadow', - value: function disableShadow(ctx) { - if (this.options.shadow.enabled === true) { - ctx.shadowColor = 'rgba(0,0,0,0)'; - ctx.shadowBlur = 0; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - } + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this._distanceToBorder(angle); } }]); - return NodeBase; - })(); + return Star; + })(_utilShapeBase2['default']); - exports['default'] = NodeBase; + exports['default'] = Star; module.exports = exports['default']; /***/ }, -/* 110 */ +/* 104 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39459,114 +39250,75 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _NodeBase2 = __webpack_require__(109); + var _utilNodeBase = __webpack_require__(111); - var _NodeBase3 = _interopRequireDefault(_NodeBase2); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - var CircleImageBase = (function (_NodeBase) { - function CircleImageBase(options, body, labelModule) { - _classCallCheck(this, CircleImageBase); + 'use strict'; - _get(Object.getPrototypeOf(CircleImageBase.prototype), 'constructor', this).call(this, options, body, labelModule); - this.labelOffset = 0; + var Text = (function (_NodeBase) { + function Text(options, body, labelModule) { + _classCallCheck(this, Text); + + _get(Object.getPrototypeOf(Text.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(CircleImageBase, _NodeBase); + _inherits(Text, _NodeBase); - _createClass(CircleImageBase, [{ - key: '_resizeImage', - value: function _resizeImage() { - if (!this.width || !this.height) { - // undefined or 0 - var width, height, ratio; - if (this.imageObj.width && this.imageObj.height) { - // not undefined or 0 - width = 0; - height = 0; - } - if (this.imageObj.width > this.imageObj.height) { - ratio = this.imageObj.width / this.imageObj.height; - width = this.options.size * 2 * ratio || this.imageObj.width; - height = this.options.size * 2 || this.imageObj.height; - } else { - ratio = this.imageObj.height / this.imageObj.width; - width = this.options.size * 2 || this.imageObj.width; - height = this.options.size * 2 * ratio || this.imageObj.height; - } - this.width = width; - this.height = height; + _createClass(Text, [{ + key: 'resize', + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + this.radius = 0.5 * this.width; } } }, { - key: '_drawRawCircle', - value: function _drawRawCircle(ctx, x, y, selected, hover, size) { - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - - ctx.lineWidth = selected ? selectionLineWidth : borderWidth; - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.circle(x, y, size); + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx, selected || hover); + this.left = x - this.width / 2; + this.top = y - this.height / 2; // draw shadow if enabled this.enableShadow(ctx); - ctx.fill(); + this.labelModule.draw(ctx, x, y, selected || hover); // disable shadows for other elements. this.disableShadow(ctx); - ctx.stroke(); + this.updateBoundingBox(x, y); } }, { - key: '_drawImageAtPosition', - value: function _drawImageAtPosition(ctx) { - if (this.imageObj.width != 0) { - // draw the image - ctx.globalAlpha = 1; - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width / 2; + this.top = y - this.height / 2; - // disable shadows for other elements. - this.disableShadow(ctx); - } + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; } }, { - key: '_drawImageLabel', - value: function _drawImageLabel(ctx, x, y, selected) { - var yLabel; - var offset = 0; - - if (this.height !== undefined) { - offset = this.height * 0.5; - var labelDimensions = this.labelModule.getTextSize(ctx); - if (labelDimensions.lineCount >= 1) { - offset += labelDimensions.height / 2; - } - } - - yLabel = y + offset; - - if (this.options.label) { - this.labelOffset = offset; - } - this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + return this._distanceToBorder(angle); } }]); - return CircleImageBase; - })(_NodeBase3['default']); + return Text; + })(_utilNodeBase2['default']); - exports['default'] = CircleImageBase; + exports['default'] = Text; module.exports = exports['default']; /***/ }, -/* 111 */ +/* 105 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39587,86 +39339,130 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _NodeBase2 = __webpack_require__(109); + var _utilShapeBase = __webpack_require__(113); - var _NodeBase3 = _interopRequireDefault(_NodeBase2); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - var ShapeBase = (function (_NodeBase) { - function ShapeBase(options, body, labelModule) { - _classCallCheck(this, ShapeBase); + 'use strict'; - _get(Object.getPrototypeOf(ShapeBase.prototype), 'constructor', this).call(this, options, body, labelModule); + var Triangle = (function (_ShapeBase) { + function Triangle(options, body, labelModule) { + _classCallCheck(this, Triangle); + + _get(Object.getPrototypeOf(Triangle.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(ShapeBase, _NodeBase); + _inherits(Triangle, _ShapeBase); - _createClass(ShapeBase, [{ - key: '_resizeShape', - value: function _resizeShape() { - if (this.width === undefined) { - var size = 2 * this.options.size; - this.width = size; - this.height = size; - } + _createClass(Triangle, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); } }, { - key: '_drawShape', - value: function _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover) { - this._resizeShape(); + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'triangle', 3, x, y, selected, hover); + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this._distanceToBorder(angle); + } + }]); - this.left = x - this.width / 2; - this.top = y - this.height / 2; + return Triangle; + })(_utilShapeBase2['default']); - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + exports['default'] = Triangle; + module.exports = exports['default']; - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = selected ? selectionLineWidth : borderWidth; - ctx.lineWidth /= this.body.view.scale; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx[shape](x, y, this.options.size); +/***/ }, +/* 106 */ +/***/ function(module, exports, __webpack_require__) { - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); + 'use strict'; - // disable shadows for other elements. - this.disableShadow(ctx); + Object.defineProperty(exports, '__esModule', { + value: true + }); - ctx.stroke(); + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - if (this.options.label !== undefined) { - var yLabel = y + 0.5 * this.height + 3; // the + 3 is to offset it a bit below the node. - this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); - } + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - this.updateBoundingBox(x, y); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + + var _utilShapeBase = __webpack_require__(113); + + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + + 'use strict'; + + var TriangleDown = (function (_ShapeBase) { + function TriangleDown(options, body, labelModule) { + _classCallCheck(this, TriangleDown); + + _get(Object.getPrototypeOf(TriangleDown.prototype), 'constructor', this).call(this, options, body, labelModule); + } + + _inherits(TriangleDown, _ShapeBase); + + _createClass(TriangleDown, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); } }, { - key: 'updateBoundingBox', - value: function updateBoundingBox(x, y) { - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; - - if (this.options.label !== undefined) { - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + 3); - } + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover); + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this._distanceToBorder(angle); } }]); - return ShapeBase; - })(_NodeBase3['default']); + return TriangleDown; + })(_utilShapeBase2['default']); - exports['default'] = ShapeBase; + exports['default'] = TriangleDown; module.exports = exports['default']; /***/ }, -/* 112 */ +/* 107 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; + + /* WEBPACK VAR INJECTION */}.call(exports, {})) + +/***/ }, +/* 108 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } + + +/***/ }, +/* 109 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -39687,7 +39483,7 @@ return /******/ (function(modules) { // webpackBootstrap function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - var _EdgeBase2 = __webpack_require__(113); + var _EdgeBase2 = __webpack_require__(110); var _EdgeBase3 = _interopRequireDefault(_EdgeBase2); @@ -39815,7 +39611,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = exports['default']; /***/ }, -/* 113 */ +/* 110 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -40400,6 +40196,304 @@ return /******/ (function(modules) { // webpackBootstrap exports['default'] = EdgeBase; module.exports = exports['default']; +/***/ }, +/* 111 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + var NodeBase = (function () { + function NodeBase(options, body, labelModule) { + _classCallCheck(this, NodeBase); + + this.body = body; + this.labelModule = labelModule; + this.setOptions(options); + this.top = undefined; + this.left = undefined; + this.height = undefined; + this.width = undefined; + this.radius = undefined; + this.boundingBox = { top: 0, left: 0, right: 0, bottom: 0 }; + } + + _createClass(NodeBase, [{ + key: 'setOptions', + value: function setOptions(options) { + this.options = options; + } + }, { + key: '_distanceToBorder', + value: function _distanceToBorder(angle) { + var borderWidth = 1; + return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; + } + }, { + key: 'enableShadow', + value: function enableShadow(ctx) { + if (this.options.shadow.enabled === true) { + ctx.shadowColor = 'rgba(0,0,0,0.5)'; + ctx.shadowBlur = this.options.shadow.size; + ctx.shadowOffsetX = this.options.shadow.x; + ctx.shadowOffsetY = this.options.shadow.y; + } + } + }, { + key: 'disableShadow', + value: function disableShadow(ctx) { + if (this.options.shadow.enabled === true) { + ctx.shadowColor = 'rgba(0,0,0,0)'; + ctx.shadowBlur = 0; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + } + } + }]); + + return NodeBase; + })(); + + exports['default'] = NodeBase; + module.exports = exports['default']; + +/***/ }, +/* 112 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + + var _utilNodeBase = __webpack_require__(111); + + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + + var CircleImageBase = (function (_NodeBase) { + function CircleImageBase(options, body, labelModule) { + _classCallCheck(this, CircleImageBase); + + _get(Object.getPrototypeOf(CircleImageBase.prototype), 'constructor', this).call(this, options, body, labelModule); + this.labelOffset = 0; + } + + _inherits(CircleImageBase, _NodeBase); + + _createClass(CircleImageBase, [{ + key: '_resizeImage', + value: function _resizeImage() { + if (!this.width || !this.height) { + // undefined or 0 + var width, height, ratio; + if (this.imageObj.width && this.imageObj.height) { + // not undefined or 0 + width = 0; + height = 0; + } + if (this.imageObj.width > this.imageObj.height) { + ratio = this.imageObj.width / this.imageObj.height; + width = this.options.size * 2 * ratio || this.imageObj.width; + height = this.options.size * 2 || this.imageObj.height; + } else { + ratio = this.imageObj.height / this.imageObj.width; + width = this.options.size * 2 || this.imageObj.width; + height = this.options.size * 2 * ratio || this.imageObj.height; + } + this.width = width; + this.height = height; + this.radius = 0.5 * this.width; + } + } + }, { + key: '_drawRawCircle', + value: function _drawRawCircle(ctx, x, y, selected, hover, size) { + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + + ctx.lineWidth = selected ? selectionLineWidth : borderWidth; + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.circle(x, y, size); + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); + + // disable shadows for other elements. + this.disableShadow(ctx); + + ctx.stroke(); + } + }, { + key: '_drawImageAtPosition', + value: function _drawImageAtPosition(ctx) { + if (this.imageObj.width != 0) { + // draw the image + ctx.globalAlpha = 1; + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + + // disable shadows for other elements. + this.disableShadow(ctx); + } + } + }, { + key: '_drawImageLabel', + value: function _drawImageLabel(ctx, x, y, selected) { + var yLabel; + var offset = 0; + + if (this.height !== undefined) { + offset = this.height * 0.5; + var labelDimensions = this.labelModule.getTextSize(ctx); + if (labelDimensions.lineCount >= 1) { + offset += labelDimensions.height / 2; + } + } + + yLabel = y + offset; + + if (this.options.label) { + this.labelOffset = offset; + } + this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + } + }]); + + return CircleImageBase; + })(_utilNodeBase2['default']); + + exports['default'] = CircleImageBase; + module.exports = exports['default']; + +/***/ }, +/* 113 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + + var _utilNodeBase = __webpack_require__(111); + + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + + var ShapeBase = (function (_NodeBase) { + function ShapeBase(options, body, labelModule) { + _classCallCheck(this, ShapeBase); + + _get(Object.getPrototypeOf(ShapeBase.prototype), 'constructor', this).call(this, options, body, labelModule); + } + + _inherits(ShapeBase, _NodeBase); + + _createClass(ShapeBase, [{ + key: '_resizeShape', + value: function _resizeShape() { + if (this.width === undefined) { + var size = 2 * this.options.size; + this.width = size; + this.height = size; + this.radius = 0.5 * this.width; + } + } + }, { + key: '_drawShape', + value: function _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover) { + this._resizeShape(); + + this.left = x - this.width / 2; + this.top = y - this.height / 2; + + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.lineWidth = selected ? selectionLineWidth : borderWidth; + ctx.lineWidth /= this.body.view.scale; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx[shape](x, y, this.options.size); + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); + + // disable shadows for other elements. + this.disableShadow(ctx); + + ctx.stroke(); + + if (this.options.label !== undefined) { + var yLabel = y + 0.5 * this.height + 3; // the + 3 is to offset it a bit below the node. + this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + } + + this.updateBoundingBox(x, y); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; + + if (this.options.label !== undefined) { + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + 3); + } + } + }]); + + return ShapeBase; + })(_utilNodeBase2['default']); + + exports['default'] = ShapeBase; + module.exports = exports['default']; + /***/ } /******/ ]) }); diff --git a/docs/network/physics.html b/docs/network/physics.html index 0a37f621..85b95326 100644 --- a/docs/network/physics.html +++ b/docs/network/physics.html @@ -90,7 +90,8 @@ var options = { centralGravity: 0.3, springLength: 95, springConstant: 0.04, - damping: 0.09 + damping: 0.09, + avoidOverlap: false }, forceAtlas2Based: { theta: 0.5, @@ -98,14 +99,16 @@ var options = { centralGravity: 0.01, springConstant: 0.08, springLength: 100, - damping: 0.4 + damping: 0.4, + avoidOverlap: false }, repulsion: { centralGravity: 0.2, springLength: 200, springConstant: 0.05, nodeDistance: 100, - damping: 0.09 + damping: 0.09, + avoidOverlap: false }, hierarchicalRepulsion: { centralGravity: 0.0, @@ -149,6 +152,7 @@ network.setOptions(options); barnesHut.springLength Number 95 The edges are modelled as springs. This springLength here is the the rest length of the spring. barnesHut.springConstant Number 0.04 This is how 'sturdy' the springs are. Higher values mean stronger springs. barnesHut.damping Number 0.09 Accepted range: [0 .. 1]. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration. + barnesHut.avoidOverlap Number 0 Accepted range: [0 .. 1]. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for both the gravity model and the spring model. Value 1 is maximum overlap avoidance. forceAtlas2Based Object Object Force Atlas 2 has been developed by Jacomi et al (2014) for use with Gephi. The forceAtlas2Based solver makes use of some of the equations provided by them and makes use of the barnesHut implementation in vis. The main differences are the central gravity model, @@ -159,6 +163,7 @@ network.setOptions(options); forceAtlas2Based.springLength Number 100 The edges are modelled as springs. This springLength here is the the rest length of the spring. forceAtlas2Based.springConstant Number 0.08 This is how 'sturdy' the springs are. Higher values mean stronger springs. forceAtlas2Based.damping Number 0.4 Accepted range: [0 .. 1]. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration. + forceAtlas2Based.avoidOverlap Number 0 Accepted range: [0 .. 1]. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for both the gravity model and the spring model. Value 1 is maximum overlap avoidance. repulsion Object Object The repulsion model assumes nodes have a simplified repulsion field around them. It's force linearly decreases from 1 (at 0.5*nodeDistance and smaller) to 0 (at 2*nodeDistance). repulsion.nodeDistance Number 100 This is the range of influence for the repulsion. @@ -166,6 +171,7 @@ network.setOptions(options); repulsion.springLength Number 200 The edges are modelled as springs. This springLength here is the the rest length of the spring. repulsion.springConstant Number 0.05 This is how 'sturdy' the springs are. Higher values mean stronger springs. repulsion.damping Number 0.09 Accepted range: [0 .. 1]. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration. + repulsion.avoidOverlap Number 0 Accepted range: [0 .. 1]. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for the spring model. Value 1 is maximum overlap avoidance. hierarchicalRepulsion Object Object This model is based on the repulsion solver but the levels are taken into account and the forces are normalized. hierarchicalRepulsion.nodeDistance Number 120 This is the range of influence for the repulsion. diff --git a/examples/network/categories/02_random_nodes.html b/examples/network/categories/02_random_nodes.html index 0c9db9af..550b988a 100644 --- a/examples/network/categories/02_random_nodes.html +++ b/examples/network/categories/02_random_nodes.html @@ -14,8 +14,8 @@ } - - + + - +

diff --git a/examples/network/categories/28_world_cup_network_performance.html b/examples/network/categories/28_world_cup_network_performance.html index c8bccb42..d0d98f3c 100644 --- a/examples/network/categories/28_world_cup_network_performance.html +++ b/examples/network/categories/28_world_cup_network_performance.html @@ -5,10 +5,10 @@ Network | Static smooth curves - World Cup Network - - + + - + - + -

Labels can be aligned to edges in various ways.

+

Labels of edges can be aligned to edges in various ways.
Label alignment for nodes (top, bottom, left, right, inside) is planned but not in vis yet.

@@ -34,10 +37,10 @@ // create an array with edges var edges = [ - {from: 1, to: 2, label: 'middle', font: {align:'middle', background:'orange'}}, - {from: 1, to: 3, label: 'top', font: {align: 'top', background:'green'}}, - {from: 2, to: 4, label: 'horizontal', font: {align: 'horizontal', background: 'red'}}, - {from: 2, to: 5, label: 'bottom', font: { align:'bottom', background: '#ccd' }} + {from: 1, to: 2, label: 'middle', font: {align: 'middle'}}, + {from: 1, to: 3, label: 'top', font: {align: 'top'}}, + {from: 2, to: 4, label: 'horizontal', font: {align: 'horizontal'}}, + {from: 2, to: 5, label: 'bottom', font: {align: 'bottom'}} ]; // create a network @@ -46,11 +49,7 @@ nodes: nodes, edges: edges }; - var options = { - edges: { - font: {stroke: 0} - } - }; + var options = {}; var network = new vis.Network(container, data, options); diff --git a/examples/network/categories/labels/label_background.html b/examples/network/categories/labels/label_background.html new file mode 100644 index 00000000..109fdef6 --- /dev/null +++ b/examples/network/categories/labels/label_background.html @@ -0,0 +1,57 @@ + + + + Network | Label alignment + + + + + + + + + + +

Labels can have any color background.

+ +
+ + + + + diff --git a/examples/network/categories/labels/label_color_and_size.html b/examples/network/categories/labels/label_color_and_size.html new file mode 100644 index 00000000..7fc56c88 --- /dev/null +++ b/examples/network/categories/labels/label_color_and_size.html @@ -0,0 +1,61 @@ + + + + Network | Label stroke + + + + + + + + +

The style of the edges can be fully customized.

+ +
+ + + + + diff --git a/examples/network/categories/35_label_stroke.html b/examples/network/categories/labels/label_stroke.html similarity index 70% rename from examples/network/categories/35_label_stroke.html rename to examples/network/categories/labels/label_stroke.html index 65e0a902..997924ae 100644 --- a/examples/network/categories/35_label_stroke.html +++ b/examples/network/categories/labels/label_stroke.html @@ -2,23 +2,25 @@ Network | Label stroke - - - + + - + -

The font, size, and stroke of labels is fully customizable.

+

The stroke of labels is fully can have a width and color. Edgelabels by default have a white stroke for clarity.

diff --git a/examples/network/categories/nodeStyles/colors.html b/examples/network/categories/nodeStyles/colors.html index e57af215..0868b650 100644 --- a/examples/network/categories/nodeStyles/colors.html +++ b/examples/network/categories/nodeStyles/colors.html @@ -12,6 +12,10 @@ height: 400px; border: 1px solid lightgray; } + + p { + max-width:700px; + } @@ -26,12 +30,12 @@ diff --git a/examples/network/categories/nodeStyles/icons.html b/examples/network/categories/nodeStyles/icons.html index e9d1aca7..d55f892d 100644 --- a/examples/network/categories/nodeStyles/icons.html +++ b/examples/network/categories/nodeStyles/icons.html @@ -15,6 +15,12 @@ #mynetworkFA, #mynetworkIO { height: 300px; + width: 700px; + border:1px solid lightgrey; + } + + p { + max-width:700px; } @@ -170,13 +176,19 @@ - +

+Icons can be used for nodes as well. This example shows Icons from fontAwesome and Ionicons but it should work with similar packages as well. + It uses unicode and css to define the icons.

Remember! Unicode in javascript is done like this: \uf274 for the unicode f274. +
If a node is shown as a rectangle, it means the css is not loaded (or not yet loaded). A redraw will fix that. + +

+

- Use FontAwesome-icons for node

+ Use FontAwesome-icons for nodes

- Use Ionicons-icons for node

+ Use Ionicons-icons for nodes
diff --git a/lib/network/Network.js b/lib/network/Network.js index 916ac444..00f5f28c 100644 --- a/lib/network/Network.js +++ b/lib/network/Network.js @@ -260,7 +260,7 @@ Network.prototype.bindEventListeners = function () { this.body.emitter.on("_dataChanged", () => { // update shortcut lists this._updateVisibleIndices(); - this.physics.updatePhysicsIndices(); + this.physics.updatePhysicsData(); // call the dataUpdated event because the only difference between the two is the updating of the indices this.body.emitter.emit("_dataUpdated"); diff --git a/lib/network/modules/CanvasRenderer.js b/lib/network/modules/CanvasRenderer.js index 6cd438b7..ac2b8ca4 100644 --- a/lib/network/modules/CanvasRenderer.js +++ b/lib/network/modules/CanvasRenderer.js @@ -36,6 +36,7 @@ class CanvasRenderer { this.dragging = true; }); this.body.emitter.on("dragEnd", () => this.dragging = false); + this.body.emitter.on("_resizeNodes", () => this._resizeNodes()); this.body.emitter.on("_redraw", () => { if (this.renderingActive === false) { this._redraw(); @@ -194,6 +195,43 @@ class CanvasRenderer { } + /** + * Redraw all nodes + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @param {Boolean} [alwaysShow] + * @private + */ + _resizeNodes() { + let ctx = this.canvas.frame.canvas.getContext('2d'); + if (this.pixelRatio === undefined) { + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1); + } + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + ctx.save(); + ctx.translate(this.body.view.translation.x, this.body.view.translation.y); + ctx.scale(this.body.view.scale, this.body.view.scale); + + let nodes = this.body.nodes; + let node; + + // resize all nodes + for (let nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + node = nodes[nodeId]; + node.resize(ctx); + node.updateBoundingBox(ctx); + } + } + + // restore original scaling and translation + ctx.restore(); + } + /** * Redraw all nodes * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); diff --git a/lib/network/modules/LayoutEngine.js b/lib/network/modules/LayoutEngine.js index 2779493d..8039c2e8 100644 --- a/lib/network/modules/LayoutEngine.js +++ b/lib/network/modules/LayoutEngine.js @@ -134,7 +134,6 @@ class LayoutEngine { if ((!node.isFixed()) && (node.x === undefined || node.y === undefined)) { let radius = 10 * 0.1 * nodesArray.length + 10; let angle = 2 * Math.PI * this.seededRandom(); - if (node.options.fixed.x === false) { node.x = radius * Math.cos(angle); } diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index 20c92170..b3f8f886 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -35,7 +35,8 @@ class PhysicsEngine { centralGravity: 0.3, springLength: 95, springConstant: 0.04, - damping: 0.09 + damping: 0.09, + avoidOverlap: 0 }, forceAtlas2Based: { theta: 0.5, @@ -43,14 +44,16 @@ class PhysicsEngine { centralGravity: 0.01, springConstant: 0.08, springLength: 100, - damping: 0.4 + damping: 0.4, + avoidOverlap: 0 }, repulsion: { centralGravity: 0.2, springLength: 200, springConstant: 0.05, nodeDistance: 100, - damping: 0.09 + damping: 0.09, + avoidOverlap: 0 }, hierarchicalRepulsion: { centralGravity: 0.0, @@ -159,7 +162,6 @@ class PhysicsEngine { else { this.ready = true; this.body.emitter.emit('fit'); - } } @@ -169,6 +171,10 @@ class PhysicsEngine { startSimulation() { if (this.physicsEnabled === true) { this.stabilized = false; + + // this sets the width of all nodes initially which could be required for the avoidOverlap + this.body.emitter.emit("_resizeNodes"); + if (this.viewFunction === undefined) { this.viewFunction = this.simulationStep.bind(this); this.body.emitter.on('initRedraw', this.viewFunction); @@ -273,7 +279,7 @@ class PhysicsEngine { * * @private */ - updatePhysicsIndices() { + updatePhysicsData() { this.physicsBody.forces = {}; this.physicsBody.physicsNodeIndices = []; this.physicsBody.physicsEdgeIndices = []; @@ -478,6 +484,9 @@ class PhysicsEngine { iterations = this.options.stabilization.iterations; } + // this sets the width of all nodes initially which could be required for the avoidOverlap + this.body.emitter.emit("_resizeNodes"); + // stop the render loop this.stopSimulation(); diff --git a/lib/network/modules/View.js b/lib/network/modules/View.js index 70c426a4..68464b40 100644 --- a/lib/network/modules/View.js +++ b/lib/network/modules/View.js @@ -125,7 +125,7 @@ class View { zoomLevel *= factor; } else { - this.body.emitter.emit("_redraw", true); + this.body.emitter.emit("_resizeNodes"); range = this._getRange(options.nodes); var xDistance = Math.abs(range.maxX - range.minX) * 1.1; var yDistance = Math.abs(range.maxY - range.minY) * 1.1; diff --git a/lib/network/modules/components/Edge.js b/lib/network/modules/components/Edge.js index f8f78495..9e542539 100644 --- a/lib/network/modules/components/Edge.js +++ b/lib/network/modules/components/Edge.js @@ -90,7 +90,6 @@ class Edge { static parseOptions(parentOptions, newOptions, allowDeletion = false) { var fields = [ 'id', - 'font', 'from', 'hidden', 'hoverWidth', diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index 6f5b8472..95cd7cb9 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -162,6 +162,7 @@ class Node { static parseOptions(parentOptions, newOptions, allowDeletion = false) { var fields = [ 'color', + 'font', 'fixed', 'shadow' ]; @@ -194,6 +195,10 @@ class Node { } } } + + if (newOptions.font !== undefined) { + Label.parseOptions(parentOptions.font, newOptions); + } } updateLabelModule() { @@ -368,6 +373,7 @@ class Node { this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); } + /** * Update the bounding box of the shape */ diff --git a/lib/network/modules/components/nodes/shapes/Box.js b/lib/network/modules/components/nodes/shapes/Box.js index 59d9e5eb..ef48b73d 100644 --- a/lib/network/modules/components/nodes/shapes/Box.js +++ b/lib/network/modules/components/nodes/shapes/Box.js @@ -13,6 +13,7 @@ class Box extends NodeBase { let textSize = this.labelModule.getTextSize(ctx,selected); this.width = textSize.width + 2 * margin; this.height = textSize.height + 2 * margin; + this.radius = 0.5*this.width; } } diff --git a/lib/network/modules/components/nodes/shapes/Circle.js b/lib/network/modules/components/nodes/shapes/Circle.js index 3c093a02..3bfb828f 100644 --- a/lib/network/modules/components/nodes/shapes/Circle.js +++ b/lib/network/modules/components/nodes/shapes/Circle.js @@ -16,6 +16,7 @@ class Circle extends CircleImageBase { this.width = diameter; this.height = diameter; + this.radius = 0.5*this.width; } } diff --git a/lib/network/modules/components/nodes/shapes/CircularImage.js b/lib/network/modules/components/nodes/shapes/CircularImage.js index 3f009ed9..d7c510ca 100644 --- a/lib/network/modules/components/nodes/shapes/CircularImage.js +++ b/lib/network/modules/components/nodes/shapes/CircularImage.js @@ -17,6 +17,7 @@ class CircularImage extends CircleImageBase { this.width = diameter; this.height = diameter; this._swapToImageResizeWhenImageLoaded = true; + this.radius = 0.5*this.width; } } else { diff --git a/lib/network/modules/components/nodes/shapes/Database.js b/lib/network/modules/components/nodes/shapes/Database.js index 2cc8e9c6..f5cc6f78 100644 --- a/lib/network/modules/components/nodes/shapes/Database.js +++ b/lib/network/modules/components/nodes/shapes/Database.js @@ -14,6 +14,7 @@ class Database extends NodeBase { var size = textSize.width + 2 * margin; this.width = size; this.height = size; + this.radius = 0.5*this.width; } } diff --git a/lib/network/modules/components/nodes/shapes/Ellipse.js b/lib/network/modules/components/nodes/shapes/Ellipse.js index 4fb9c644..480dae27 100644 --- a/lib/network/modules/components/nodes/shapes/Ellipse.js +++ b/lib/network/modules/components/nodes/shapes/Ellipse.js @@ -16,6 +16,7 @@ class Ellipse extends NodeBase { if (this.width < this.height) { this.width = this.height; } + this.radius = 0.5*this.width; } } diff --git a/lib/network/modules/components/nodes/shapes/Empty.js b/lib/network/modules/components/nodes/shapes/Empty.js deleted file mode 100644 index 5b27e5c4..00000000 --- a/lib/network/modules/components/nodes/shapes/Empty.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -import NodeBase from '../util/NodeBase' - -class Empty extends NodeBase { - constructor (options, body, labelModule) { - super(options, body, labelModule); - } - - setOptions() {} - - resize() {} - - draw() {} - - distanceToBorder() {} - - updateBoundingBox() {}} - - -export default Empty; \ No newline at end of file diff --git a/lib/network/modules/components/nodes/shapes/Icon.js b/lib/network/modules/components/nodes/shapes/Icon.js index 200176ca..b1a0e150 100644 --- a/lib/network/modules/components/nodes/shapes/Icon.js +++ b/lib/network/modules/components/nodes/shapes/Icon.js @@ -16,6 +16,7 @@ class Icon extends NodeBase { }; this.width = iconSize.width + 2 * margin; this.height = iconSize.height + 2 * margin; + this.radius = 0.5*this.width; } } diff --git a/lib/network/modules/components/nodes/shapes/Text.js b/lib/network/modules/components/nodes/shapes/Text.js index 7b6a3be2..f649dcba 100644 --- a/lib/network/modules/components/nodes/shapes/Text.js +++ b/lib/network/modules/components/nodes/shapes/Text.js @@ -13,6 +13,7 @@ class Text extends NodeBase { var textSize = this.labelModule.getTextSize(ctx,selected); this.width = textSize.width + 2 * margin; this.height = textSize.height + 2 * margin; + this.radius = 0.5*this.width; } } diff --git a/lib/network/modules/components/nodes/util/CircleImageBase.js b/lib/network/modules/components/nodes/util/CircleImageBase.js index b075b8ed..bff9e6dc 100644 --- a/lib/network/modules/components/nodes/util/CircleImageBase.js +++ b/lib/network/modules/components/nodes/util/CircleImageBase.js @@ -25,6 +25,7 @@ class CircleImageBase extends NodeBase { } this.width = width; this.height = height; + this.radius = 0.5*this.width; } } diff --git a/lib/network/modules/components/nodes/util/NodeBase.js b/lib/network/modules/components/nodes/util/NodeBase.js index 954a17b2..b718a7f1 100644 --- a/lib/network/modules/components/nodes/util/NodeBase.js +++ b/lib/network/modules/components/nodes/util/NodeBase.js @@ -6,6 +6,8 @@ class NodeBase { this.top = undefined; this.left = undefined; this.height = undefined; + this.width = undefined; + this.radius = undefined; this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; } diff --git a/lib/network/modules/components/nodes/util/ShapeBase.js b/lib/network/modules/components/nodes/util/ShapeBase.js index 82cd86ba..8e635f2e 100644 --- a/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/network/modules/components/nodes/util/ShapeBase.js @@ -10,6 +10,7 @@ class ShapeBase extends NodeBase { var size = 2 * this.options.size; this.width = size; this.height = size; + this.radius = 0.5*this.width; } } diff --git a/lib/network/modules/components/physics/BarnesHutSolver.js b/lib/network/modules/components/physics/BarnesHutSolver.js index 447e4cca..e18a0d4b 100644 --- a/lib/network/modules/components/physics/BarnesHutSolver.js +++ b/lib/network/modules/components/physics/BarnesHutSolver.js @@ -9,7 +9,8 @@ class BarnesHutSolver { setOptions(options) { this.options = options; - this.thetaInversed = 1/ this.options.theta; + this.thetaInversed = 1 / this.options.theta; + this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1,this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius } @@ -104,6 +105,11 @@ class BarnesHutSolver { distance = 0.1 * Math.random(); dx = distance; } + + if (this.overlapAvoidanceFactor < 1) { + distance = Math.max(0.1 + (this.overlapAvoidanceFactor * node.shape.radius), distance - node.shape.radius); + } + // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce let gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance,3); diff --git a/lib/network/modules/components/physics/FA2BasedRepulsionSolver.js b/lib/network/modules/components/physics/FA2BasedRepulsionSolver.js index 786ce392..24fe2742 100644 --- a/lib/network/modules/components/physics/FA2BasedRepulsionSolver.js +++ b/lib/network/modules/components/physics/FA2BasedRepulsionSolver.js @@ -20,6 +20,11 @@ class ForceAtlas2BasedRepulsionSolver extends BarnesHutSolver { distance = 0.1 * Math.random(); dx = distance; } + + if (this.overlapAvoidanceFactor < 1) { + distance = Math.max(0.1 + (this.overlapAvoidanceFactor * node.shape.radius), distance - node.shape.radius); + } + let degree = (node.edges.length + 1); // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce diff --git a/lib/network/modules/components/physics/SpringSolver.js b/lib/network/modules/components/physics/SpringSolver.js index 14bd4292..5998823a 100644 --- a/lib/network/modules/components/physics/SpringSolver.js +++ b/lib/network/modules/components/physics/SpringSolver.js @@ -7,6 +7,8 @@ class SpringSolver { setOptions(options) { this.options = options; + this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1,this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius + } /** @@ -56,8 +58,17 @@ class SpringSolver { * @private */ _calculateSpringForce(node1, node2, edgeLength) { - let dx = (node1.x - node2.x); - let dy = (node1.y - node2.y); + let dx,dy; + if (this.overlapAvoidanceFactor < 1) { + let nodesWidth = this.overlapAvoidanceFactor * (node1.shape.radius + node2.shape.radius); + dx = (node1.x - node2.x) - nodesWidth; + dy = (node1.y - node2.y) - nodesWidth; + } + else { + dx = (node1.x - node2.x); + dy = (node1.y - node2.y); + } + let distance = Math.sqrt(dx * dx + dy * dy); distance = distance === 0 ? 0.01 : distance; diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 89961bf5..47c19c4f 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -92,7 +92,7 @@ class Label { ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height); break; default: - ctx.fillRect(this.size.left, this.size.top, this.size.width, this.size.height); + ctx.fillRect(this.size.left, this.size.top - 0.5*lineMargin, this.size.width, this.size.height); break; } } diff --git a/lib/network/options.js b/lib/network/options.js index c47c15b6..ddbb37d3 100644 --- a/lib/network/options.js +++ b/lib/network/options.js @@ -220,6 +220,7 @@ let allOptions = { springLength: {number}, springConstant: {number}, damping: {number}, + avoidOverlap: {number}, __type__: {object} }, forceAtlas2Based: { @@ -228,6 +229,7 @@ let allOptions = { springLength: {number}, springConstant: {number}, damping: {number}, + avoidOverlap: {number}, __type__: {object} }, repulsion: { @@ -236,6 +238,7 @@ let allOptions = { springConstant: {number}, nodeDistance: {number}, damping: {number}, + avoidOverlap: {number}, __type__: {object} }, hierarchicalRepulsion: { @@ -426,7 +429,8 @@ let configureOptions = { centralGravity: [0.3, 0, 10, 0.05], springLength: [95, 0, 500, 5], springConstant: [0.04, 0, 5, 0.005], - damping: [0.09, 0, 1, 0.01] + damping: [0.09, 0, 1, 0.01], + avoidOverlap: [0, 0, 1, 0.01] }, forceAtlas2Based: { //theta: [0.5, 0.1, 1, 0.05], @@ -434,14 +438,16 @@ let configureOptions = { centralGravity: [0.01, 0, 1, 0.005], springLength: [95, 0, 500, 5], springConstant: [0.08, 0, 5, 0.005], - damping: [0.4, 0, 1, 0.01] + damping: [0.4, 0, 1, 0.01], + avoidOverlap: [0, 0, 1, 0.01] }, repulsion: { centralGravity: [0.2, 0, 10, 0.05], springLength: [200, 0, 500, 5], springConstant: [0.05, 0, 5, 0.005], nodeDistance: [100, 0, 500, 5], - damping: [0.09, 0, 1, 0.01] + damping: [0.09, 0, 1, 0.01], + avoidOverlap: [0, 0, 1, 0.01] }, hierarchicalRepulsion: { centralGravity: [0.2, 0, 10, 0.05], diff --git a/test/timeline_groups.html b/test/timeline_groups.html index e4661fec..aac1c2ca 100644 --- a/test/timeline_groups.html +++ b/test/timeline_groups.html @@ -67,7 +67,7 @@ var names = ['John (0)', 'Alston (1)', 'Lee (2)', 'Grant (3)']; var groups = new vis.DataSet(); for (var g = 0; g < groupCount; g++) { - groups.add({id: g, content: names[g], title: 'Title of group ' + g, 'className': 'myGroup'}); + groups.add({id: g, content: '' + names[g] + '', title: 'Title of group ' + g, 'className': 'myGroup'}); } // create a dataset with items @@ -80,7 +80,7 @@ id: i, group: group, content: 'item ' + i + - ' (' + names[group] + ')', + ' (' + names[group] + ') ', start: start, end: end, title: 'Title for item ' + i,