diff --git a/packages/mindplot/src/components/ActionDispatcher.js b/packages/mindplot/src/components/ActionDispatcher.js index ed0ffc14..f33917de 100644 --- a/packages/mindplot/src/components/ActionDispatcher.js +++ b/packages/mindplot/src/components/ActionDispatcher.js @@ -1,4 +1,4 @@ -/* eslint-disable no-unused-vars */ +/* eslint-disable class-methods-use-this */ /* * Copyright [2015] [wisemapping] * @@ -19,89 +19,88 @@ import { $assert } from '@wisemapping/core-js'; import Events from './Events'; -// noinspection JSUnusedLocalSymbols -const ActionDispatcher = new Class({ - Implements: [Events], - initialize(commandContext) { +class ActionDispatcher extends Events { + constructor(commandContext) { $assert(commandContext, 'commandContext can not be null'); - }, + super(); + } addRelationship(model, mindmap) { throw new Error('method must be implemented.'); - }, + } addTopics(models, parentTopicId) { throw new Error('method must be implemented.'); - }, + } deleteEntities(topicsIds, relIds) { throw new Error('method must be implemented.'); - }, + } dragTopic(topicId, position, order, parentTopic) { throw new Error('method must be implemented.'); - }, + } moveTopic(topicId, position) { throw new Error('method must be implemented.'); - }, + } moveControlPoint(ctrlPoint, point) { throw new Error('method must be implemented.'); - }, + } changeFontFamilyToTopic(topicIds, fontFamily) { throw new Error('method must be implemented.'); - }, + } changeFontStyleToTopic(topicsIds) { throw new Error('method must be implemented.'); - }, + } changeFontColorToTopic(topicsIds, color) { throw new Error('method must be implemented.'); - }, + } changeFontSizeToTopic(topicsIds, size) { throw new Error('method must be implemented.'); - }, + } changeBackgroundColorToTopic(topicsIds, color) { throw new Error('method must be implemented.'); - }, + } changeBorderColorToTopic(topicsIds, color) { throw new Error('method must be implemented.'); - }, + } changeShapeTypeToTopic(topicsIds, shapeType) { throw new Error('method must be implemented.'); - }, + } changeFontWeightToTopic(topicsIds) { throw new Error('method must be implemented.'); - }, + } changeTextToTopic(topicsIds, text) { throw new Error('method must be implemented.'); - }, + } shrinkBranch(topicsIds, collapse) { throw new Error('method must be implemented.'); - }, + } addFeatureToTopic(topicId, type, attributes) { throw new Error('method must be implemented.'); - }, + } changeFeatureToTopic(topicId, featureId, attributes) { throw new Error('method must be implemented.'); - }, + } removeFeatureFromTopic(topicId, featureId) { throw new Error('method must be implemented.'); - }, -}); + } +} ActionDispatcher.setInstance = (dispatcher) => { ActionDispatcher._instance = dispatcher; diff --git a/packages/mindplot/src/components/CommandContext.js b/packages/mindplot/src/components/CommandContext.js new file mode 100644 index 00000000..3062294d --- /dev/null +++ b/packages/mindplot/src/components/CommandContext.js @@ -0,0 +1,115 @@ +/* + * Copyright [2015] [wisemapping] + * + * Licensed under WiseMapping Public License, Version 1.0 (the "License"). + * It is basically the Apache License, Version 2.0 (the "License") plus the + * "powered by wisemapping" text requirement on every single page; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the license at + * + * http://www.wisemapping.org/license + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { $assert } from "@wisemapping/core-js"; + +class CommandContext { + constructor(designer) { + $assert(designer, 'designer can not be null'); + this._designer = designer; + } + + /** */ + findTopics(topicsIds) { + $assert($defined(topicsIds), 'topicsIds can not be null'); + if (!(topicsIds instanceof Array)) { + topicsIds = [topicsIds]; + } + + const designerTopics = this._designer.getModel().getTopics(); + const result = designerTopics.filter((topic) => topicsIds.contains(topic.getId())); + + if (result.length !== topicsIds.length) { + const ids = designerTopics.map((topic) => topic.getId()); + $assert( + result.length === topicsIds.length, + `Could not find topic. Result:${result + } Filter Criteria:${topicsIds + } Current Topics: [${ids + }]`, + ); + } + return result; + } + + /** */ + deleteTopic(topic) { + this._designer.removeTopic(topic); + } + + /** */ + createTopic(model) { + $assert(model, 'model can not be null'); + return this._designer.nodeModelToNodeGraph(model); + } + + /** */ + createModel() { + const mindmap = this._designer.getMindmap(); + return mindmap.createNode(NodeModel.MAIN_TOPIC_TYPE); + } + + /** */ + addTopic(topic) { + const mindmap = this._designer.getMindmap(); + return mindmap.addBranch(topic.getModel()); + } + + /** */ + connect(childTopic, parentTopic) { + childTopic.connectTo(parentTopic, this._designer._workspace); + } + + /** */ + disconnect(topic) { + topic.disconnect(this._designer._workspace); + } + + /** */ + addRelationship(model) { + $assert(model, 'model cannot be null'); + return this._designer.addRelationship(model); + } + + /** */ + deleteRelationship(relationship) { + this._designer.deleteRelationship(relationship); + } + + /** */ + findRelationships(relIds) { + $assert($defined(relIds), 'relId can not be null'); + if (!(relIds instanceof Array)) { + relIds = [relIds]; + } + + const designerRel = this._designer.getModel().getRelationships(); + return designerRel.filter((rel) => relIds.contains(rel.getId())); + } + + /** */ + moveTopic(topic, position) { + $assert(topic, 'topic cannot be null'); + $assert(position, 'position cannot be null'); + EventBus.instance.fireEvent(EventBus.events.NodeMoveEvent, { + node: topic.getModel(), + position, + }); + } +} +// eslint-disable-next-line import/prefer-default-export +export default CommandContext; diff --git a/packages/mindplot/src/components/Designer.js b/packages/mindplot/src/components/Designer.js index 42191529..04999bea 100644 --- a/packages/mindplot/src/components/Designer.js +++ b/packages/mindplot/src/components/Designer.js @@ -15,12 +15,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { $assert } from '@wisemapping/core-js'; +import { $assert, $defined } from '@wisemapping/core-js'; +import $ from '@libraries/jquery-2.1.0'; +import _ from '@libraries/underscore-min'; import Events from './Events'; import Messages from './Messages'; +import StandaloneActionDispatcher from './StandaloneActionDispatcher'; -import { StandaloneActionDispatcher, CommandContext } from './StandaloneActionDispatcher'; +import CommandContext from './CommandContext'; import ActionDispatcher from './ActionDispatcher'; import DesignerModel from './DesignerModel'; @@ -45,987 +48,974 @@ import LayoutManager from './layout/LayoutManager'; import INodeModel, { TopicShape } from './model/INodeModel'; -const Designer = new Class( - /** @lends Designer */ { - Extends: Events, - /** - * @constructs - * @param {Object} options - * @param {HTMLElement} divElement - * @extends mindplot.Events - */ - initialize(options, divElement) { - $assert(options, 'options must be defined'); - $assert(options.zoom, 'zoom must be defined'); - $assert(options.size, 'size must be defined'); - $assert(divElement, 'divElement must be defined'); +class Designer extends Events { + constructor(options, divElement) { + $assert(options, 'options must be defined'); + $assert(options.zoom, 'zoom must be defined'); + $assert(options.size, 'size must be defined'); + $assert(divElement, 'divElement must be defined'); + super(); - // Set up i18n location ... - Messages.init(options.locale); + // Set up i18n location ... + Messages.init(options.locale); - this._options = options; + this._options = options; - // Set full div elem render area ... - divElement.css(options.size); + // Set full div elem render area ... + divElement.css(options.size); - // Dispatcher manager ... - const commandContext = new CommandContext(this); - this._actionDispatcher = new StandaloneActionDispatcher(commandContext); + // Dispatcher manager ... + const commandContext = new CommandContext(this); + this._actionDispatcher = new StandaloneActionDispatcher(commandContext); - const me = this; - this._actionDispatcher.addEvent('modelUpdate', (event) => { - me.fireEvent('modelUpdate', event); - }); + const me = this; + this._actionDispatcher.addEvent('modelUpdate', (event) => { + me.fireEvent('modelUpdate', event); + }); - ActionDispatcher.setInstance(this._actionDispatcher); - this._model = new DesignerModel(options); + ActionDispatcher.setInstance(this._actionDispatcher); + this._model = new DesignerModel(options); - // Init Screen manager.. - const screenManager = new ScreenManager(divElement); - this._workspace = new Workspace(screenManager, this._model.getZoom()); + // Init Screen manager.. + const screenManager = new ScreenManager(divElement); + this._workspace = new Workspace(screenManager, this._model.getZoom()); - // Init layout manager ... - this._eventBussDispatcher = new EventBusDispatcher(this.getModel()); + // Init layout manager ... + this._eventBussDispatcher = new EventBusDispatcher(this.getModel()); - // Register events - if (!this.isReadOnly()) { - // Register mouse events ... - this._registerMouseEvents(); + // Register events + if (!this.isReadOnly()) { + // Register mouse events ... + this._registerMouseEvents(); - // Register keyboard events ... - DesignerKeyboard.register(this); + // Register keyboard events ... + DesignerKeyboard.register(this); - this._dragManager = this._buildDragManager(this._workspace); - } - this._registerWheelEvents(); + this._dragManager = this._buildDragManager(this._workspace); + } + this._registerWheelEvents(); - this._relPivot = new RelationshipPivot(this._workspace, this); + this._relPivot = new RelationshipPivot(this._workspace, this); - // Set editor working area ... - this.setViewPort(options.viewPort); + // Set editor working area ... + this.setViewPort(options.viewPort); - TopicEventDispatcher.configure(this.isReadOnly()); - this._clipboard = []; - }, + TopicEventDispatcher.configure(this.isReadOnly()); + this._clipboard = []; + } - /** - * @private - */ - _registerWheelEvents() { - const zoomFactor = 1.006; - const me = this; - // Zoom In and Zoom Out must active event - $(document).on('mousewheel', (event) => { - if (event.deltaY > 0) { - me.zoomIn(zoomFactor); - } else { - me.zoomOut(zoomFactor); - } - event.preventDefault(); - }); - }, - - /** - * @param {String} type the event type - * @param {Function} listener - * forwards to the TopicEventDispatcher or the parent Events class, depending on the type - */ - addEvent(type, listener) { - if (type === TopicEvent.EDIT || type === TopicEvent.CLICK) { - const editor = TopicEventDispatcher.getInstance(); - editor.addEvent(type, listener); + /** + * @private + */ + _registerWheelEvents() { + const zoomFactor = 1.006; + const me = this; + // Zoom In and Zoom Out must active event + $(document).on('mousewheel', (event) => { + if (event.deltaY > 0) { + me.zoomIn(zoomFactor); } else { - this.parent(type, listener); + me.zoomOut(zoomFactor); } - }, + event.preventDefault(); + }); + } - /** - * @private - */ - _registerMouseEvents() { - const workspace = this._workspace; - const screenManager = workspace.getScreenManager(); - const me = this; - // Initialize workspace event listeners. - screenManager.addEvent('update', () => { - // Topic must be set to his original state. All editors must be closed. - const topics = me.getModel().getTopics(); - _.each(topics, (object) => { - object.closeEditors(); - }); + /** + * @param {String} type the event type + * @param {Function} listener + * forwards to the TopicEventDispatcher or the parent Events class, depending on the type + */ + addEvent(type, listener) { + if (type === TopicEvent.EDIT || type === TopicEvent.CLICK) { + const editor = TopicEventDispatcher.getInstance(); + editor.addEvent(type, listener); + } else { + super.addEvent(type, listener); + } + } - // Clean some selected nodes on event .. - if (me._cleanScreen) me._cleanScreen(); + /** + * @private + */ + _registerMouseEvents() { + const workspace = this._workspace; + const screenManager = workspace.getScreenManager(); + const me = this; + // Initialize workspace event listeners. + screenManager.addEvent('update', () => { + // Topic must be set to his original state. All editors must be closed. + const topics = me.getModel().getTopics(); + _.each(topics, (object) => { + object.closeEditors(); }); - // Deselect on click ... - screenManager.addEvent('click', (event) => { - me.onObjectFocusEvent(null, event); - }); + // Clean some selected nodes on event .. + if (me._cleanScreen) me._cleanScreen(); + }); - // Create nodes on double click... - screenManager.addEvent('dblclick', (event) => { - if (workspace.isWorkspaceEventsEnabled()) { - const mousePos = screenManager.getWorkspaceMousePosition(event); - const centralTopic = me.getModel().getCentralTopic(); - const model = me._createChildModel(centralTopic, mousePos); - this._actionDispatcher.addTopics([model], [centralTopic.getId()]); - } - }); + // Deselect on click ... + screenManager.addEvent('click', (event) => { + me.onObjectFocusEvent(null, event); + }); - // Register mouse drag and drop event ... - function noopHandler(evt) { - evt.stopPropagation(); - evt.preventDefault(); + // Create nodes on double click... + screenManager.addEvent('dblclick', (event) => { + if (workspace.isWorkspaceEventsEnabled()) { + const mousePos = screenManager.getWorkspaceMousePosition(event); + const centralTopic = me.getModel().getCentralTopic(); + const model = me._createChildModel(centralTopic, mousePos); + this._actionDispatcher.addTopics([model], [centralTopic.getId()]); } - }, + }); - /** - * @private - * @param {mindplot.Workspace} workspace - * @return {mindplot.DragManager} the new dragManager for the workspace with events - * registered - */ - _buildDragManager(workspace) { - const designerModel = this.getModel(); - const dragConnector = new DragConnector(designerModel, this._workspace); - const dragManager = new DragManager(workspace, this._eventBussDispatcher); - const topics = designerModel.getTopics(); + // Register mouse drag and drop event ... + function noopHandler(evt) { + evt.stopPropagation(); + evt.preventDefault(); + } + } - dragManager.addEvent('startdragging', () => { - // Enable all mouse events. - for (let i = 0; i < topics.length; i++) { - topics[i].setMouseEventsEnabled(false); - } - }); + /** + * @private + * @param {mindplot.Workspace} workspace + * @return {mindplot.DragManager} the new dragManager for the workspace with events + * registered + */ + _buildDragManager(workspace) { + const designerModel = this.getModel(); + const dragConnector = new DragConnector(designerModel, this._workspace); + const dragManager = new DragManager(workspace, this._eventBussDispatcher); + const topics = designerModel.getTopics(); - dragManager.addEvent('dragging', (event, dragTopic) => { - dragTopic.updateFreeLayout(event); - if (!dragTopic.isFreeLayoutOn(event)) { - // The node is being drag. Is the connection still valid ? - dragConnector.checkConnection(dragTopic); + dragManager.addEvent('startdragging', () => { + // Enable all mouse events. + for (let i = 0; i < topics.length; i++) { + topics[i].setMouseEventsEnabled(false); + } + }); - if (!dragTopic.isVisible() && dragTopic.isConnected()) { - dragTopic.setVisibility(true); - } - } - }); + dragManager.addEvent('dragging', (event, dragTopic) => { + dragTopic.updateFreeLayout(event); + if (!dragTopic.isFreeLayoutOn(event)) { + // The node is being drag. Is the connection still valid ? + dragConnector.checkConnection(dragTopic); - dragManager.addEvent('enddragging', (event, dragTopic) => { - for (let i = 0; i < topics.length; i++) { - topics[i].setMouseEventsEnabled(true); - } - dragTopic.applyChanges(workspace); - }); - - return dragManager; - }, - - /** - * @param {{width:Number, height:Number}} size - * sets width and height of the workspace - */ - setViewPort(size) { - this._workspace.setViewPort(size); - const model = this.getModel(); - this._workspace.setZoom(model.getZoom(), true); - }, - - /** - * @private - * @param {mindplot.model.NodeModel} model - * @param {Boolean} readOnly - * @return {mindplot.CentralTopic|mindplot.MainTopic} the topic to the given model, - * connected, added to the drag manager, with events registered - complying type & read mode - */ - _buildNodeGraph(model, readOnly) { - // Create node graph ... - const topic = create(model, { readOnly }); - this.getModel().addTopic(topic); - const me = this; - // Add Topic events ... - if (!readOnly) { - // If a node had gained focus, clean the rest of the nodes ... - topic.addEvent('mousedown', (event) => { - me.onObjectFocusEvent(topic, event); - }); - - // Register node listeners ... - if (topic.getType() !== INodeModel.CENTRAL_TOPIC_TYPE) { - // Central Topic doesn't support to be dragged - this._dragManager.add(topic); + if (!dragTopic.isVisible() && dragTopic.isConnected()) { + dragTopic.setVisibility(true); } } + }); - // Connect Topic ... - const isConnected = model.isConnected(); - if (isConnected) { - // Improve this ... - const targetTopicModel = model.getParent(); - let targetTopic = null; - - const topics = this.getModel().getTopics(); - for (let i = 0; i < topics.length; i++) { - const t = topics[i]; - if (t.getModel() === targetTopicModel) { - targetTopic = t; - // Disconnect the node. It will be connected again later ... - model.disconnect(); - break; - } - } - $assert(targetTopic, 'Could not find a topic to connect'); - topic.connectTo(targetTopic, this._workspace); + dragManager.addEvent('enddragging', (event, dragTopic) => { + for (let i = 0; i < topics.length; i++) { + topics[i].setMouseEventsEnabled(true); } + dragTopic.applyChanges(workspace); + }); - topic.addEvent('ontblur', () => { - const topics = me.getModel().filterSelectedTopics(); - const rels = me.getModel().filterSelectedRelationships(); + return dragManager; + } - if (topics.length === 0 || rels.length === 0) { - me.fireEvent('onblur'); - } + /** + * @param {{width:Number, height:Number}} size + * sets width and height of the workspace + */ + setViewPort(size) { + this._workspace.setViewPort(size); + const model = this.getModel(); + this._workspace.setZoom(model.getZoom(), true); + } + + /** + * @private + * @param {mindplot.model.NodeModel} model + * @param {Boolean} readOnly + * @return {mindplot.CentralTopic|mindplot.MainTopic} the topic to the given model, + * connected, added to the drag manager, with events registered - complying type & read mode + */ + _buildNodeGraph(model, readOnly) { + // Create node graph ... + const topic = create(model, { readOnly }); + this.getModel().addTopic(topic); + const me = this; + // Add Topic events ... + if (!readOnly) { + // If a node had gained focus, clean the rest of the nodes ... + topic.addEvent('mousedown', (event) => { + me.onObjectFocusEvent(topic, event); }); - topic.addEvent('ontfocus', () => { - const topics = me.getModel().filterSelectedTopics(); - const rels = me.getModel().filterSelectedRelationships(); - - if (topics.length === 1 || rels.length === 1) { - me.fireEvent('onfocus'); - } - }); - - return topic; - }, - - /** - * @param {?mindplot.Topic} currentObject - * @param {Event=} event - * sets focus to the given currentObject and removes it from any other objects if not - * triggered with Ctrl pressed - */ - onObjectFocusEvent(currentObject, event) { - // Close node editors .. - const topics = this.getModel().getTopics(); - _.each(topics, (topic) => { - topic.closeEditors(); - }); - - const model = this.getModel(); - const objects = model.getEntities(); - _.each(objects, (object) => { - // Disable all nodes on focus but not the current if Ctrl key isn't being pressed - if (!$defined(event) || (!event.ctrlKey && !event.metaKey)) { - if (object.isOnFocus() && object !== currentObject) { - object.setOnFocus(false); - } - } - }); - }, - - /** sets focus to all model entities, i.e. relationships and topics */ - selectAll() { - const model = this.getModel(); - const objects = model.getEntities(); - _.each(objects, (object) => { - object.setOnFocus(true); - }); - }, - - /** removes focus from all model entities, i.e. relationships and topics */ - deselectAll() { - const objects = this.getModel().getEntities(); - _.each(objects, (object) => { - object.setOnFocus(false); - }); - }, - - /** - * Set the zoom of the map - * @param {Number} zoom number between 0.3 and 1.9 - */ - setZoom(zoom) { - if (zoom > 1.9 || zoom < 0.3) { - $notify($msg('ZOOM_IN_ERROR')); - return; - } - this.getModel().setZoom(zoom); - this._workspace.setZoom(zoom); - }, - - /** - * @param {Number=} factor - * zoom out by the given factor, or 1.2, if undefined - */ - zoomOut(factor) { - if (!factor) { - factor = 1.2; - } - - const model = this.getModel(); - const scale = model.getZoom() * factor; - if (scale <= 1.9) { - model.setZoom(scale); - this._workspace.setZoom(scale); - } else { - $notify($msg('ZOOM_ERROR')); - } - }, - - /** - * @param {Number=} factor - * zoom in by the given factor, or 1.2, if undefined - */ - zoomIn(factor) { - if (!factor) factor = 1.2; - - const model = this.getModel(); - const scale = model.getZoom() / factor; - - if (scale >= 0.3) { - model.setZoom(scale); - this._workspace.setZoom(scale); - } else { - $notify($msg('ZOOM_ERROR')); - } - }, - - /** copy selected topics to a private clipboard */ - copyToClipboard() { - let topics = this.getModel().filterSelectedTopics(); - if (topics.length <= 0) { - // If there are more than one node selected, - $notify($msg('AT_LEAST_ONE_TOPIC_MUST_BE_SELECTED')); - return; - } - - // Exclude central topic .. - topics = topics.filter((topic) => !topic.isCentralTopic()); - - this._clipboard = topics.map((topic) => { - const nodeModel = topic.getModel().deepCopy(); - - // Change position to make the new topic evident... - const pos = nodeModel.getPosition(); - nodeModel.setPosition(pos.x + 60 * Math.sign(pos.x), pos.y + 30); - - return nodeModel; - }); - - $notify($msg('SELECTION_COPIED_TO_CLIPBOARD')); - }, - - /** paste clipboard contents to the mindmap */ - pasteClipboard() { - if (this._clipboard.length === 0) { - $notify($msg('CLIPBOARD_IS_EMPTY')); - return; - } - this._actionDispatcher.addTopics(this._clipboard); - this._clipboard = []; - }, - - /** @return {mindplot.DesignerModel} model */ - getModel() { - return this._model; - }, - - /** collapse the subtree of the selected topic */ - shrinkSelectedBranch() { - const nodes = this.getModel().filterSelectedTopics(); - if (nodes.length <= 0 || nodes.length !== 1) { - // If there are more than one node selected, - $notify($msg('ONLY_ONE_TOPIC_MUST_BE_SELECTED_COLLAPSE')); - return; - } - // Execute event ... - const topic = nodes[0]; + // Register node listeners ... if (topic.getType() !== INodeModel.CENTRAL_TOPIC_TYPE) { - this._actionDispatcher.shrinkBranch([topic.getId()], !topic.areChildrenShrunken()); + // Central Topic doesn't support to be dragged + this._dragManager.add(topic); } - }, + } - /** create a NodeModel for the selected node's child and add it via the ActionDispatcher */ - createChildForSelectedNode() { - const nodes = this.getModel().filterSelectedTopics(); - if (nodes.length <= 0) { - // If there are more than one node selected, - $notify($msg('ONE_TOPIC_MUST_BE_SELECTED')); - return; - } - if (nodes.length !== 1) { - // If there are more than one node selected, - $notify($msg('ONLY_ONE_TOPIC_MUST_BE_SELECTED')); - return; - } + // Connect Topic ... + const isConnected = model.isConnected(); + if (isConnected) { + // Improve this ... + const targetTopicModel = model.getParent(); + let targetTopic = null; - // Add new node ... - const parentTopic = nodes[0]; - const parentTopicId = parentTopic.getId(); - const childModel = this._createChildModel(parentTopic); - - // Execute event ... - this._actionDispatcher.addTopics([childModel], [parentTopicId]); - }, - - /** - * @private - */ - _copyNodeProps(sourceModel, targetModel) { - // I don't copy the font size if the target is the source is the central topic. - if (sourceModel.getType() !== INodeModel.CENTRAL_TOPIC_TYPE) { - const fontSize = sourceModel.getFontSize(); - if (fontSize) { - targetModel.setFontSize(fontSize); + const topics = this.getModel().getTopics(); + for (let i = 0; i < topics.length; i++) { + const t = topics[i]; + if (t.getModel() === targetTopicModel) { + targetTopic = t; + // Disconnect the node. It will be connected again later ... + model.disconnect(); + break; } } + $assert(targetTopic, 'Could not find a topic to connect'); + topic.connectTo(targetTopic, this._workspace); + } - const fontFamily = sourceModel.getFontFamily(); - if (fontFamily) { - targetModel.setFontFamily(fontFamily); + topic.addEvent('ontblur', () => { + const topics = me.getModel().filterSelectedTopics(); + const rels = me.getModel().filterSelectedRelationships(); + + if (topics.length === 0 || rels.length === 0) { + me.fireEvent('onblur'); } + }); - const fontColor = sourceModel.getFontColor(); - if (fontColor) { - targetModel.setFontColor(fontColor); + topic.addEvent('ontfocus', () => { + const topics = me.getModel().filterSelectedTopics(); + const rels = me.getModel().filterSelectedRelationships(); + + if (topics.length === 1 || rels.length === 1) { + me.fireEvent('onfocus'); } + }); - const fontWeight = sourceModel.getFontWeight(); - if (fontWeight) { - targetModel.setFontWeight(fontWeight); - } + return topic; + } - const fontStyle = sourceModel.getFontStyle(); - if (fontStyle) { - targetModel.setFontStyle(fontStyle); - } + /** + * @param {?mindplot.Topic} currentObject + * @param {Event=} event + * sets focus to the given currentObject and removes it from any other objects if not + * triggered with Ctrl pressed + */ + onObjectFocusEvent(currentObject, event) { + // Close node editors .. + const topics = this.getModel().getTopics(); + _.each(topics, (topic) => { + topic.closeEditors(); + }); - const shape = sourceModel.getShapeType(); - if (shape) { - targetModel.setShapeType(shape); - } - - const borderColor = sourceModel.getBorderColor(); - if (borderColor) { - targetModel.setBorderColor(borderColor); - } - - const backgroundColor = sourceModel.getBackgroundColor(); - if (backgroundColor) { - targetModel.setBackgroundColor(backgroundColor); - } - }, - - /** - * @private - * @param {mindplot.Topic} topic the parent topic of the child to create the NodeModel for - * @param {web2d.Point} mousePos the mouse position - * @return {mindplot.NodeModel} the node model for the new child - */ - _createChildModel(topic, mousePos) { - // Create a new node ... - const parentModel = topic.getModel(); - const mindmap = parentModel.getMindmap(); - const childModel = mindmap.createNode(); - - // Create a new node ... - const layoutManager = this._eventBussDispatcher.getLayoutManager(); - const result = layoutManager.predict(topic.getId(), null, mousePos); - childModel.setOrder(result.order); - - const { position } = result; - childModel.setPosition(position.x, position.y); - - this._copyNodeProps(parentModel, childModel); - - return childModel; - }, - - /** - * @param {Events} event - * @param {mindplot.model.NodeModel} model - * @todo not used - */ - addDraggedNode(event, model) { - $assert(event, 'event can not be null'); - $assert(model, 'model can not be null'); - - // Position far from the visual area ... - model.setPosition(1000, 1000); - - this._actionDispatcher.addTopics([model]); - const topic = this.getModel().findTopicById(model.getId()); - - // Simulate a mouse down event to start the dragging ... - topic.fireEvent('mousedown', event); - }, - - /** - * creates a sibling or child node of the selected node, if the selected node is the - * central topic - */ - createSiblingForSelectedNode() { - const nodes = this.getModel().filterSelectedTopics(); - if (nodes.length <= 0) { - // If there are no nodes selected, - $notify($msg('ONE_TOPIC_MUST_BE_SELECTED')); - return; - } - if (nodes.length > 1) { - // If there are more than one node selected, - $notify($msg('ONLY_ONE_TOPIC_MUST_BE_SELECTED')); - return; - } - - const topic = nodes[0]; - if (!topic.getOutgoingConnectedTopic()) { - // Central topic and isolated topics .... - // Central topic doesn't have siblings ... - this.createChildForSelectedNode(); - } else { - const parentTopic = topic.getOutgoingConnectedTopic(); - const siblingModel = this._createSiblingModel(topic); - - // Hack: if parent is central topic, add node below not on opposite side. - // This should be done in the layout - if (parentTopic.getType() === INodeModel.CENTRAL_TOPIC_TYPE) { - siblingModel.setOrder(topic.getOrder() + 2); + const model = this.getModel(); + const objects = model.getEntities(); + _.each(objects, (object) => { + // Disable all nodes on focus but not the current if Ctrl key isn't being pressed + if (!$defined(event) || (!event.ctrlKey && !event.metaKey)) { + if (object.isOnFocus() && object !== currentObject) { + object.setOnFocus(false); } - - const parentTopicId = parentTopic.getId(); - this._actionDispatcher.addTopics([siblingModel], [parentTopicId]); } - }, + }); + } - /** - * @private - * @param {mindplot.Topic} topic the topic to create the sibling to - * @return {mindplot.NodeModel} the node model of the sibling - */ - _createSiblingModel(topic) { - let result = null; - let model = null; + /** sets focus to all model entities, i.e. relationships and topics */ + selectAll() { + const model = this.getModel(); + const objects = model.getEntities(); + _.each(objects, (object) => { + object.setOnFocus(true); + }); + } + + /** removes focus from all model entities, i.e. relationships and topics */ + deselectAll() { + const objects = this.getModel().getEntities(); + _.each(objects, (object) => { + object.setOnFocus(false); + }); + } + + /** + * Set the zoom of the map + * @param {Number} zoom number between 0.3 and 1.9 + */ + setZoom(zoom) { + if (zoom > 1.9 || zoom < 0.3) { + $notify($msg('ZOOM_IN_ERROR')); + return; + } + this.getModel().setZoom(zoom); + this._workspace.setZoom(zoom); + } + + /** + * @param {Number=} factor + * zoom out by the given factor, or 1.2, if undefined + */ + zoomOut(factor) { + if (!factor) { + factor = 1.2; + } + + const model = this.getModel(); + const scale = model.getZoom() * factor; + if (scale <= 1.9) { + model.setZoom(scale); + this._workspace.setZoom(scale); + } else { + $notify($msg('ZOOM_ERROR')); + } + } + + /** + * @param {Number=} factor + * zoom in by the given factor, or 1.2, if undefined + */ + zoomIn(factor) { + if (!factor) factor = 1.2; + + const model = this.getModel(); + const scale = model.getZoom() / factor; + + if (scale >= 0.3) { + model.setZoom(scale); + this._workspace.setZoom(scale); + } else { + $notify($msg('ZOOM_ERROR')); + } + } + + /** copy selected topics to a private clipboard */ + copyToClipboard() { + let topics = this.getModel().filterSelectedTopics(); + if (topics.length <= 0) { + // If there are more than one node selected, + $notify($msg('AT_LEAST_ONE_TOPIC_MUST_BE_SELECTED')); + return; + } + + // Exclude central topic .. + topics = topics.filter((topic) => !topic.isCentralTopic()); + + this._clipboard = topics.map((topic) => { + const nodeModel = topic.getModel().deepCopy(); + + // Change position to make the new topic evident... + const pos = nodeModel.getPosition(); + nodeModel.setPosition(pos.x + 60 * Math.sign(pos.x), pos.y + 30); + + return nodeModel; + }); + + $notify($msg('SELECTION_COPIED_TO_CLIPBOARD')); + } + + /** paste clipboard contents to the mindmap */ + pasteClipboard() { + if (this._clipboard.length === 0) { + $notify($msg('CLIPBOARD_IS_EMPTY')); + return; + } + this._actionDispatcher.addTopics(this._clipboard); + this._clipboard = []; + } + + /** @return {mindplot.DesignerModel} model */ + getModel() { + return this._model; + } + + /** collapse the subtree of the selected topic */ + shrinkSelectedBranch() { + const nodes = this.getModel().filterSelectedTopics(); + if (nodes.length <= 0 || nodes.length !== 1) { + // If there are more than one node selected, + $notify($msg('ONLY_ONE_TOPIC_MUST_BE_SELECTED_COLLAPSE')); + return; + } + // Execute event ... + const topic = nodes[0]; + if (topic.getType() !== INodeModel.CENTRAL_TOPIC_TYPE) { + this._actionDispatcher.shrinkBranch([topic.getId()], !topic.areChildrenShrunken()); + } + } + + /** create a NodeModel for the selected node's child and add it via the ActionDispatcher */ + createChildForSelectedNode() { + const nodes = this.getModel().filterSelectedTopics(); + if (nodes.length <= 0) { + // If there are more than one node selected, + $notify($msg('ONE_TOPIC_MUST_BE_SELECTED')); + return; + } + if (nodes.length !== 1) { + // If there are more than one node selected, + $notify($msg('ONLY_ONE_TOPIC_MUST_BE_SELECTED')); + return; + } + + // Add new node ... + const parentTopic = nodes[0]; + const parentTopicId = parentTopic.getId(); + const childModel = this._createChildModel(parentTopic); + + // Execute event ... + this._actionDispatcher.addTopics([childModel], [parentTopicId]); + } + + /** + * @private + */ + _copyNodeProps(sourceModel, targetModel) { + // I don't copy the font size if the target is the source is the central topic. + if (sourceModel.getType() !== INodeModel.CENTRAL_TOPIC_TYPE) { + const fontSize = sourceModel.getFontSize(); + if (fontSize) { + targetModel.setFontSize(fontSize); + } + } + + const fontFamily = sourceModel.getFontFamily(); + if (fontFamily) { + targetModel.setFontFamily(fontFamily); + } + + const fontColor = sourceModel.getFontColor(); + if (fontColor) { + targetModel.setFontColor(fontColor); + } + + const fontWeight = sourceModel.getFontWeight(); + if (fontWeight) { + targetModel.setFontWeight(fontWeight); + } + + const fontStyle = sourceModel.getFontStyle(); + if (fontStyle) { + targetModel.setFontStyle(fontStyle); + } + + const shape = sourceModel.getShapeType(); + if (shape) { + targetModel.setShapeType(shape); + } + + const borderColor = sourceModel.getBorderColor(); + if (borderColor) { + targetModel.setBorderColor(borderColor); + } + + const backgroundColor = sourceModel.getBackgroundColor(); + if (backgroundColor) { + targetModel.setBackgroundColor(backgroundColor); + } + } + + /** + * @private + * @param {mindplot.Topic} topic the parent topic of the child to create the NodeModel for + * @param {web2d.Point} mousePos the mouse position + * @return {mindplot.NodeModel} the node model for the new child + */ + _createChildModel(topic, mousePos) { + // Create a new node ... + const parentModel = topic.getModel(); + const mindmap = parentModel.getMindmap(); + const childModel = mindmap.createNode(); + + // Create a new node ... + const layoutManager = this._eventBussDispatcher.getLayoutManager(); + const result = layoutManager.predict(topic.getId(), null, mousePos); + childModel.setOrder(result.order); + + const { position } = result; + childModel.setPosition(position.x, position.y); + + this._copyNodeProps(parentModel, childModel); + + return childModel; + } + + addDraggedNode(event, model) { + $assert(event, 'event can not be null'); + $assert(model, 'model can not be null'); + + // Position far from the visual area ... + model.setPosition(1000, 1000); + + this._actionDispatcher.addTopics([model]); + const topic = this.getModel().findTopicById(model.getId()); + + // Simulate a mouse down event to start the dragging ... + topic.fireEvent('mousedown', event); + } + + /** + * creates a sibling or child node of the selected node, if the selected node is the + * central topic + */ + createSiblingForSelectedNode() { + const nodes = this.getModel().filterSelectedTopics(); + if (nodes.length <= 0) { + // If there are no nodes selected, + $notify($msg('ONE_TOPIC_MUST_BE_SELECTED')); + return; + } + if (nodes.length > 1) { + // If there are more than one node selected, + $notify($msg('ONLY_ONE_TOPIC_MUST_BE_SELECTED')); + return; + } + + const topic = nodes[0]; + if (!topic.getOutgoingConnectedTopic()) { + // Central topic and isolated topics .... + // Central topic doesn't have siblings ... + this.createChildForSelectedNode(); + } else { const parentTopic = topic.getOutgoingConnectedTopic(); - if (parentTopic != null) { - // Create a new node ... - model = topic.getModel(); - const mindmap = model.getMindmap(); - result = mindmap.createNode(); + const siblingModel = this._createSiblingModel(topic); - // Create a new node ... - const order = topic.getOrder() + 1; - result.setOrder(order); - result.setPosition(10, 10); // Set a dummy position ... + // Hack: if parent is central topic, add node below not on opposite side. + // This should be done in the layout + if (parentTopic.getType() === INodeModel.CENTRAL_TOPIC_TYPE) { + siblingModel.setOrder(topic.getOrder() + 2); } - this._copyNodeProps(model, result); + const parentTopicId = parentTopic.getId(); + this._actionDispatcher.addTopics([siblingModel], [parentTopicId]); + } + } - return result; - }, + /** + * @private + * @param {mindplot.Topic} topic the topic to create the sibling to + * @return {mindplot.NodeModel} the node model of the sibling + */ + _createSiblingModel(topic) { + let result = null; + let model = null; + const parentTopic = topic.getOutgoingConnectedTopic(); + if (parentTopic != null) { + // Create a new node ... + model = topic.getModel(); + const mindmap = model.getMindmap(); + result = mindmap.createNode(); - /** - * @param {Event} event - */ - showRelPivot(event) { - const nodes = this.getModel().filterSelectedTopics(); - if (nodes.length <= 0) { - // This could not happen ... - $notify($msg('RELATIONSHIP_COULD_NOT_BE_CREATED')); - return; + // Create a new node ... + const order = topic.getOrder() + 1; + result.setOrder(order); + result.setPosition(10, 10); // Set a dummy position ... + } + + this._copyNodeProps(model, result); + + return result; + } + + /** + * @param {Event} event + */ + showRelPivot(event) { + const nodes = this.getModel().filterSelectedTopics(); + if (nodes.length <= 0) { + // This could not happen ... + $notify($msg('RELATIONSHIP_COULD_NOT_BE_CREATED')); + return; + } + + // Current mouse position .... + const screen = this._workspace.getScreenManager(); + const pos = screen.getWorkspaceMousePosition(event); + + // create a connection ... + this._relPivot.start(nodes[0], pos); + } + + /** @return {{zoom:Number}} the zoom */ + getMindmapProperties() { + const model = this.getModel(); + return { zoom: model.getZoom() }; + } + + /** + * @param {mindplot.Mindmap} mindmapModel + * @throws will throw an error if mindmapModel is null or undefined + */ + loadMap(mindmapModel) { + $assert(mindmapModel, 'mindmapModel can not be null'); + this._mindmap = mindmapModel; + + // Init layout manager ... + const size = { width: 25, height: 25 }; + const layoutManager = new LayoutManager(mindmapModel.getCentralTopic().getId(), size); + const me = this; + layoutManager.addEvent('change', (event) => { + const id = event.getId(); + const topic = me.getModel().findTopicById(id); + topic.setPosition(event.getPosition()); + topic.setOrder(event.getOrder()); + }); + this._eventBussDispatcher.setLayoutManager(layoutManager); + + // Building node graph ... + const branches = mindmapModel.getBranches(); + for (let i = 0; i < branches.length; i++) { + // NodeModel -> NodeGraph ... + const nodeModel = branches[i]; + const nodeGraph = this.nodeModelToNodeGraph(nodeModel); + + // Update shrink render state... + nodeGraph.setBranchVisibility(true); + } + + const relationships = mindmapModel.getRelationships(); + for (let j = 0; j < relationships.length; j++) { + this._relationshipModelToRelationship(relationships[j]); + } + + // Place the focus on the Central Topic + const centralTopic = this.getModel().getCentralTopic(); + this.goToNode(centralTopic); + + // Finally, sort the map ... + EventBus.instance.fireEvent(EventBus.events.DoLayout); + + this.fireEvent('loadSuccess'); + } + + /** */ + getMindmap() { + return this._mindmap; + } + + /** */ + undo() { + // @Todo: This is a hack... + this._actionDispatcher._actionRunner.undo(); + } + + /** */ + redo() { + this._actionDispatcher._actionRunner.redo(); + } + + /** */ + isReadOnly() { + return this._options.readOnly; + } + + /** + * @param {mindplot.model.NodeModel} nodeModel + * @return {mindplot.Topic} the topic (extends mindplot.NodeGraph) created to the model + */ + nodeModelToNodeGraph(nodeModel) { + $assert(nodeModel, 'Node model can not be null'); + let children = nodeModel.getChildren().slice(); + children = children.sort((a, b) => a.getOrder() - b.getOrder()); + + const nodeGraph = this._buildNodeGraph(nodeModel, this.isReadOnly()); + nodeGraph.setVisibility(false); + + this._workspace.append(nodeGraph); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + if ($defined(child)) this.nodeModelToNodeGraph(child); + } + + return nodeGraph; + } + + /** + * @private + * @param {mindplot.model.RelationshipModel} model + * @return {mindplot.Relationship} the relationship created to the model + * @throws will throw an error if model is null or undefined + */ + _relationshipModelToRelationship(model) { + $assert(model, 'Node model can not be null'); + + const result = this._buildRelationshipShape(model); + + const sourceTopic = result.getSourceTopic(); + sourceTopic.addRelationship(result); + + const targetTopic = result.getTargetTopic(); + targetTopic.addRelationship(result); + + result.setVisibility(sourceTopic.isVisible() && targetTopic.isVisible()); + + this._workspace.append(result); + return result; + } + + /** + * @param {mindplot.model.RelationshipModel} model + * @return {mindplot.Relationship} the relationship added to the mindmap + */ + addRelationship(model) { + const mindmap = this.getMindmap(); + mindmap.addRelationship(model); + return this._relationshipModelToRelationship(model); + } + + /** + * deletes the relationship from the linked topics, DesignerModel, Workspace and Mindmap + * @param {mindplot.Relationship} rel the relationship to delete + */ + deleteRelationship(rel) { + const sourceTopic = rel.getSourceTopic(); + sourceTopic.deleteRelationship(rel); + + const targetTopic = rel.getTargetTopic(); + targetTopic.deleteRelationship(rel); + + this.getModel().removeRelationship(rel); + this._workspace.removeChild(rel); + + const mindmap = this.getMindmap(); + mindmap.deleteRelationship(rel.getModel()); + } + + /** + * @private + * @param {mindplot.model.RelationshipModel} model + * @return {mindplot.Relationship} the new relationship with events registered + * @throws will throw an error if the target topic cannot be found + */ + _buildRelationshipShape(model) { + const dmodel = this.getModel(); + + const sourceTopicId = model.getFromNode(); + const sourceTopic = dmodel.findTopicById(sourceTopicId); + + const targetTopicId = model.getToNode(); + const targetTopic = dmodel.findTopicById(targetTopicId); + $assert( + targetTopic, + `targetTopic could not be found:${targetTopicId}${dmodel + .getTopics() + .map((e) => e.getId())}`, + ); + + // Build relationship line .... + const result = new Relationship(sourceTopic, targetTopic, model); + const me = this; + + result.addEvent('ontblur', () => { + const topics = me.getModel().filterSelectedTopics(); + const rels = me.getModel().filterSelectedRelationships(); + + if (topics.length === 0 || rels.length === 0) { + me.fireEvent('onblur'); + } + }); + + result.addEvent('ontfocus', () => { + const topics = me.getModel().filterSelectedTopics(); + const rels = me.getModel().filterSelectedRelationships(); + + if (topics.length === 1 || rels.length === 1) { + me.fireEvent('onfocus'); + } + }); + + // Append it to the workspace ... + dmodel.addRelationship(result); + + return result; + } + + /** + * @param {mindplot.Topic} node the topic to remove + * removes the given topic and its children from Workspace, DesignerModel and NodeModel + */ + removeTopic(node) { + if (!node.isCentralTopic()) { + const parent = node._parent; + node.disconnect(this._workspace); + + // remove children + while (node.getChildren().length > 0) { + this.removeTopic(node.getChildren()[0]); } - // Current mouse position .... - const screen = this._workspace.getScreenManager(); - const pos = screen.getWorkspaceMousePosition(event); + this._workspace.removeChild(node); + this.getModel().removeTopic(node); - // create a connection ... - this._relPivot.start(nodes[0], pos); - }, + // Delete this node from the model... + const model = node.getModel(); + model.deleteNode(); - /** @return {{zoom:Number}} the zoom */ - getMindmapProperties() { - const model = this.getModel(); - return { zoom: model.getZoom() }; - }, + if ($defined(parent)) { + this.goToNode(parent); + } + } + } - /** - * @param {mindplot.Mindmap} mindmapModel - * @throws will throw an error if mindmapModel is null or undefined - */ - loadMap(mindmapModel) { - $assert(mindmapModel, 'mindmapModel can not be null'); - this._mindmap = mindmapModel; + /** + * @private + */ + _resetEdition() { + const screenManager = this._workspace.getScreenManager(); + screenManager.fireEvent('update'); + screenManager.fireEvent('mouseup'); + this._relPivot.dispose(); + } - // Init layout manager ... - const size = { width: 25, height: 25 }; - const layoutManager = new LayoutManager(mindmapModel.getCentralTopic().getId(), size); - const me = this; - layoutManager.addEvent('change', (event) => { - const id = event.getId(); - const topic = me.getModel().findTopicById(id); - topic.setPosition(event.getPosition()); - topic.setOrder(event.getOrder()); + /** */ + deleteSelectedEntities() { + // Is there some action in progress ?. + this._resetEdition(); + + const topics = this.getModel().filterSelectedTopics(); + const relation = this.getModel().filterSelectedRelationships(); + if (topics.length <= 0 && relation.length <= 0) { + // If there are more than one node selected, + $notify($msg('ENTITIES_COULD_NOT_BE_DELETED')); + return; + } + if (topics.length === 1 && topics[0].isCentralTopic()) { + $notify($msg('CENTRAL_TOPIC_CAN_NOT_BE_DELETED')); + return; + } + + // If the central topic has been selected, I must filter ir + const topicIds = topics + .filter((topic) => !topic.isCentralTopic()) + .map((topic) => topic.getId()); + + const relIds = relation.map((rel) => rel.getId()); + + // Finally delete the topics ... + if (topicIds.length > 0 || relIds.length > 0) { + this._actionDispatcher.deleteEntities(topicIds, relIds); + } + } + + /** */ + changeFontFamily(font) { + const topicsIds = this.getModel().filterTopicsIds(); + if (topicsIds.length > 0) { + this._actionDispatcher.changeFontFamilyToTopic(topicsIds, font); + } + } + + /** */ + changeFontStyle() { + const topicsIds = this.getModel().filterTopicsIds(); + if (topicsIds.length > 0) { + this._actionDispatcher.changeFontStyleToTopic(topicsIds); + } + } + + /** */ + changeFontColor(color) { + $assert(color, 'color can not be null'); + + const topicsIds = this.getModel().filterTopicsIds(); + if (topicsIds.length > 0) { + this._actionDispatcher.changeFontColorToTopic(topicsIds, color); + } + } + + /** */ + changeBackgroundColor(color) { + const validateFunc = (topic) => topic.getShapeType() !== TopicShape.LINE; + const validateError = 'Color can not be set to line topics.'; + + const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError); + if (topicsIds.length > 0) { + this._actionDispatcher.changeBackgroundColorToTopic(topicsIds, color); + } + } + + /** */ + changeBorderColor(color) { + const validateFunc = (topic) => topic.getShapeType() !== TopicShape.LINE; + const validateError = 'Color can not be set to line topics.'; + const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError); + if (topicsIds.length > 0) { + this._actionDispatcher.changeBorderColorToTopic(topicsIds, color); + } + } + + /** */ + changeFontSize(size) { + const topicsIds = this.getModel().filterTopicsIds(); + if (topicsIds.length > 0) { + this._actionDispatcher.changeFontSizeToTopic(topicsIds, size); + } + } + + /** */ + changeTopicShape(shape) { + const validateFunc = (topic) => !(topic.getType() === INodeModel.CENTRAL_TOPIC_TYPE && shape === TopicShape.LINE + ); + + const validateError = 'Central Topic shape can not be changed to line figure.'; + const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError); + if (topicsIds.length > 0) { + this._actionDispatcher.changeShapeTypeToTopic(topicsIds, shape); + } + } + + /** */ + changeFontWeight() { + const topicsIds = this.getModel().filterTopicsIds(); + if (topicsIds.length > 0) { + this._actionDispatcher.changeFontWeightToTopic(topicsIds); + } + } + + /** */ + addIconType(iconType) { + const topicsIds = this.getModel().filterTopicsIds(); + if (topicsIds.length > 0) { + this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeature.Icon.id, { + id: iconType, }); - this._eventBussDispatcher.setLayoutManager(layoutManager); + } + } - // Building node graph ... - const branches = mindmapModel.getBranches(); - for (let i = 0; i < branches.length; i++) { - // NodeModel -> NodeGraph ... - const nodeModel = branches[i]; - const nodeGraph = this.nodeModelToNodeGraph(nodeModel); + /** + * lets the selected topic open the link editor where the user can define or modify an + * existing link + */ + addLink() { + const model = this.getModel(); + const topic = model.selectedTopic(); + if (topic) { + topic.showLinkEditor(); + this.onObjectFocusEvent(); + } + } - // Update shrink render state... - nodeGraph.setBranchVisibility(true); - } + /** */ + addNote() { + const model = this.getModel(); + const topic = model.selectedTopic(); + if (topic) { + topic.showNoteEditor(); + this.onObjectFocusEvent(); + } + } - const relationships = mindmapModel.getRelationships(); - for (let j = 0; j < relationships.length; j++) { - this._relationshipModelToRelationship(relationships[j]); - } + /** + * @param {mindplot.Topic} node + * sets the focus to the given node + */ + goToNode(node) { + node.setOnFocus(true); + this.onObjectFocusEvent(node); + } - // Place the focus on the Central Topic - const centralTopic = this.getModel().getCentralTopic(); - this.goToNode(centralTopic); - - // Finally, sort the map ... - EventBus.instance.fireEvent(EventBus.events.DoLayout); - - this.fireEvent('loadSuccess'); - }, - - /** */ - getMindmap() { - return this._mindmap; - }, - - /** */ - undo() { - // @Todo: This is a hack... - this._actionDispatcher._actionRunner.undo(); - }, - - /** */ - redo() { - this._actionDispatcher._actionRunner.redo(); - }, - - /** */ - isReadOnly() { - return this._options.readOnly; - }, - - /** - * @param {mindplot.model.NodeModel} nodeModel - * @return {mindplot.Topic} the topic (extends mindplot.NodeGraph) created to the model - */ - nodeModelToNodeGraph(nodeModel) { - $assert(nodeModel, 'Node model can not be null'); - let children = nodeModel.getChildren().slice(); - children = children.sort((a, b) => a.getOrder() - b.getOrder()); - - const nodeGraph = this._buildNodeGraph(nodeModel, this.isReadOnly()); - nodeGraph.setVisibility(false); - - this._workspace.append(nodeGraph); - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if ($defined(child)) this.nodeModelToNodeGraph(child); - } - - return nodeGraph; - }, - - /** - * @private - * @param {mindplot.model.RelationshipModel} model - * @return {mindplot.Relationship} the relationship created to the model - * @throws will throw an error if model is null or undefined - */ - _relationshipModelToRelationship(model) { - $assert(model, 'Node model can not be null'); - - const result = this._buildRelationshipShape(model); - - const sourceTopic = result.getSourceTopic(); - sourceTopic.addRelationship(result); - - const targetTopic = result.getTargetTopic(); - targetTopic.addRelationship(result); - - result.setVisibility(sourceTopic.isVisible() && targetTopic.isVisible()); - - this._workspace.append(result); - return result; - }, - - /** - * @param {mindplot.model.RelationshipModel} model - * @return {mindplot.Relationship} the relationship added to the mindmap - */ - addRelationship(model) { - const mindmap = this.getMindmap(); - mindmap.addRelationship(model); - return this._relationshipModelToRelationship(model); - }, - - /** - * deletes the relationship from the linked topics, DesignerModel, Workspace and Mindmap - * @param {mindplot.Relationship} rel the relationship to delete - */ - deleteRelationship(rel) { - const sourceTopic = rel.getSourceTopic(); - sourceTopic.deleteRelationship(rel); - - const targetTopic = rel.getTargetTopic(); - targetTopic.deleteRelationship(rel); - - this.getModel().removeRelationship(rel); - this._workspace.removeChild(rel); - - const mindmap = this.getMindmap(); - mindmap.deleteRelationship(rel.getModel()); - }, - - /** - * @private - * @param {mindplot.model.RelationshipModel} model - * @return {mindplot.Relationship} the new relationship with events registered - * @throws will throw an error if the target topic cannot be found - */ - _buildRelationshipShape(model) { - const dmodel = this.getModel(); - - const sourceTopicId = model.getFromNode(); - const sourceTopic = dmodel.findTopicById(sourceTopicId); - - const targetTopicId = model.getToNode(); - const targetTopic = dmodel.findTopicById(targetTopicId); - $assert( - targetTopic, - `targetTopic could not be found:${targetTopicId}${dmodel - .getTopics() - .map((e) => e.getId())}`, - ); - - // Build relationship line .... - const result = new Relationship(sourceTopic, targetTopic, model); - const me = this; - - result.addEvent('ontblur', () => { - const topics = me.getModel().filterSelectedTopics(); - const rels = me.getModel().filterSelectedRelationships(); - - if (topics.length === 0 || rels.length === 0) { - me.fireEvent('onblur'); - } - }); - - result.addEvent('ontfocus', () => { - const topics = me.getModel().filterSelectedTopics(); - const rels = me.getModel().filterSelectedRelationships(); - - if (topics.length === 1 || rels.length === 1) { - me.fireEvent('onfocus'); - } - }); - - // Append it to the workspace ... - dmodel.addRelationship(result); - - return result; - }, - - /** - * @param {mindplot.Topic} node the topic to remove - * removes the given topic and its children from Workspace, DesignerModel and NodeModel - */ - removeTopic(node) { - if (!node.isCentralTopic()) { - const parent = node._parent; - node.disconnect(this._workspace); - - // remove children - while (node.getChildren().length > 0) { - this.removeTopic(node.getChildren()[0]); - } - - this._workspace.removeChild(node); - this.getModel().removeTopic(node); - - // Delete this node from the model... - const model = node.getModel(); - model.deleteNode(); - - if ($defined(parent)) { - this.goToNode(parent); - } - } - }, - - /** - * @private - */ - _resetEdition() { - const screenManager = this._workspace.getScreenManager(); - screenManager.fireEvent('update'); - screenManager.fireEvent('mouseup'); - this._relPivot.dispose(); - }, - - /** */ - deleteSelectedEntities() { - // Is there some action in progress ?. - this._resetEdition(); - - const topics = this.getModel().filterSelectedTopics(); - const relation = this.getModel().filterSelectedRelationships(); - if (topics.length <= 0 && relation.length <= 0) { - // If there are more than one node selected, - $notify($msg('ENTITIES_COULD_NOT_BE_DELETED')); - return; - } - if (topics.length === 1 && topics[0].isCentralTopic()) { - $notify($msg('CENTRAL_TOPIC_CAN_NOT_BE_DELETED')); - return; - } - - // If the central topic has been selected, I must filter ir - const topicIds = topics - .filter((topic) => !topic.isCentralTopic()) - .map((topic) => topic.getId()); - - const relIds = relation.map((rel) => rel.getId()); - - // Finally delete the topics ... - if (topicIds.length > 0 || relIds.length > 0) { - this._actionDispatcher.deleteEntities(topicIds, relIds); - } - }, - - /** */ - changeFontFamily(font) { - const topicsIds = this.getModel().filterTopicsIds(); - if (topicsIds.length > 0) { - this._actionDispatcher.changeFontFamilyToTopic(topicsIds, font); - } - }, - - /** */ - changeFontStyle() { - const topicsIds = this.getModel().filterTopicsIds(); - if (topicsIds.length > 0) { - this._actionDispatcher.changeFontStyleToTopic(topicsIds); - } - }, - - /** */ - changeFontColor(color) { - $assert(color, 'color can not be null'); - - const topicsIds = this.getModel().filterTopicsIds(); - if (topicsIds.length > 0) { - this._actionDispatcher.changeFontColorToTopic(topicsIds, color); - } - }, - - /** */ - changeBackgroundColor(color) { - const validateFunc = (topic) => topic.getShapeType() !== TopicShape.LINE; - const validateError = 'Color can not be set to line topics.'; - - const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError); - if (topicsIds.length > 0) { - this._actionDispatcher.changeBackgroundColorToTopic(topicsIds, color); - } - }, - - /** */ - changeBorderColor(color) { - const validateFunc = (topic) => topic.getShapeType() !== TopicShape.LINE; - const validateError = 'Color can not be set to line topics.'; - const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError); - if (topicsIds.length > 0) { - this._actionDispatcher.changeBorderColorToTopic(topicsIds, color); - } - }, - - /** */ - changeFontSize(size) { - const topicsIds = this.getModel().filterTopicsIds(); - if (topicsIds.length > 0) { - this._actionDispatcher.changeFontSizeToTopic(topicsIds, size); - } - }, - - /** */ - changeTopicShape(shape) { - const validateFunc = (topic) => !(topic.getType() === INodeModel.CENTRAL_TOPIC_TYPE && shape === TopicShape.LINE - ); - - const validateError = 'Central Topic shape can not be changed to line figure.'; - const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError); - if (topicsIds.length > 0) { - this._actionDispatcher.changeShapeTypeToTopic(topicsIds, shape); - } - }, - - /** */ - changeFontWeight() { - const topicsIds = this.getModel().filterTopicsIds(); - if (topicsIds.length > 0) { - this._actionDispatcher.changeFontWeightToTopic(topicsIds); - } - }, - - /** */ - addIconType(iconType) { - const topicsIds = this.getModel().filterTopicsIds(); - if (topicsIds.length > 0) { - this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeature.Icon.id, { - id: iconType, - }); - } - }, - - /** - * lets the selected topic open the link editor where the user can define or modify an - * existing link - */ - addLink() { - const model = this.getModel(); - const topic = model.selectedTopic(); - if (topic) { - topic.showLinkEditor(); - this.onObjectFocusEvent(); - } - }, - - /** */ - addNote() { - const model = this.getModel(); - const topic = model.selectedTopic(); - if (topic) { - topic.showNoteEditor(); - this.onObjectFocusEvent(); - } - }, - - /** - * @param {mindplot.Topic} node - * sets the focus to the given node - */ - goToNode(node) { - node.setOnFocus(true); - this.onObjectFocusEvent(node); - }, - - /** @return {mindplot.Workspace} */ - getWorkSpace() { - return this._workspace; - }, - }, -); + /** @return {mindplot.Workspace} */ + getWorkSpace() { + return this._workspace; + } +} export default Designer; diff --git a/packages/mindplot/src/components/DesignerKeyboard.js b/packages/mindplot/src/components/DesignerKeyboard.js index 35816de6..4089bdc3 100644 --- a/packages/mindplot/src/components/DesignerKeyboard.js +++ b/packages/mindplot/src/components/DesignerKeyboard.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; import Keyboard from './Keyboard'; const DesignerKeyboard = new Class({ diff --git a/packages/mindplot/src/components/DesignerModel.js b/packages/mindplot/src/components/DesignerModel.js index 11289985..04678dcd 100644 --- a/packages/mindplot/src/components/DesignerModel.js +++ b/packages/mindplot/src/components/DesignerModel.js @@ -15,51 +15,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; import Events from './Events'; -const DesignerModel = new Class(/** @lends DesignerModel */{ - Implements: [Events], - /** - * @implements {mindplot.Events} - * @constructs - * @param {{readOnly: Boolean, zoom: Number, saveOnLoad: Boolean, size: {width: Number, - * height: Number}, viewPort: {width: Number, height: Number}, container: String, - * persistenceManager: String, mapId: String, locale: String}} options - * options loaded from json config - * @see {@link ConfigParameters.md} - * @see {@link editor.html} - */ - initialize(options) { +class DesignerModel extends Events { + constructor(options) { + super(); this._zoom = options.zoom; this._topics = []; this._relationships = []; - }, + } /** @return {Number} zoom between 0.3 (largest text) and 1.9 */ getZoom() { return this._zoom; - }, + } /** @param {Number} zoom number between 0.3 and 1.9 to set the zoom to */ setZoom(zoom) { this._zoom = zoom; - }, + } /** @return {@link mindplot.Topic[]} all topics */ getTopics() { return this._topics; - }, + } /** @return {mindplot.Relationship[]} all relationships */ getRelationships() { return this._relationships; - }, + } /** @return {mindplot.CentralTopic} the central topic */ getCentralTopic() { const topics = this.getTopics(); return topics[0]; - }, + } /** @return {mindplot.Topic[]} selected topics */ filterSelectedTopics() { @@ -70,7 +61,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ } } return result; - }, + } /** * @return {mindplot.Relationship[]} selected relationships @@ -83,7 +74,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ } } return result; - }, + } /** * @return {Array.} all topics and relationships @@ -92,7 +83,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ const result = [].append(this._topics); result.append(this._relationships); return result; - }, + } /** * removes occurrences of the given topic from the topic array @@ -101,7 +92,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ removeTopic(topic) { $assert(topic, 'topic can not be null'); this._topics.erase(topic); - }, + } /** * removes occurrences of the given relationship from the relationship array @@ -110,7 +101,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ removeRelationship(rel) { $assert(rel, 'rel can not be null'); this._relationships.erase(rel); - }, + } /** * adds the given topic to the topic array @@ -122,7 +113,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ $assert(topic, 'topic can not be null'); $assert(typeof topic.getId() === 'number', `id is not a number:${topic.getId()}`); this._topics.push(topic); - }, + } /** * adds the given relationship to the relationship array @@ -132,7 +123,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ addRelationship(rel) { $assert(rel, 'rel can not be null'); this._relationships.push(rel); - }, + } /** * @param {Function=} validate a function to validate nodes @@ -158,7 +149,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ } } return result; - }, + } /** * @return {mindplot.Topic} the first selected topic if one or more are found by the @@ -167,7 +158,7 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ selectedTopic() { const topics = this.filterSelectedTopics(); return (topics.length > 0) ? topics[0] : null; - }, + } /** * @param {String} id the id of the topic to be retrieved @@ -177,13 +168,13 @@ const DesignerModel = new Class(/** @lends DesignerModel */{ let result = null; for (let i = 0; i < this._topics.length; i++) { const topic = this._topics[i]; - if (topic.getId() == id) { + if (topic.getId() === id) { result = topic; break; } } return result; - }, -}); + } +} export default DesignerModel; diff --git a/packages/mindplot/src/components/DesignerUndoManager.js b/packages/mindplot/src/components/DesignerUndoManager.js index 15f06754..d6b425ff 100644 --- a/packages/mindplot/src/components/DesignerUndoManager.js +++ b/packages/mindplot/src/components/DesignerUndoManager.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; const DesignerUndoManager = new Class({ initialize(fireChange) { diff --git a/packages/mindplot/src/components/DragConnector.js b/packages/mindplot/src/components/DragConnector.js index da539906..e65402ab 100644 --- a/packages/mindplot/src/components/DragConnector.js +++ b/packages/mindplot/src/components/DragConnector.js @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import { $assert } from '@wisemapping/core-js'; const DragConnector = new Class({ initialize(designerModel, workspace) { $assert(designerModel, 'designerModel can not be null'); diff --git a/packages/mindplot/src/components/DragManager.js b/packages/mindplot/src/components/DragManager.js index 9d9cbdca..b4a587ae 100644 --- a/packages/mindplot/src/components/DragManager.js +++ b/packages/mindplot/src/components/DragManager.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; import DragTopic from './DragTopic'; const DragManager = new Class({ diff --git a/packages/mindplot/src/components/DragPivot.js b/packages/mindplot/src/components/DragPivot.js index 5ce7ff4c..215e5fae 100644 --- a/packages/mindplot/src/components/DragPivot.js +++ b/packages/mindplot/src/components/DragPivot.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; import web2d from '@wisemapping/web2d'; import DragTopicConfig from './DragTopicConfig'; diff --git a/packages/mindplot/src/components/DragTopic.js b/packages/mindplot/src/components/DragTopic.js index f011df0a..dc59a3aa 100644 --- a/packages/mindplot/src/components/DragTopic.js +++ b/packages/mindplot/src/components/DragTopic.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; import web2d from '@wisemapping/web2d'; import ActionDispatcher from './ActionDispatcher'; diff --git a/packages/mindplot/src/components/Events.js b/packages/mindplot/src/components/Events.js index dbad7a59..9d0c96a8 100644 --- a/packages/mindplot/src/components/Events.js +++ b/packages/mindplot/src/components/Events.js @@ -1,20 +1,41 @@ -const Events = new Class({ - $events: {}, +/* + * Copyright [2015] [wisemapping] + * + * Licensed under WiseMapping Public License, Version 1.0 (the "License"). + * It is basically the Apache License, Version 2.0 (the "License") plus the + * "powered by wisemapping" text requirement on every single page; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the license at + * + * http://www.wisemapping.org/license + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import _ from '@libraries/underscore-min'; - _removeOn(string) { +class Events { + constructor() { + this.$events = {}; + } + + static _removeOn(string) { return string.replace(/^on([A-Z])/, (full, first) => first.toLowerCase()); - }, + } addEvent(type, fn, internal) { - type = this._removeOn(type); + type = Events._removeOn(type); this.$events[type] = (this.$events[type] || []).include(fn); if (internal) fn.internal = true; return this; - }, + } fireEvent(type, args, delay) { - type = this._removeOn(type); + type = Events._removeOn(type); const events = this.$events[type]; if (!events) return this; args = Array.isArray(args) ? args : [args]; @@ -27,17 +48,17 @@ const Events = new Class({ this, ); return this; - }, + } removeEvent(type, fn) { - type = this._removeOn(type); + type = Events._removeOn(type); const events = this.$events[type]; if (events && !fn.internal) { const index = events.indexOf(fn); - if (index != -1) events.splice(index, 1); + if (index !== -1) events.splice(index, 1); } return this; - }, -}); + } +} export default Events; diff --git a/packages/mindplot/src/components/IconGroup.js b/packages/mindplot/src/components/IconGroup.js index 48b0477e..6c819dda 100644 --- a/packages/mindplot/src/components/IconGroup.js +++ b/packages/mindplot/src/components/IconGroup.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; import web2d from '@wisemapping/web2d'; import Icon from './Icon'; diff --git a/packages/mindplot/src/components/Keyboard.js b/packages/mindplot/src/components/Keyboard.js index 0ae86470..d360d83e 100644 --- a/packages/mindplot/src/components/Keyboard.js +++ b/packages/mindplot/src/components/Keyboard.js @@ -16,11 +16,8 @@ * limitations under the License. */ -const Keyboard = new Class({ - - initialize() { - }, - +class Keyboard { + // eslint-disable-next-line class-methods-use-this addShortcut(shortcuts, callback) { if (!$.isArray(shortcuts)) { shortcuts = [shortcuts]; @@ -28,8 +25,7 @@ const Keyboard = new Class({ _.each(shortcuts, (shortcut) => { $(document).bind('keydown', shortcut, callback); }); - }, - -}); + } +} export default Keyboard; diff --git a/packages/mindplot/src/components/LinkIcon.js b/packages/mindplot/src/components/LinkIcon.js index ad484edf..27c02d36 100644 --- a/packages/mindplot/src/components/LinkIcon.js +++ b/packages/mindplot/src/components/LinkIcon.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; import Icon from './Icon'; import LinkIconTooltip from './widget/LinkIconTooltip'; diff --git a/packages/mindplot/src/components/MainTopic.js b/packages/mindplot/src/components/MainTopic.js index 9f2da86c..74a40c62 100644 --- a/packages/mindplot/src/components/MainTopic.js +++ b/packages/mindplot/src/components/MainTopic.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; import web2d from '@wisemapping/web2d'; import Topic from './Topic'; diff --git a/packages/mindplot/src/components/NodeGraph.js b/packages/mindplot/src/components/NodeGraph.js index 003b9d69..f38aa96a 100644 --- a/packages/mindplot/src/components/NodeGraph.js +++ b/packages/mindplot/src/components/NodeGraph.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; import TopicConfig from './TopicConfig'; import DragTopic from './DragTopic'; diff --git a/packages/mindplot/src/components/NodeGraphUtils.js b/packages/mindplot/src/components/NodeGraphUtils.js index 3b466eb3..30ec6aa2 100644 --- a/packages/mindplot/src/components/NodeGraphUtils.js +++ b/packages/mindplot/src/components/NodeGraphUtils.js @@ -1,3 +1,5 @@ +import { $assert } from '@wisemapping/core-js'; + import CentralTopic from './CentralTopic'; import MainTopic from './MainTopic'; import INodeModel from './model/INodeModel'; diff --git a/packages/mindplot/src/components/NoteIcon.js b/packages/mindplot/src/components/NoteIcon.js index 2a42ea62..79fd89a0 100644 --- a/packages/mindplot/src/components/NoteIcon.js +++ b/packages/mindplot/src/components/NoteIcon.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; import Icon from './Icon'; import FloatingTip from './widget/FloatingTip'; diff --git a/packages/mindplot/src/components/Relationship.js b/packages/mindplot/src/components/Relationship.js index d0d233b7..abb0ddd3 100644 --- a/packages/mindplot/src/components/Relationship.js +++ b/packages/mindplot/src/components/Relationship.js @@ -16,6 +16,7 @@ * limitations under the License. */ import web2d from '@wisemapping/web2d'; +import { $assert, $defined } from '@wisemapping/core-js'; import ConnectionLine from './ConnectionLine'; import ControlPoint from './ControlPoint'; diff --git a/packages/mindplot/src/components/RestPersistenceManager.js b/packages/mindplot/src/components/RestPersistenceManager.js index e3a8cfda..e2ffc9f9 100644 --- a/packages/mindplot/src/components/RestPersistenceManager.js +++ b/packages/mindplot/src/components/RestPersistenceManager.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; import PersistenceManager from './PersistenceManager'; const RESTPersistenceManager = new Class({ diff --git a/packages/mindplot/src/components/StandaloneActionDispatcher.js b/packages/mindplot/src/components/StandaloneActionDispatcher.js index 740e4f5c..bb62b7a1 100644 --- a/packages/mindplot/src/components/StandaloneActionDispatcher.js +++ b/packages/mindplot/src/components/StandaloneActionDispatcher.js @@ -30,345 +30,238 @@ import ChangeFeatureToTopicCommand from './commands/ChangeFeatureToTopicCommand' import NodeModel from './model/NodeModel'; import EventBus from './layout/EventBus'; -const StandaloneActionDispatcher = new Class( - /** @lends StandaloneActionDispatcher */ { - Extends: ActionDispatcher, - /** - * @extends mindplot.ActionDispatcher - * @constructs - * @param {mindplot.CommandContext} commandContext - */ - initialize(commandContext) { - this.parent(commandContext); - this._actionRunner = new DesignerActionRunner(commandContext, this); - }, +class StandaloneActionDispatcher extends ActionDispatcher { + constructor(commandContext) { + super(commandContext); + this._actionRunner = new DesignerActionRunner(commandContext, this); + } - /** */ - addTopics(models, parentTopicsId) { - const command = new AddTopicCommand(models, parentTopicsId); - this.execute(command); - }, + /** */ + addTopics(models, parentTopicsId) { + const command = new AddTopicCommand(models, parentTopicsId); + this.execute(command); + } - /** */ - addRelationship(model) { - const command = new AddRelationshipCommand(model); - this.execute(command); - }, + /** */ + addRelationship(model) { + const command = new AddRelationshipCommand(model); + this.execute(command); + } - /** */ - deleteEntities(topicsIds, relIds) { - const command = new DeleteCommand(topicsIds, relIds); - this.execute(command); - }, + /** */ + deleteEntities(topicsIds, relIds) { + const command = new DeleteCommand(topicsIds, relIds); + this.execute(command); + } - /** */ - dragTopic(topicId, position, order, parentTopic) { - const command = new DragTopicCommand(topicId, position, order, parentTopic); - this.execute(command); - }, + /** */ + dragTopic(topicId, position, order, parentTopic) { + const command = new DragTopicCommand(topicId, position, order, parentTopic); + this.execute(command); + } - /** */ - moveTopic(topicId, position) { - $assert($defined(topicId), 'topicsId can not be null'); - $assert($defined(position), 'position can not be null'); + /** */ + moveTopic(topicId, position) { + $assert($defined(topicId), 'topicsId can not be null'); + $assert($defined(position), 'position can not be null'); - const commandFunc = (topic, value) => { - const result = topic.getPosition(); - EventBus.instance.fireEvent(EventBus.events.NodeMoveEvent, { - node: topic.getModel(), - position: value, - }); - return result; - }; - - const command = new GenericFunctionCommand(commandFunc, topicId, position); - this.execute(command); - }, - - /** */ - moveControlPoint(ctrlPoint, point) { - const command = new MoveControlPointCommand(ctrlPoint, point); - this.execute(command); - }, - - /** */ - changeFontStyleToTopic(topicsIds) { - const commandFunc = (topic) => { - const result = topic.getFontStyle(); - const style = result === 'italic' ? 'normal' : 'italic'; - topic.setFontStyle(style, true); - return result; - }; - const command = new GenericFunctionCommand(commandFunc, topicsIds); - this.execute(command); - }, - - /** */ - changeTextToTopic(topicsIds, text) { - $assert($defined(topicsIds), 'topicsIds can not be null'); - - const commandFunc = (topic, value) => { - const result = topic.getText(); - topic.setText(value); - return result; - }; - commandFunc.commandType = 'changeTextToTopic'; - - const command = new GenericFunctionCommand(commandFunc, topicsIds, text); - this.execute(command); - }, - - /** */ - changeFontFamilyToTopic(topicIds, fontFamily) { - $assert(topicIds, 'topicIds can not be null'); - $assert(fontFamily, 'fontFamily can not be null'); - - const commandFunc = (topic, fontFamily) => { - const result = topic.getFontFamily(); - topic.setFontFamily(fontFamily, true); - - topic._adjustShapes(); - return result; - }; - - const command = new GenericFunctionCommand(commandFunc, topicIds, fontFamily); - this.execute(command); - }, - - /** */ - changeFontColorToTopic(topicsIds, color) { - $assert(topicsIds, 'topicIds can not be null'); - $assert(color, 'color can not be null'); - - const commandFunc = (topic, color) => { - const result = topic.getFontColor(); - topic.setFontColor(color, true); - return result; - }; - - const command = new GenericFunctionCommand(commandFunc, topicsIds, color); - command.discardDuplicated = 'fontColorCommandId'; - this.execute(command); - }, - - /** */ - changeBackgroundColorToTopic(topicsIds, color) { - $assert(topicsIds, 'topicIds can not be null'); - $assert(color, 'color can not be null'); - - const commandFunc = (topic, color) => { - const result = topic.getBackgroundColor(); - topic.setBackgroundColor(color); - return result; - }; - - const command = new GenericFunctionCommand(commandFunc, topicsIds, color); - command.discardDuplicated = 'backColor'; - this.execute(command); - }, - - /** */ - changeBorderColorToTopic(topicsIds, color) { - $assert(topicsIds, 'topicIds can not be null'); - $assert(color, 'topicIds can not be null'); - - const commandFunc = (topic, color) => { - const result = topic.getBorderColor(); - topic.setBorderColor(color); - return result; - }; - - const command = new GenericFunctionCommand(commandFunc, topicsIds, color); - command.discardDuplicated = 'borderColorCommandId'; - this.execute(command); - }, - - /** */ - changeFontSizeToTopic(topicsIds, size) { - $assert(topicsIds, 'topicIds can not be null'); - $assert(size, 'size can not be null'); - - const commandFunc = (topic, size) => { - const result = topic.getFontSize(); - topic.setFontSize(size, true); - - topic._adjustShapes(); - return result; - }; - - const command = new GenericFunctionCommand(commandFunc, topicsIds, size); - this.execute(command); - }, - - /** */ - changeShapeTypeToTopic(topicsIds, shapeType) { - $assert(topicsIds, 'topicsIds can not be null'); - $assert(shapeType, 'shapeType can not be null'); - - const commandFunc = (topic, shapeType) => { - const result = topic.getShapeType(); - topic.setShapeType(shapeType, true); - return result; - }; - - const command = new GenericFunctionCommand(commandFunc, topicsIds, shapeType); - this.execute(command); - }, - - /** */ - changeFontWeightToTopic(topicsIds) { - $assert(topicsIds, 'topicsIds can not be null'); - - const commandFunc = (topic) => { - const result = topic.getFontWeight(); - const weight = result === 'bold' ? 'normal' : 'bold'; - topic.setFontWeight(weight, true); - - topic._adjustShapes(); - return result; - }; - - const command = new GenericFunctionCommand(commandFunc, topicsIds); - this.execute(command); - }, - - /** */ - shrinkBranch(topicsIds, collapse) { - $assert(topicsIds, 'topicsIds can not be null'); - - const commandFunc = (topic, isShrink) => { - topic.setChildrenShrunken(isShrink); - return !isShrink; - }; - - const command = new GenericFunctionCommand(commandFunc, topicsIds, collapse); - this.execute(command, false); - }, - - /** */ - addFeatureToTopic(topicId, featureType, attributes) { - const command = new AddFeatureToTopicCommand(topicId, featureType, attributes); - this.execute(command); - }, - - /** */ - changeFeatureToTopic(topicId, featureId, attributes) { - const command = new ChangeFeatureToTopicCommand(topicId, featureId, attributes); - this.execute(command); - }, - - /** */ - removeFeatureFromTopic(topicId, featureId) { - const command = new RemoveFeatureFromTopicCommand(topicId, featureId); - this.execute(command); - }, - - /** */ - execute(command) { - this._actionRunner.execute(command); - }, - }, -); - -const CommandContext = new Class( - /** @lends CommandContext */ { - /** - * @constructs - * @param {mindplot.Designer} designer - */ - initialize(designer) { - $assert(designer, 'designer can not be null'); - this._designer = designer; - }, - - /** */ - findTopics(topicsIds) { - $assert($defined(topicsIds), 'topicsIds can not be null'); - if (!(topicsIds instanceof Array)) { - topicsIds = [topicsIds]; - } - - const designerTopics = this._designer.getModel().getTopics(); - const result = designerTopics.filter((topic) => topicsIds.contains(topic.getId())); - - if (result.length !== topicsIds.length) { - const ids = designerTopics.map((topic) => topic.getId()); - $assert( - result.length === topicsIds.length, - `Could not find topic. Result:${result - }, Filter Criteria:${topicsIds - }, Current Topics: [${ids - }]`, - ); - } - return result; - }, - - /** */ - deleteTopic(topic) { - this._designer.removeTopic(topic); - }, - - /** */ - createTopic(model) { - $assert(model, 'model can not be null'); - return this._designer.nodeModelToNodeGraph(model); - }, - - /** */ - createModel() { - const mindmap = this._designer.getMindmap(); - return mindmap.createNode(NodeModel.MAIN_TOPIC_TYPE); - }, - - /** */ - addTopic(topic) { - const mindmap = this._designer.getMindmap(); - return mindmap.addBranch(topic.getModel()); - }, - - /** */ - connect(childTopic, parentTopic) { - childTopic.connectTo(parentTopic, this._designer._workspace); - }, - - /** */ - disconnect(topic) { - topic.disconnect(this._designer._workspace); - }, - - /** */ - addRelationship(model) { - $assert(model, 'model cannot be null'); - return this._designer.addRelationship(model); - }, - - /** */ - deleteRelationship(relationship) { - this._designer.deleteRelationship(relationship); - }, - - /** */ - findRelationships(relIds) { - $assert($defined(relIds), 'relId can not be null'); - if (!(relIds instanceof Array)) { - relIds = [relIds]; - } - - const designerRel = this._designer.getModel().getRelationships(); - return designerRel.filter((rel) => relIds.contains(rel.getId())); - }, - - /** */ - moveTopic(topic, position) { - $assert(topic, 'topic cannot be null'); - $assert(position, 'position cannot be null'); + const commandFunc = (topic, value) => { + const result = topic.getPosition(); EventBus.instance.fireEvent(EventBus.events.NodeMoveEvent, { node: topic.getModel(), - position, + position: value, }); - }, - }, -); + return result; + }; -export { StandaloneActionDispatcher, CommandContext }; + const command = new GenericFunctionCommand(commandFunc, topicId, position); + this.execute(command); + } + + /** */ + moveControlPoint(ctrlPoint, point) { + const command = new MoveControlPointCommand(ctrlPoint, point); + this.execute(command); + } + + /** */ + changeFontStyleToTopic(topicsIds) { + const commandFunc = (topic) => { + const result = topic.getFontStyle(); + const style = result === 'italic' ? 'normal' : 'italic'; + topic.setFontStyle(style, true); + return result; + }; + const command = new GenericFunctionCommand(commandFunc, topicsIds); + this.execute(command); + } + + /** */ + changeTextToTopic(topicsIds, text) { + $assert($defined(topicsIds), 'topicsIds can not be null'); + + const commandFunc = (topic, value) => { + const result = topic.getText(); + topic.setText(value); + return result; + }; + commandFunc.commandType = 'changeTextToTopic'; + + const command = new GenericFunctionCommand(commandFunc, topicsIds, text); + this.execute(command); + } + + /** */ + changeFontFamilyToTopic(topicIds, fontFamily) { + $assert(topicIds, 'topicIds can not be null'); + $assert(fontFamily, 'fontFamily can not be null'); + + const commandFunc = (topic, fontFamily) => { + const result = topic.getFontFamily(); + topic.setFontFamily(fontFamily, true); + + topic._adjustShapes(); + return result; + }; + + const command = new GenericFunctionCommand(commandFunc, topicIds, fontFamily); + this.execute(command); + } + + /** */ + changeFontColorToTopic(topicsIds, color) { + $assert(topicsIds, 'topicIds can not be null'); + $assert(color, 'color can not be null'); + + const commandFunc = (topic, color) => { + const result = topic.getFontColor(); + topic.setFontColor(color, true); + return result; + }; + + const command = new GenericFunctionCommand(commandFunc, topicsIds, color); + command.discardDuplicated = 'fontColorCommandId'; + this.execute(command); + } + + /** */ + changeBackgroundColorToTopic(topicsIds, color) { + $assert(topicsIds, 'topicIds can not be null'); + $assert(color, 'color can not be null'); + + const commandFunc = (topic, color) => { + const result = topic.getBackgroundColor(); + topic.setBackgroundColor(color); + return result; + }; + + const command = new GenericFunctionCommand(commandFunc, topicsIds, color); + command.discardDuplicated = 'backColor'; + this.execute(command); + } + + /** */ + changeBorderColorToTopic(topicsIds, color) { + $assert(topicsIds, 'topicIds can not be null'); + $assert(color, 'topicIds can not be null'); + + const commandFunc = (topic, color) => { + const result = topic.getBorderColor(); + topic.setBorderColor(color); + return result; + }; + + const command = new GenericFunctionCommand(commandFunc, topicsIds, color); + command.discardDuplicated = 'borderColorCommandId'; + this.execute(command); + } + + /** */ + changeFontSizeToTopic(topicsIds, size) { + $assert(topicsIds, 'topicIds can not be null'); + $assert(size, 'size can not be null'); + + const commandFunc = (topic, size) => { + const result = topic.getFontSize(); + topic.setFontSize(size, true); + + topic._adjustShapes(); + return result; + }; + + const command = new GenericFunctionCommand(commandFunc, topicsIds, size); + this.execute(command); + } + + /** */ + changeShapeTypeToTopic(topicsIds, shapeType) { + $assert(topicsIds, 'topicsIds can not be null'); + $assert(shapeType, 'shapeType can not be null'); + + const commandFunc = (topic, shapeType) => { + const result = topic.getShapeType(); + topic.setShapeType(shapeType, true); + return result; + }; + + const command = new GenericFunctionCommand(commandFunc, topicsIds, shapeType); + this.execute(command); + } + + /** */ + changeFontWeightToTopic(topicsIds) { + $assert(topicsIds, 'topicsIds can not be null'); + + const commandFunc = (topic) => { + const result = topic.getFontWeight(); + const weight = result === 'bold' ? 'normal' : 'bold'; + topic.setFontWeight(weight, true); + + topic._adjustShapes(); + return result; + }; + + const command = new GenericFunctionCommand(commandFunc, topicsIds); + this.execute(command); + } + + /** */ + shrinkBranch(topicsIds, collapse) { + $assert(topicsIds, 'topicsIds can not be null'); + + const commandFunc = (topic, isShrink) => { + topic.setChildrenShrunken(isShrink); + return !isShrink; + }; + + const command = new GenericFunctionCommand(commandFunc, topicsIds, collapse); + this.execute(command, false); + } + + /** */ + addFeatureToTopic(topicId, featureType, attributes) { + const command = new AddFeatureToTopicCommand(topicId, featureType, attributes); + this.execute(command); + } + + /** */ + changeFeatureToTopic(topicId, featureId, attributes) { + const command = new ChangeFeatureToTopicCommand(topicId, featureId, attributes); + this.execute(command); + } + + /** */ + removeFeatureFromTopic(topicId, featureId) { + const command = new RemoveFeatureFromTopicCommand(topicId, featureId); + this.execute(command); + } + + /** */ + execute(command) { + this._actionRunner.execute(command); + } +} + + + +export default StandaloneActionDispatcher ; diff --git a/packages/mindplot/src/components/TextEditor.js b/packages/mindplot/src/components/TextEditor.js index 2b54422d..6b792554 100644 --- a/packages/mindplot/src/components/TextEditor.js +++ b/packages/mindplot/src/components/TextEditor.js @@ -12,8 +12,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $defined } from '@wisemapping/core-js'; import web2d from '@wisemapping/web2d'; - import ActionDispatcher from './ActionDispatcher'; // FIXME: Not used! diff --git a/packages/mindplot/src/components/Topic.js b/packages/mindplot/src/components/Topic.js index 252d38d0..5c7b6074 100644 --- a/packages/mindplot/src/components/Topic.js +++ b/packages/mindplot/src/components/Topic.js @@ -15,6 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; +import { _ } from '@libraries/underscore-min'; import web2d from '@wisemapping/web2d'; import NodeGraph from './NodeGraph'; @@ -192,7 +194,7 @@ const Topic = new Class( return model.getImageSize(); }; - result.setPosition = function () {}; + result.setPosition = function () { }; } else if (shapeType === TopicShape.ELLIPSE) { result = new web2d.Rect(0.9, attributes); } else if (shapeType === TopicShape.ROUNDED_RECT) { @@ -213,11 +215,11 @@ const Topic = new Class( return this.size; }; - result.setPosition = function () {}; + result.setPosition = function () { }; - result.setFill = function () {}; + result.setFill = function () { }; - result.setStroke = function () {}; + result.setStroke = function () { }; } else { $assert(false, `Unsupported figure shapeType:${shapeType}`); } @@ -644,7 +646,7 @@ const Topic = new Class( let value = true; if ( (event.metaKey && Browser.Platform.mac) - || (event.ctrlKey && !Browser.Platform.mac) + || (event.ctrlKey && !Browser.Platform.mac) ) { value = !me.isOnFocus(); event.stopPropagation(); @@ -969,8 +971,8 @@ const Topic = new Class( const sourceParent = sourceTopic.getModel().getParent(); relationship.setVisibility( value - && (targetParent == null || !targetParent.areChildrenShrunken()) - && (sourceParent == null || !sourceParent.areChildrenShrunken()), + && (targetParent == null || !targetParent.areChildrenShrunken()) + && (sourceParent == null || !sourceParent.areChildrenShrunken()), ); }); }, diff --git a/packages/mindplot/src/components/TopicEventDispatcher.js b/packages/mindplot/src/components/TopicEventDispatcher.js index a31273b1..1e0a1e72 100644 --- a/packages/mindplot/src/components/TopicEventDispatcher.js +++ b/packages/mindplot/src/components/TopicEventDispatcher.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; import Events from './Events'; import MultilineTextEditor from './MultilineTextEditor'; import { TopicShape } from './model/INodeModel'; diff --git a/packages/mindplot/src/components/TopicFeature.js b/packages/mindplot/src/components/TopicFeature.js index 758173a1..28782a66 100644 --- a/packages/mindplot/src/components/TopicFeature.js +++ b/packages/mindplot/src/components/TopicFeature.js @@ -16,7 +16,7 @@ * limitations under the License. */ -/** */ +import { $assert } from '@wisemapping/core-js'; import IconModel from './model/IconModel'; import ImageIcon from './ImageIcon'; import LinkModel from './model/LinkModel'; diff --git a/packages/mindplot/src/components/TopicStyle.js b/packages/mindplot/src/components/TopicStyle.js index 7db56086..6a87b41b 100644 --- a/packages/mindplot/src/components/TopicStyle.js +++ b/packages/mindplot/src/components/TopicStyle.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; import { TopicShape } from './model/INodeModel'; const TopicStyle = new Class({}); diff --git a/packages/mindplot/src/components/Workspace.js b/packages/mindplot/src/components/Workspace.js index 80d8d672..f73ea34d 100644 --- a/packages/mindplot/src/components/Workspace.js +++ b/packages/mindplot/src/components/Workspace.js @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { $assert } from '@wisemapping/core-js'; +import { $assert, $defined } from '@wisemapping/core-js'; import web2d from '@wisemapping/web2d'; class Workspace { diff --git a/packages/mindplot/src/components/header.js b/packages/mindplot/src/components/header.js deleted file mode 100644 index e7b9b5f7..00000000 --- a/packages/mindplot/src/components/header.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright [2015] [wisemapping] - * - * Licensed under WiseMapping Public License, Version 1.0 (the "License"). - * It is basically the Apache License, Version 2.0 (the "License") plus the - * "powered by wisemapping" text requirement on every single page; - * you may not use this file except in compliance with the License. - * You may obtain a copy of the license at - * - * http://www.wisemapping.org/license - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const mindplot = {}; -mindplot.util = {}; -mindplot.commands = {}; -mindplot.layout = {}; -mindplot.layout.boards = {}; -mindplot.layout.boards.original = {}; -mindplot.widget = {}; -mindplot.model = {}; -mindplot.collaboration = {}; -mindplot.collaboration.framework = {}; -mindplot.persistence = {}; - -mindplot.layout = {}; - -Class.Mutators.Static = function (items) { - this.extend(items); -}; diff --git a/packages/mindplot/src/components/index.js b/packages/mindplot/src/components/index.js deleted file mode 100644 index b6b417ed..00000000 --- a/packages/mindplot/src/components/index.js +++ /dev/null @@ -1,95 +0,0 @@ -import actionDispatcher from './ActionDispatcher'; -import actionIcon from './ActionIcon'; -import centralTopic from './CentralTopic'; -import command from './Command'; -import connectionLine from './ConnectionLine'; -import controlPoint from './ControlPoint'; -import designer from './Designer'; -import designerActionRunner from './DesignerActionRunner'; -import designerKeyboard from './DesignerKeyboard'; -import designerModal from './DesignerModel'; -import designerUndoManager from './DesignerUndoManager'; -import dragConnector from './DragConnector'; -import dragManager from './DragManager'; -import dragPivot from './DragPivot'; -import dragTopic from './DragTopic'; -import editorOptions from './EditorOptions'; -import editorProperties from './EditorProperties'; -import events from './Events'; -import footer from './footer'; -import header from './header'; -import icon from './Icon'; -import iconGroup from './IconGroup'; -import imageIcon from './ImageIcon'; -import keyboard from './Keyboard'; -import linkIcon from './LinkIcon'; -import localSorageManager from './LocalStorageManager'; -import mainTopic from './MainTopic'; -import messages from './Messages'; -import multilineTextEditor from './MultilineTextEditor'; -import nodeGraph from './NodeGraph'; -import noteIcon from './NoteIcon'; -import options from './Options'; -import persistenceManager from './PersistenceManager'; -import relationship from './Relationship'; -import relationshipPivot from './RelationshipPivot'; -import resetPersistenceManager from './RestPersistenceManager'; -import screenManager from './ScreenManager'; -import shrinkConnector from './ShrinkConnector'; -import standaloneActionDispatcher from './StandaloneActionDispatcher'; -import textEditor from './TextEditor'; -import textEditorFactory from './TextEditorFactory'; -import topic from './Topic'; -import topicEventDispatcher from './TopicEventDispatcher'; -import topicFeature from './TopicFeature'; -import topicStyle from './TopicStyle'; -import workspace from './Workspace'; - -export default { - ActionDispatcher: actionDispatcher, - ActionIcon: actionIcon, - CentralTopic: centralTopic, - Command: command, - ConnectionLine: connectionLine, - ControlPoint: controlPoint, - Designer: designer, - DesignerActionRunner: designerActionRunner, - DesignerKeyboard: designerKeyboard, - DesignerModel: designerModal, - DesignerUndoManager: designerUndoManager, - DragConnector: dragConnector, - DragManager: dragManager, - DragPivot: dragPivot, - DragTopic: dragTopic, - EditorOptions: editorOptions, - EditorProperties: editorProperties, - Events: events, - Footer: footer, - Header: header, - Icon: icon, - IconGroup: iconGroup, - ImageIcon: imageIcon, - Keyboard: keyboard, - LinkIcon: linkIcon, - LocalStorageManager: localSorageManager, - MainTopic: mainTopic, - Messages: messages, - MultilineTextEditor: multilineTextEditor, - NodeGraph: nodeGraph, - NoteIcon: noteIcon, - Options: options, - PersistenceManager: persistenceManager, - Relationship: relationship, - RelationshipPivot: relationshipPivot, - RestPersistenceManager: resetPersistenceManager, - ScreenManager: screenManager, - ShrinkConnector: shrinkConnector, - StandaloneActionDispatcher: standaloneActionDispatcher, - TextEditor: textEditor, - TextEditorFactory: textEditorFactory, - Topic: topic, - TopicEventDispatcher: topicEventDispatcher, - TopicFeature: topicFeature, - TopicStyle: topicStyle, - Workspace: workspace, -}; diff --git a/packages/mindplot/src/components/layout/BalancedSorter.js b/packages/mindplot/src/components/layout/BalancedSorter.js index 867915bd..3e7608b5 100644 --- a/packages/mindplot/src/components/layout/BalancedSorter.js +++ b/packages/mindplot/src/components/layout/BalancedSorter.js @@ -15,6 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import _ from '@libraries/underscore-min'; +import { $assert, $defined } from '@wisemapping/core-js'; import AbstractBasicSorter from './AbstractBasicSorter'; const BalancedSorter = new Class( diff --git a/packages/mindplot/src/components/layout/ChangeEvent.js b/packages/mindplot/src/components/layout/ChangeEvent.js index 5f55ee10..ebb0fb61 100644 --- a/packages/mindplot/src/components/layout/ChangeEvent.js +++ b/packages/mindplot/src/components/layout/ChangeEvent.js @@ -15,34 +15,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; -const ChangeEvent = new Class(/** @lends ChangeEvent */{ - /** - * @constructs - * @param {} id - * @throws will throw an error if the given id is not/cannot be converted to a numerical value - */ - initialize(id) { - $assert(!isNaN(id), 'id can not be null'); +class ChangeEvent { + constructor(id) { + $assert(!Number.isNaN(id), 'id can not be null'); this._id = id; this._position = null; this._order = null; - }, + } /** @return id */ getId() { return this._id; - }, + } /** @return order */ getOrder() { return this._order; - }, + } /** @return position */ getPosition() { return this._position; - }, + } /** * @param {} value the order to set @@ -50,21 +46,21 @@ const ChangeEvent = new Class(/** @lends ChangeEvent */{ * value */ setOrder(value) { - $assert(!isNaN(value), 'value can not be null'); + $assert(!Number.isNaN(value), 'value can not be null'); this._order = value; - }, + } /** @param {} value * @throws will throw an error if the value is null or undefined */ setPosition(value) { $assert(value, 'value can not be null'); this._position = value; - }, + } /** @return {String} order and position */ toString() { return `[order:${this.getOrder()}, position: {${this.getPosition().x},${this.getPosition().y}}]`; - }, -}); + } +} export default ChangeEvent; diff --git a/packages/mindplot/src/components/layout/EventBus.js b/packages/mindplot/src/components/layout/EventBus.js index 17a890b9..8837abbf 100644 --- a/packages/mindplot/src/components/layout/EventBus.js +++ b/packages/mindplot/src/components/layout/EventBus.js @@ -17,15 +17,8 @@ */ import Events from '../Events'; -const EventBus = new Class(/** @lends EventBus */{ - Implements: Events, - /** - * @constructs - * @implements mindplot.Events - */ - initialize() { - }, -}); +class EventBus extends Events { +} /** * Enum for events diff --git a/packages/mindplot/src/components/layout/GridSorter.js b/packages/mindplot/src/components/layout/GridSorter.js index 6c9e962e..657de416 100644 --- a/packages/mindplot/src/components/layout/GridSorter.js +++ b/packages/mindplot/src/components/layout/GridSorter.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert } from '@wisemapping/core-js'; import AbstractBasicSorter from './AbstractBasicSorter'; /** diff --git a/packages/mindplot/src/components/layout/LayoutManager.js b/packages/mindplot/src/components/layout/LayoutManager.js index 240d7566..37f4e519 100644 --- a/packages/mindplot/src/components/layout/LayoutManager.js +++ b/packages/mindplot/src/components/layout/LayoutManager.js @@ -15,274 +15,269 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import _ from '@libraries/underscore-min'; +import $ from '@libraries/jquery-2.1.0'; +import { $assert, $defined } from '@wisemapping/core-js'; import Events from '../Events'; import RootedTreeSet from './RootedTreeSet'; import OriginalLayout from './OriginalLayout'; import ChangeEvent from './ChangeEvent'; -const LayoutManager = new Class( - /** @lends LayoutManager */ { - Extends: Events, - /** - * @constructs - * @extends mindplot.Events - * @param {} rootNodeId - * @param {} rootSize - * @throws will throw an error if the root node id is null or undefined - * @throws will throw an error if the root size is null - */ - initialize(rootNodeId, rootSize) { - $assert($defined(rootNodeId), 'rootNodeId can not be null'); - $assert(rootSize, 'rootSize can not be null'); - var position = position || { x: 0, y: 0 }; +class LayoutManager extends Events { + constructor(rootNodeId, rootSize) { + super(); + $assert($defined(rootNodeId), 'rootNodeId can not be null'); + $assert(rootSize, 'rootSize can not be null'); + var position = position || { x: 0, y: 0 }; - this._treeSet = new RootedTreeSet(); - this._layout = new OriginalLayout(this._treeSet); + this._treeSet = new RootedTreeSet(); + this._layout = new OriginalLayout(this._treeSet); - const rootNode = this._layout.createNode(rootNodeId, rootSize, position, 'root'); - this._treeSet.setRoot(rootNode); - this._events = []; - }, + const rootNode = this._layout.createNode(rootNodeId, rootSize, position, 'root'); + this._treeSet.setRoot(rootNode); + this._events = []; + } - /** - * @param id - * @param size - * @throws will throw an error if id is null or undefined - */ - updateNodeSize(id, size) { - $assert($defined(id), 'id can not be null'); + /** + * @param id + * @param size + * @throws will throw an error if id is null or undefined + */ + updateNodeSize(id, size) { + $assert($defined(id), 'id can not be null'); - const node = this._treeSet.find(id); - node.setSize(size); - }, + const node = this._treeSet.find(id); + node.setSize(size); + } - /** - * @param id - * @param value - * @throws will throw an error if id is null or undefined - * @throws will throw an error if value is null or undefined - * @return this - */ - updateShrinkState(id, value) { - $assert($defined(id), 'id can not be null'); - $assert($defined(value), 'value can not be null'); + /** + * @param id + * @param value + * @throws will throw an error if id is null or undefined + * @throws will throw an error if value is null or undefined + * @return this + */ + updateShrinkState(id, value) { + $assert($defined(id), 'id can not be null'); + $assert($defined(value), 'value can not be null'); - const node = this._treeSet.find(id); - node.setShrunken(value); + const node = this._treeSet.find(id); + node.setShrunken(value); - return this; - }, + return this; + } - /** - * @param id - * @return {@link RootedTreeSet}.find(id) - */ - find(id) { - return this._treeSet.find(id); - }, + /** + * @param id + * @return {@link RootedTreeSet}.find(id) + */ + find(id) { + return this._treeSet.find(id); + } - /** - * @param id - * @param position - * @throws will throw an error if id is null or undefined - * @throws will throw an error if position is null or undefined - * @throws will throw an error if the position's x property is null or undefined - * @throws will throw an error if the position's y property is null or undefined - */ - moveNode(id, position) { - $assert($defined(id), 'id cannot be null'); - $assert($defined(position), 'position cannot be null'); - $assert($defined(position.x), 'x can not be null'); - $assert($defined(position.y), 'y can not be null'); + /** + * @param id + * @param position + * @throws will throw an error if id is null or undefined + * @throws will throw an error if position is null or undefined + * @throws will throw an error if the position's x property is null or undefined + * @throws will throw an error if the position's y property is null or undefined + */ + moveNode(id, position) { + $assert($defined(id), 'id cannot be null'); + $assert($defined(position), 'position cannot be null'); + $assert($defined(position.x), 'x can not be null'); + $assert($defined(position.y), 'y can not be null'); - const node = this._treeSet.find(id); - // @Todo: this should not be here. This is broking the isolated node support... - // node.setFree(true); - // node.setFreeDisplacement({x:position.x - node.getPosition().x, y:position.y - node.getPosition().y}); - node.setPosition(position); - }, + const node = this._treeSet.find(id); + // @Todo: this should not be here. This is broking the isolated node support... + // node.setFree(true); + // node.setFreeDisplacement({x:position.x - node.getPosition().x, y:position.y - node.getPosition().y}); + node.setPosition(position); + } - /** - * @param parentId - * @param childId - * @param order - * @throws will throw an error if parentId is null or undefined - * @throws will throw an error if childId is null or undefined - * @throws will throw an error if order is null or undefined - * @return this - */ - connectNode(parentId, childId, order) { - $assert($defined(parentId), 'parentId cannot be null'); - $assert($defined(childId), 'childId cannot be null'); - $assert($defined(order), 'order cannot be null'); + /** + * @param parentId + * @param childId + * @param order + * @throws will throw an error if parentId is null or undefined + * @throws will throw an error if childId is null or undefined + * @throws will throw an error if order is null or undefined + * @return this + */ + connectNode(parentId, childId, order) { + $assert($defined(parentId), 'parentId cannot be null'); + $assert($defined(childId), 'childId cannot be null'); + $assert($defined(order), 'order cannot be null'); - this._layout.connectNode(parentId, childId, order); + this._layout.connectNode(parentId, childId, order); - return this; - }, + return this; + } - /** - * @param id - * @throws will throw an error if id is null or undefined - * @return this - */ - disconnectNode(id) { - $assert($defined(id), 'id can not be null'); - this._layout.disconnectNode(id); + /** + * @param id + * @throws will throw an error if id is null or undefined + * @return this + */ + disconnectNode(id) { + $assert($defined(id), 'id can not be null'); + this._layout.disconnectNode(id); - return this; - }, + return this; + } - /** - * @param id - * @param size - * @param position - * @throws will throw an error if id is null or undefined - * @return this - */ - addNode(id, size, position) { - $assert($defined(id), 'id can not be null'); - const result = this._layout.createNode(id, size, position, 'topic'); - this._treeSet.add(result); + /** + * @param id + * @param size + * @param position + * @throws will throw an error if id is null or undefined + * @return this + */ + addNode(id, size, position) { + $assert($defined(id), 'id can not be null'); + const result = this._layout.createNode(id, size, position, 'topic'); + this._treeSet.add(result); - return this; - }, + return this; + } - /** - * removes a node and its connection to parent if existing - * @param id - * @throws will throw an error if id is null or undefined - * @return this - */ - removeNode(id) { - $assert($defined(id), 'id can not be null'); - const node = this._treeSet.find(id); + /** + * removes a node and its connection to parent if existing + * @param id + * @throws will throw an error if id is null or undefined + * @return this + */ + removeNode(id) { + $assert($defined(id), 'id can not be null'); + const node = this._treeSet.find(id); - // Is It connected ? - if (this._treeSet.getParent(node)) { - this.disconnectNode(id); - } + // Is It connected ? + if (this._treeSet.getParent(node)) { + this.disconnectNode(id); + } - // Remove the all the branch ... - this._treeSet.remove(id); + // Remove the all the branch ... + this._treeSet.remove(id); - return this; - }, + return this; + } - /** - * @param {Number} parentId - * @param {Number=} nodeId - * @param {String=} position the position to use as mindplot.layout.Node.properties position - * property as '(x,y)' - * @param {Boolean=} free true specifies free node positioning - * @throws will throw an error if parentId is null or undefined - */ - predict(parentId, nodeId, position, free) { - $assert($defined(parentId), 'parentId can not be null'); + /** + * @param {Number} parentId + * @param {Number=} nodeId + * @param {String=} position the position to use as mindplot.layout.Node.properties position + * property as '(x,y)' + * @param {Boolean=} free true specifies free node positioning + * @throws will throw an error if parentId is null or undefined + */ + predict(parentId, nodeId, position, free) { + $assert($defined(parentId), 'parentId can not be null'); - const parent = this._treeSet.find(parentId); - const node = nodeId ? this._treeSet.find(nodeId) : null; - const sorter = parent.getSorter(); + const parent = this._treeSet.find(parentId); + const node = nodeId ? this._treeSet.find(nodeId) : null; + const sorter = parent.getSorter(); - const result = sorter.predict(this._treeSet, parent, node, position, free); - return { order: result[0], position: result[1] }; - }, + const result = sorter.predict(this._treeSet, parent, node, position, free); + return { order: result[0], position: result[1] }; + } - /** - * logs dump to console - */ - dump() { - console.log(this._treeSet.dump()); - }, + /** + * logs dump to console + */ + dump() { + console.log(this._treeSet.dump()); + } - /** - * @param containerId - * @param {width:Number, height:Number} size - * @throws will throw an error if containerId is null or undefined - * @return canvas - */ - plot(containerId, size) { - // this method is only used from tests that include Raphael - if (!global.Raphael) { - console.warn('Raphael.js not found, exiting plot()'); - return null; - } - $assert(containerId, 'containerId cannot be null'); - size = size || { width: 200, height: 200 }; - const squaresize = 10; - const canvas = global.Raphael(containerId, size.width, size.height); - canvas.drawGrid( - 0, - 0, - size.width, - size.height, - size.width / squaresize, - size.height / squaresize, - ); - this._treeSet.plot(canvas); + /** + * @param containerId + * @param {width:Number, height:Number} size + * @throws will throw an error if containerId is null or undefined + * @return canvas + */ + plot(containerId, size) { + // this method is only used from tests that include Raphael + if (!global.Raphael) { + console.warn('Raphael.js not found, exiting plot()'); + return null; + } + $assert(containerId, 'containerId cannot be null'); + size = size || { width: 200, height: 200 }; + const squaresize = 10; + const canvas = global.Raphael(containerId, size.width, size.height); + canvas.drawGrid( + 0, + 0, + size.width, + size.height, + size.width / squaresize, + size.height / squaresize, + ); + this._treeSet.plot(canvas); - return canvas; - }, + return canvas; + } - /** - * initializes the layout to be updated - * @param fireEvents - * @return this - */ - layout(fireEvents) { - // File repositioning ... - this._layout.layout(); + /** + * initializes the layout to be updated + * @param fireEvents + * @return this + */ + layout(fireEvents) { + // File repositioning ... + this._layout.layout(); - // Collect changes ... - this._collectChanges(); + // Collect changes ... + this._collectChanges(); - if ($(fireEvents).length > 0 || fireEvents) { - this._flushEvents(); - } + if ($(fireEvents).length > 0 || fireEvents) { + this._flushEvents(); + } - return this; - }, + return this; + } - _flushEvents() { - _.each( - this._events, - function (event) { - this.fireEvent('change', event); - }, - this, - ); - this._events = []; - }, + _flushEvents() { + _.each( + this._events, + function (event) { + this.fireEvent('change', event); + }, + this, + ); + this._events = []; + } - _collectChanges(nodes) { - if (!nodes) nodes = this._treeSet.getTreeRoots(); + _collectChanges(nodes) { + if (!nodes) { + nodes = this._treeSet.getTreeRoots(); + } - _.each( - nodes, - function (node) { - if (node.hasOrderChanged() || node.hasPositionChanged()) { - // Find or create a event ... - const id = node.getId(); - let event = this._events.some((event) => event.id == id); - if (!event) { - event = new ChangeEvent(id); - } - - // Update nodes ... - event.setOrder(node.getOrder()); - event.setPosition(node.getPosition()); - - node.resetPositionState(); - node.resetOrderState(); - node.resetFreeState(); - this._events.push(event); + _.each( + nodes, + function (node) { + if (node.hasOrderChanged() || node.hasPositionChanged()) { + // Find or create a event ... + const id = node.getId(); + let event = this._events.some((event) => event.id == id); + if (!event) { + event = new ChangeEvent(id); } - this._collectChanges(this._treeSet.getChildren(node)); - }, - this, - ); - }, - }, -); + + // Update nodes ... + event.setOrder(node.getOrder()); + event.setPosition(node.getPosition()); + + node.resetPositionState(); + node.resetOrderState(); + node.resetFreeState(); + this._events.push(event); + } + this._collectChanges(this._treeSet.getChildren(node)); + }, + this, + ); + } +} export default LayoutManager; diff --git a/packages/mindplot/src/components/layout/Node.js b/packages/mindplot/src/components/layout/Node.js index ae40e0bc..3fba43cb 100644 --- a/packages/mindplot/src/components/layout/Node.js +++ b/packages/mindplot/src/components/layout/Node.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; const Node = new Class( /** @lends Node */ { @@ -30,7 +31,7 @@ const Node = new Class( * @throws will throw an error if sorter is null or undefined */ initialize(id, size, position, sorter) { - $assert(typeof id === 'number' && isFinite(id), 'id can not be null'); + $assert(typeof id === 'number' && Number.isFinite(id), 'id can not be null'); $assert(size, 'size can not be null'); $assert(position, 'position can not be null'); $assert(sorter, 'sorter can not be null'); @@ -82,7 +83,7 @@ const Node = new Class( /** */ setOrder(order) { $assert( - typeof order === 'number' && isFinite(order), + typeof order === 'number' && Number.isFinite(order), `Order can not be null. Value:${order}`, ); this._setProperty('order', order); @@ -199,7 +200,7 @@ const Node = new Class( } // Only update if the property has changed ... - if (JSON.stringify(prop.value) != JSON.stringify(value)) { + if (JSON.stringify(prop.value) !== JSON.stringify(value)) { prop.oldValue = prop.value; prop.value = value; prop.hasChanged = true; diff --git a/packages/mindplot/src/components/layout/OriginalLayout.js b/packages/mindplot/src/components/layout/OriginalLayout.js index ee62054d..2bdb1e17 100644 --- a/packages/mindplot/src/components/layout/OriginalLayout.js +++ b/packages/mindplot/src/components/layout/OriginalLayout.js @@ -15,265 +15,261 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import _ from '@libraries/underscore-min'; +import { $assert, $defined } from '@wisemapping/core-js'; import Node from './Node'; import SymmetricSorter from './SymmetricSorter'; import BalancedSorter from './BalancedSorter'; -const OriginalLayout = new Class( - /** @lends OriginalLayout */ { - /** - * @constructs - * @param treeSet - */ - initialize(treeSet) { - this._treeSet = treeSet; - }, - - /** */ - createNode(id, size, position, type) { - $assert($defined(id), 'id can not be null'); - $assert(size, 'size can not be null'); - $assert(position, 'position can not be null'); - $assert(type, 'type can not be null'); - - const strategy = type === 'root' ? OriginalLayout.BALANCED_SORTER : OriginalLayout.SYMMETRIC_SORTER; - return new Node(id, size, position, strategy); - }, - - /** */ - connectNode(parentId, childId, order) { - const parent = this._treeSet.find(parentId); - const child = this._treeSet.find(childId); - - // Insert the new node ... - const sorter = parent.getSorter(); - sorter.insert(this._treeSet, parent, child, order); - - // Connect the new node ... - this._treeSet.connect(parentId, childId); - - // Fire a basic validation ... - sorter.verify(this._treeSet, parent); - }, - - /** */ - disconnectNode(nodeId) { - const node = this._treeSet.find(nodeId); - const parent = this._treeSet.getParent(node); - $assert(parent, 'Node already disconnected'); - - // Make it fixed - node.setFree(false); - node.resetFreeDisplacement(); - - // Remove from children list. - const sorter = parent.getSorter(); - sorter.detach(this._treeSet, node); - - // Disconnect the new node ... - this._treeSet.disconnect(nodeId); - - // Fire a basic validation ... - parent.getSorter().verify(this._treeSet, parent); - }, - - /** */ - layout() { - const roots = this._treeSet.getTreeRoots(); - _.each( - roots, - function (node) { - // Calculate all node heights ... - const sorter = node.getSorter(); - - const heightById = sorter.computeChildrenIdByHeights(this._treeSet, node); - - this._layoutChildren(node, heightById); - - this._fixOverlapping(node, heightById); - }, - this, - ); - }, - - _layoutChildren(node, heightById) { - const nodeId = node.getId(); - const children = this._treeSet.getChildren(node); - const parent = this._treeSet.getParent(node); - const childrenOrderMoved = children.some((child) => child.hasOrderChanged()); - const childrenSizeChanged = children.some((child) => child.hasSizeChanged()); - - // If ether any of the nodes has been changed of position or the height of the children is not - // the same, children nodes must be repositioned .... - const newBranchHeight = heightById[nodeId]; - - const parentHeightChanged = $defined(parent) ? parent._heightChanged : false; - const heightChanged = node._branchHeight != newBranchHeight; - node._heightChanged = heightChanged || parentHeightChanged; - - if (childrenOrderMoved || childrenSizeChanged || heightChanged || parentHeightChanged) { - const sorter = node.getSorter(); - const offsetById = sorter.computeOffsets(this._treeSet, node); - const parentPosition = node.getPosition(); - const me = this; - _.each(children, (child) => { - const offset = offsetById[child.getId()]; - - const childFreeDisplacement = child.getFreeDisplacement(); - const direction = node.getSorter().getChildDirection(me._treeSet, child); - - if ( - (direction > 0 && childFreeDisplacement.x < 0) - || (direction < 0 && childFreeDisplacement.x > 0) - ) { - child.resetFreeDisplacement(); - child.setFreeDisplacement({ - x: -childFreeDisplacement.x, - y: childFreeDisplacement.y, - }); - } - - offset.x += child.getFreeDisplacement().x; - offset.y += child.getFreeDisplacement().y; - - const parentX = parentPosition.x; - const parentY = parentPosition.y; - - const newPos = { - x: parentX + offset.x, - y: parentY + offset.y + me._calculateAlignOffset(node, child, heightById), - }; - me._treeSet.updateBranchPosition(child, newPos); - }); - - node._branchHeight = newBranchHeight; - } - - // Continue reordering the children nodes ... - _.each( - children, - function (child) { - this._layoutChildren(child, heightById); - }, - this, - ); - }, - - _calculateAlignOffset(node, child, heightById) { - if (child.isFree()) { - return 0; - } - - let offset = 0; - - const nodeHeight = node.getSize().height; - const childHeight = child.getSize().height; - - if ( - this._treeSet.isStartOfSubBranch(child) - && this._branchIsTaller(child, heightById) - ) { - if (this._treeSet.hasSinglePathToSingleLeaf(child)) { - offset = heightById[child.getId()] / 2 - - (childHeight + child.getSorter()._getVerticalPadding() * 2) / 2; - } else { - offset = this._treeSet.isLeaf(child) ? 0 : -(childHeight - nodeHeight) / 2; - } - } else if (nodeHeight > childHeight) { - if (this._treeSet.getSiblings(child).length > 0) { - offset = 0; - } else { - offset = nodeHeight / 2 - childHeight / 2; - } - } else if (childHeight > nodeHeight) { - if (this._treeSet.getSiblings(child).length > 0) { - offset = 0; - } else { - offset = -(childHeight / 2 - nodeHeight / 2); - } - } - - return offset; - }, - - _branchIsTaller(node, heightById) { - return ( - heightById[node.getId()] - > node.getSize().height + node.getSorter()._getVerticalPadding() * 2 - ); - }, - - _fixOverlapping(node, heightById) { - const children = this._treeSet.getChildren(node); - - if (node.isFree()) { - this._shiftBranches(node, heightById); - } - - _.each( - children, - function (child) { - this._fixOverlapping(child, heightById); - }, - this, - ); - }, - - _shiftBranches(node, heightById) { - const shiftedBranches = [node]; - - const siblingsToShift = this._treeSet.getSiblingsInVerticalDirection( - node, - node.getFreeDisplacement().y, - ); - let last = node; - _.each( - siblingsToShift, - function (sibling) { - const overlappingOccurs = shiftedBranches.some(function (shiftedBranch) { - return this._branchesOverlap(shiftedBranch, sibling, heightById); - }, this); - - if (!sibling.isFree() || overlappingOccurs) { - const sAmount = node.getFreeDisplacement().y; - this._treeSet.shiftBranchPosition(sibling, 0, sAmount); - shiftedBranches.push(sibling); - } - }, - this, - ); - - const branchesToShift = this._treeSet - .getBranchesInVerticalDirection(node, node.getFreeDisplacement().y) - .filter((branch) => !shiftedBranches.contains(branch)); - - _.each( - branchesToShift, - function (branch) { - const bAmount = node.getFreeDisplacement().y; - this._treeSet.shiftBranchPosition(branch, 0, bAmount); - shiftedBranches.push(branch); - last = branch; - }, - this, - ); - }, - - _branchesOverlap(branchA, branchB, heightById) { - // a branch doesn't really overlap with itself - if (branchA == branchB) { - return false; - } - - const topA = branchA.getPosition().y - heightById[branchA.getId()] / 2; - const bottomA = branchA.getPosition().y + heightById[branchA.getId()] / 2; - const topB = branchB.getPosition().y - heightById[branchB.getId()] / 2; - const bottomB = branchB.getPosition().y + heightById[branchB.getId()] / 2; - - return !(topA >= bottomB || bottomA <= topB); - }, +const OriginalLayout = new Class({ + initialize(treeSet) { + this._treeSet = treeSet; }, + + /** */ + createNode(id, size, position, type) { + $assert($defined(id), 'id can not be null'); + $assert(size, 'size can not be null'); + $assert(position, 'position can not be null'); + $assert(type, 'type can not be null'); + + const strategy = type === 'root' ? OriginalLayout.BALANCED_SORTER : OriginalLayout.SYMMETRIC_SORTER; + return new Node(id, size, position, strategy); + }, + + /** */ + connectNode(parentId, childId, order) { + const parent = this._treeSet.find(parentId); + const child = this._treeSet.find(childId); + + // Insert the new node ... + const sorter = parent.getSorter(); + sorter.insert(this._treeSet, parent, child, order); + + // Connect the new node ... + this._treeSet.connect(parentId, childId); + + // Fire a basic validation ... + sorter.verify(this._treeSet, parent); + }, + + /** */ + disconnectNode(nodeId) { + const node = this._treeSet.find(nodeId); + const parent = this._treeSet.getParent(node); + $assert(parent, 'Node already disconnected'); + + // Make it fixed + node.setFree(false); + node.resetFreeDisplacement(); + + // Remove from children list. + const sorter = parent.getSorter(); + sorter.detach(this._treeSet, node); + + // Disconnect the new node ... + this._treeSet.disconnect(nodeId); + + // Fire a basic validation ... + parent.getSorter().verify(this._treeSet, parent); + }, + + /** */ + layout() { + const roots = this._treeSet.getTreeRoots(); + _.each( + roots, + function (node) { + // Calculate all node heights ... + const sorter = node.getSorter(); + + const heightById = sorter.computeChildrenIdByHeights(this._treeSet, node); + + this._layoutChildren(node, heightById); + + this._fixOverlapping(node, heightById); + }, + this, + ); + }, + + _layoutChildren(node, heightById) { + const nodeId = node.getId(); + const children = this._treeSet.getChildren(node); + const parent = this._treeSet.getParent(node); + const childrenOrderMoved = children.some((child) => child.hasOrderChanged()); + const childrenSizeChanged = children.some((child) => child.hasSizeChanged()); + + // If ether any of the nodes has been changed of position or the height of the children is not + // the same, children nodes must be repositioned .... + const newBranchHeight = heightById[nodeId]; + + const parentHeightChanged = $defined(parent) ? parent._heightChanged : false; + const heightChanged = node._branchHeight !== newBranchHeight; + node._heightChanged = heightChanged || parentHeightChanged; + + if (childrenOrderMoved || childrenSizeChanged || heightChanged || parentHeightChanged) { + const sorter = node.getSorter(); + const offsetById = sorter.computeOffsets(this._treeSet, node); + const parentPosition = node.getPosition(); + const me = this; + _.each(children, (child) => { + const offset = offsetById[child.getId()]; + + const childFreeDisplacement = child.getFreeDisplacement(); + const direction = node.getSorter().getChildDirection(me._treeSet, child); + + if ( + (direction > 0 && childFreeDisplacement.x < 0) + || (direction < 0 && childFreeDisplacement.x > 0) + ) { + child.resetFreeDisplacement(); + child.setFreeDisplacement({ + x: -childFreeDisplacement.x, + y: childFreeDisplacement.y, + }); + } + + offset.x += child.getFreeDisplacement().x; + offset.y += child.getFreeDisplacement().y; + + const parentX = parentPosition.x; + const parentY = parentPosition.y; + + const newPos = { + x: parentX + offset.x, + y: parentY + offset.y + me._calculateAlignOffset(node, child, heightById), + }; + me._treeSet.updateBranchPosition(child, newPos); + }); + + node._branchHeight = newBranchHeight; + } + + // Continue reordering the children nodes ... + _.each( + children, + function (child) { + this._layoutChildren(child, heightById); + }, + this, + ); + }, + + _calculateAlignOffset(node, child, heightById) { + if (child.isFree()) { + return 0; + } + + let offset = 0; + + const nodeHeight = node.getSize().height; + const childHeight = child.getSize().height; + + if ( + this._treeSet.isStartOfSubBranch(child) + && this._branchIsTaller(child, heightById) + ) { + if (this._treeSet.hasSinglePathToSingleLeaf(child)) { + offset = heightById[child.getId()] / 2 + - (childHeight + child.getSorter()._getVerticalPadding() * 2) / 2; + } else { + offset = this._treeSet.isLeaf(child) ? 0 : -(childHeight - nodeHeight) / 2; + } + } else if (nodeHeight > childHeight) { + if (this._treeSet.getSiblings(child).length > 0) { + offset = 0; + } else { + offset = nodeHeight / 2 - childHeight / 2; + } + } else if (childHeight > nodeHeight) { + if (this._treeSet.getSiblings(child).length > 0) { + offset = 0; + } else { + offset = -(childHeight / 2 - nodeHeight / 2); + } + } + + return offset; + }, + + _branchIsTaller(node, heightById) { + return ( + heightById[node.getId()] + > node.getSize().height + node.getSorter()._getVerticalPadding() * 2 + ); + }, + + _fixOverlapping(node, heightById) { + const children = this._treeSet.getChildren(node); + + if (node.isFree()) { + this._shiftBranches(node, heightById); + } + + _.each( + children, + function (child) { + this._fixOverlapping(child, heightById); + }, + this, + ); + }, + + _shiftBranches(node, heightById) { + const shiftedBranches = [node]; + + const siblingsToShift = this._treeSet.getSiblingsInVerticalDirection( + node, + node.getFreeDisplacement().y, + ); + let last = node; + _.each( + siblingsToShift, + function (sibling) { + const overlappingOccurs = shiftedBranches.some(function (shiftedBranch) { + return this._branchesOverlap(shiftedBranch, sibling, heightById); + }, this); + + if (!sibling.isFree() || overlappingOccurs) { + const sAmount = node.getFreeDisplacement().y; + this._treeSet.shiftBranchPosition(sibling, 0, sAmount); + shiftedBranches.push(sibling); + } + }, + this, + ); + + const branchesToShift = this._treeSet + .getBranchesInVerticalDirection(node, node.getFreeDisplacement().y) + .filter((branch) => !shiftedBranches.contains(branch)); + + _.each( + branchesToShift, + function (branch) { + const bAmount = node.getFreeDisplacement().y; + this._treeSet.shiftBranchPosition(branch, 0, bAmount); + shiftedBranches.push(branch); + last = branch; + }, + this, + ); + }, + + _branchesOverlap(branchA, branchB, heightById) { + // a branch doesn't really overlap with itself + if (branchA === branchB) { + return false; + } + + const topA = branchA.getPosition().y - heightById[branchA.getId()] / 2; + const bottomA = branchA.getPosition().y + heightById[branchA.getId()] / 2; + const topB = branchB.getPosition().y - heightById[branchB.getId()] / 2; + const bottomB = branchB.getPosition().y + heightById[branchB.getId()] / 2; + + return !(topA >= bottomB || bottomA <= topB); + }, +}, ); /** diff --git a/packages/mindplot/src/components/layout/RootedTreeSet.js b/packages/mindplot/src/components/layout/RootedTreeSet.js index b144e848..b2d5e86f 100644 --- a/packages/mindplot/src/components/layout/RootedTreeSet.js +++ b/packages/mindplot/src/components/layout/RootedTreeSet.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; const RootedTreeSet = new Class( /** @lends RootedTreeSet */ { diff --git a/packages/mindplot/src/components/layout/SymmetricSorter.js b/packages/mindplot/src/components/layout/SymmetricSorter.js index 590b2c2d..1054ba8c 100644 --- a/packages/mindplot/src/components/layout/SymmetricSorter.js +++ b/packages/mindplot/src/components/layout/SymmetricSorter.js @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { $assert, $defined } from '@wisemapping/core-js'; import AbstractBasicSorter from './AbstractBasicSorter'; const SymmetricSorter = new Class( diff --git a/packages/mindplot/src/components/layout/index.js b/packages/mindplot/src/components/layout/index.js deleted file mode 100644 index 3071d189..00000000 --- a/packages/mindplot/src/components/layout/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import abstractBasicSorter from './AbstractBasicSorter'; -import balancedSorter from './BalancedSorter'; -import changeEvent from './ChangeEvent'; -import childrenSorterStrategy from './ChildrenSorterStrategy'; -import eventBus from './EventBus'; -import eventBusDispatcher from './EventBusDispatcher'; -import gridSorter from './GridSorter'; -import layoutManager from './LayoutManager'; -import node from './Node'; -import originalLayout from './OriginalLayout'; -import rootedTreeSet from './RootedTreeSet'; -import symmetricSorter from './SymmetricSorter'; - -export default { - AbstractBasicSorter: abstractBasicSorter, - BalancedSorter: balancedSorter, - ChangeEvent: changeEvent, - ChildrenSorterStrategy: childrenSorterStrategy, - EventBus: eventBus, - EventBusDispatcher: eventBusDispatcher, - GridSorter: gridSorter, - LayoutManager: layoutManager, - Node: node, - OriginalLayout: originalLayout, - RootedTreeSet: rootedTreeSet, - SymmetricSorter: symmetricSorter, -}; diff --git a/packages/mindplot/src/components/libraries/hotkeys/jquery.hotkeys.js b/packages/mindplot/src/components/libraries/hotkeys/jquery.hotkeys.js deleted file mode 100644 index 72fa7f1c..00000000 --- a/packages/mindplot/src/components/libraries/hotkeys/jquery.hotkeys.js +++ /dev/null @@ -1,172 +0,0 @@ -/* - * jQuery Hotkeys Plugin - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * - * Based upon the plugin by Tzury Bar Yochay: - * http://github.com/tzuryby/hotkeys - * - * Original idea by: - * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ -*/ - -/* - * One small change is: now keys are passed by object { keys: '...' } - * Might be useful, when you want to pass some other data to your handler - */ - -(function (jQuery) { - jQuery.hotkeys = { - version: '0.8', - - specialKeys: { - 8: 'backspace', - 9: 'tab', - 10: 'return', - 13: 'enter', - 16: 'shift', - 17: 'ctrl', - 18: 'alt', - 19: 'pause', - 20: 'capslock', - 27: 'esc', - 32: 'space', - 33: 'pageup', - 34: 'pagedown', - 35: 'end', - 36: 'home', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 45: 'insert', - 46: 'del', - 96: '0', - 97: '1', - 98: '2', - 99: '3', - 100: '4', - 101: '5', - 102: '6', - 103: '7', - 104: '8', - 105: '9', - 106: '*', - 107: '+', - 109: '-', - 110: '.', - 111: '/', - 112: 'f1', - 113: 'f2', - 114: 'f3', - 115: 'f4', - 116: 'f5', - 117: 'f6', - 118: 'f7', - 119: 'f8', - 120: 'f9', - 121: 'f10', - 122: 'f11', - 123: 'f12', - 144: 'numlock', - 145: 'scroll', - 186: ';', - 191: '/', - 220: '\\', - 222: "'", - 224: 'meta', - }, - - shiftNums: { - '`': '~', - 1: '!', - 2: '@', - 3: '#', - 4: '$', - 5: '%', - 6: '^', - 7: '&', - 8: '*', - 9: '(', - 0: ')', - '-': '_', - '=': '+', - ';': ': ', - "'": '"', - ',': '<', - '.': '>', - '/': '?', - '\\': '|', - }, - }; - - function keyHandler(handleObj) { - if (typeof handleObj.data === 'string') { - handleObj.data = { keys: handleObj.data }; - } - - // Only care when a possible input has been specified - if (!handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== 'string') { - return; - } - - const origHandler = handleObj.handler; - const keys = handleObj.data.keys.toLowerCase().split(' '); - const textAcceptingInputTypes = ['text', 'password', 'number', 'email', 'url', 'range', 'date', 'month', 'week', 'time', 'datetime', 'datetime-local', 'search', 'color', 'tel']; - - handleObj.handler = function (event) { - // Don't fire in text-accepting inputs that we didn't directly bind to - if (this !== event.target && (/textarea|select/i.test(event.target.nodeName) - || jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1)) { - return; - } - - const special = jQuery.hotkeys.specialKeys[event.keyCode]; - const character = String.fromCharCode(event.which).toLowerCase(); - let modif = ''; const - possible = {}; - - // check combinations (alt|ctrl|shift+anything) - if (event.altKey && special !== 'alt') { - modif += 'alt+'; - } - - if (event.ctrlKey && special !== 'ctrl') { - modif += 'ctrl+'; - } - - // TODO: Need to make sure this works consistently across platforms - if (event.metaKey && !event.ctrlKey && special !== 'meta') { - modif += 'meta+'; - } - - if (event.shiftKey && special !== 'shift') { - modif += 'shift+'; - } - - if (special) { - possible[modif + special] = true; - } - - if (character) { - possible[modif + character] = true; - possible[modif + jQuery.hotkeys.shiftNums[character]] = true; - - // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" - if (modif === 'shift+') { - possible[jQuery.hotkeys.shiftNums[character]] = true; - } - } - - for (let i = 0, l = keys.length; i < l; i++) { - if (possible[keys[i]]) { - return origHandler.apply(this, arguments); - } - } - }; - } - - jQuery.each(['keydown', 'keyup', 'keypress'], function () { - jQuery.event.special[this] = { add: keyHandler }; - }); -}(this.jQuery)); diff --git a/packages/mindplot/src/components/libraries/jquery/jquery-2.1.0.js b/packages/mindplot/src/components/libraries/jquery/jquery-2.1.0.js deleted file mode 100644 index 37257b61..00000000 --- a/packages/mindplot/src/components/libraries/jquery/jquery-2.1.0.js +++ /dev/null @@ -1,8861 +0,0 @@ -/*! - * jQuery JavaScript Library v2.1.0 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-01-23T21:10Z - */ - -(function (global, factory) { - if (typeof module === 'object' && typeof module.exports === 'object') { - // For CommonJS and CommonJS-like environments where a proper window is present, - // execute the factory and get jQuery - // For environments that do not inherently posses a window with a document - // (such as Node.js), expose a jQuery-making factory as module.exports - // This accentuates the need for the creation of a real window - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info - module.exports = global.document - ? factory(global, true) - : function (w) { - if (!w.document) { - throw new Error('jQuery requires a window with a document'); - } - return factory(w); - }; - } else { - factory(global); - } - -// Pass this if window is not defined yet -}(typeof window !== 'undefined' ? window : this, (window, noGlobal) => { -// Can't do this because several apps including ASP.NET trace - // the stack via arguments.caller.callee and Firefox dies if - // you try to trace through "use strict" call chains. (#13335) - // Support: Firefox 18+ - // - - const arr = []; - - const { slice } = arr; - - const { concat } = arr; - - const { push } = arr; - - const { indexOf } = arr; - - const class2type = {}; - - const { toString } = class2type; - - const hasOwn = class2type.hasOwnProperty; - - const { trim } = ''; - - const support = {}; - - const - // Use the correct document accordingly with window argument (sandbox) - { document } = window; - - const version = '2.1.0'; - - // Define a local copy of jQuery - var jQuery = function (selector, context) { - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init(selector, context); - }; - - // Matches dashed string for camelizing - const rmsPrefix = /^-ms-/; - const rdashAlpha = /-([\da-z])/gi; - - // Used by jQuery.camelCase as callback to replace() - const fcamelCase = function (all, letter) { - return letter.toUpperCase(); - }; - - jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // Start with an empty selector - selector: '', - - // The default length of a jQuery object is 0 - length: 0, - - toArray() { - return slice.call(this); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get(num) { - return num != null - - // Return a 'clean' array - ? (num < 0 ? this[num + this.length] : this[num]) - - // Return just the object - : slice.call(this); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack(elems) { - // Build a new jQuery matched element set - const ret = jQuery.merge(this.constructor(), elems); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - ret.context = this.context; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each(callback, args) { - return jQuery.each(this, callback, args); - }, - - map(callback) { - return this.pushStack(jQuery.map(this, (elem, i) => callback.call(elem, i, elem))); - }, - - slice() { - return this.pushStack(slice.apply(this, arguments)); - }, - - first() { - return this.eq(0); - }, - - last() { - return this.eq(-1); - }, - - eq(i) { - const len = this.length; - const j = +i + (i < 0 ? len : 0); - return this.pushStack(j >= 0 && j < len ? [this[j]] : []); - }, - - end() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push, - sort: arr.sort, - splice: arr.splice, - }; - - jQuery.extend = jQuery.fn.extend = function () { - let options; let name; let src; let copy; let copyIsArray; let clone; - let target = arguments[0] || {}; - let i = 1; - const { length } = arguments; - let deep = false; - - // Handle a deep copy situation - if (typeof target === 'boolean') { - deep = target; - - // skip the boolean and the target - target = arguments[i] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if (typeof target !== 'object' && !jQuery.isFunction(target)) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if (i === length) { - target = this; - i--; - } - - for (; i < length; i++) { - // Only deal with non-null/undefined values - if ((options = arguments[i]) != null) { - // Extend the base object - for (name in options) { - src = target[name]; - copy = options[name]; - - // Prevent never-ending loop - if (target === copy) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) { - if (copyIsArray) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[name] = jQuery.extend(deep, clone, copy); - - // Don't bring in undefined values - } else if (copy !== undefined) { - target[name] = copy; - } - } - } - } - - // Return the modified object - return target; - }; - - jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: `jQuery${(version + Math.random()).replace(/\D/g, '')}`, - - // Assume jQuery is ready without the ready module - isReady: true, - - error(msg) { - throw new Error(msg); - }, - - noop() {}, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction(obj) { - return jQuery.type(obj) === 'function'; - }, - - isArray: Array.isArray, - - isWindow(obj) { - return obj != null && obj === obj.window; - }, - - isNumeric(obj) { - // parseFloat NaNs numeric-cast false positives (null|true|false|"") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - return obj - parseFloat(obj) >= 0; - }, - - isPlainObject(obj) { - // Not plain objects: - // - Any object or value whose internal [[Class]] property is not "[object Object]" - // - DOM nodes - // - window - if (jQuery.type(obj) !== 'object' || obj.nodeType || jQuery.isWindow(obj)) { - return false; - } - - // Support: Firefox <20 - // The try/catch suppresses exceptions thrown when attempting to access - // the "constructor" property of certain host objects, ie. |window.location| - // https://bugzilla.mozilla.org/show_bug.cgi?id=814622 - try { - if (obj.constructor - && !hasOwn.call(obj.constructor.prototype, 'isPrototypeOf')) { - return false; - } - } catch (e) { - return false; - } - - // If the function hasn't returned already, we're confident that - // |obj| is a plain object, created by {} or constructed with new Object - return true; - }, - - isEmptyObject(obj) { - let name; - for (name in obj) { - return false; - } - return true; - }, - - type(obj) { - if (obj == null) { - return `${obj}`; - } - // Support: Android < 4.0, iOS < 6 (functionish RegExp) - return typeof obj === 'object' || typeof obj === 'function' - ? class2type[toString.call(obj)] || 'object' - : typeof obj; - }, - - // Evaluates a script in a global context - globalEval(code) { - let script; - const indirect = eval; - - code = jQuery.trim(code); - - if (code) { - // If the code includes a valid, prologue position - // strict mode pragma, execute code by injecting a - // script tag into the document. - if (code.indexOf('use strict') === 1) { - script = document.createElement('script'); - script.text = code; - document.head.appendChild(script).parentNode.removeChild(script); - } else { - // Otherwise, avoid the DOM node creation, insertion - // and removal by using an indirect global eval - indirect(code); - } - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase(string) { - return string.replace(rmsPrefix, 'ms-').replace(rdashAlpha, fcamelCase); - }, - - nodeName(elem, name) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, - - // args is for internal usage only - each(obj, callback, args) { - let value; - let i = 0; - const { length } = obj; - const isArray = isArraylike(obj); - - if (args) { - if (isArray) { - for (; i < length; i++) { - value = callback.apply(obj[i], args); - - if (value === false) { - break; - } - } - } else { - for (i in obj) { - value = callback.apply(obj[i], args); - - if (value === false) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else if (isArray) { - for (; i < length; i++) { - value = callback.call(obj[i], i, obj[i]); - - if (value === false) { - break; - } - } - } else { - for (i in obj) { - value = callback.call(obj[i], i, obj[i]); - - if (value === false) { - break; - } - } - } - - return obj; - }, - - trim(text) { - return text == null ? '' : trim.call(text); - }, - - // results is for internal usage only - makeArray(arr, results) { - const ret = results || []; - - if (arr != null) { - if (isArraylike(Object(arr))) { - jQuery.merge(ret, - typeof arr === 'string' - ? [arr] : arr); - } else { - push.call(ret, arr); - } - } - - return ret; - }, - - inArray(elem, arr, i) { - return arr == null ? -1 : indexOf.call(arr, elem, i); - }, - - merge(first, second) { - const len = +second.length; - let j = 0; - let i = first.length; - - for (; j < len; j++) { - first[i++] = second[j]; - } - - first.length = i; - - return first; - }, - - grep(elems, callback, invert) { - let callbackInverse; - const matches = []; - let i = 0; - const { length } = elems; - const callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for (; i < length; i++) { - callbackInverse = !callback(elems[i], i); - if (callbackInverse !== callbackExpect) { - matches.push(elems[i]); - } - } - - return matches; - }, - - // arg is for internal usage only - map(elems, callback, arg) { - let value; - let i = 0; - const { length } = elems; - const isArray = isArraylike(elems); - const ret = []; - - // Go through the array, translating each of the items to their new values - if (isArray) { - for (; i < length; i++) { - value = callback(elems[i], i, arg); - - if (value != null) { - ret.push(value); - } - } - - // Go through every key on the object, - } else { - for (i in elems) { - value = callback(elems[i], i, arg); - - if (value != null) { - ret.push(value); - } - } - } - - // Flatten any nested arrays - return concat.apply([], ret); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy(fn, context) { - let tmp; let args; let - proxy; - - if (typeof context === 'string') { - tmp = fn[context]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if (!jQuery.isFunction(fn)) { - return undefined; - } - - // Simulated bind - args = slice.call(arguments, 2); - proxy = function () { - return fn.apply(context || this, args.concat(slice.call(arguments))); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: Date.now, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support, - }); - - // Populate the class2type map - jQuery.each('Boolean Number String Function Array Date RegExp Object Error'.split(' '), (i, name) => { - class2type[`[object ${name}]`] = name.toLowerCase(); - }); - - function isArraylike(obj) { - const { length } = obj; - const type = jQuery.type(obj); - - if (type === 'function' || jQuery.isWindow(obj)) { - return false; - } - - if (obj.nodeType === 1 && length) { - return true; - } - - return type === 'array' || length === 0 - || typeof length === 'number' && length > 0 && (length - 1) in obj; - } - const Sizzle = -/*! - * Sizzle CSS Selector Engine v1.10.16 - * http://sizzlejs.com/ - * - * Copyright 2013 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-01-13 - */ -(function (window) { - let i; - let support; - let Expr; - let getText; - let isXML; - let compile; - let outermostContext; - let sortInput; - let hasDuplicate; - - // Local document vars - let setDocument; - let document; - let docElem; - let documentIsHTML; - let rbuggyQSA; - let rbuggyMatches; - let matches; - let contains; - - // Instance-specific data - const expando = `sizzle${-(new Date())}`; - const preferredDoc = window.document; - let dirruns = 0; - let done = 0; - const classCache = createCache(); - const tokenCache = createCache(); - const compilerCache = createCache(); - let sortOrder = function (a, b) { - if (a === b) { - hasDuplicate = true; - } - return 0; - }; - - // General-purpose constants - const strundefined = typeof undefined; - const MAX_NEGATIVE = 1 << 31; - - // Instance methods - const hasOwn = ({}).hasOwnProperty; - let arr = []; - const { pop } = arr; - const push_native = arr.push; - let { push } = arr; - const { slice } = arr; - // Use a stripped-down indexOf if we can't use a native one - const indexOf = arr.indexOf || function (elem) { - let i = 0; - const len = this.length; - for (; i < len; i++) { - if (this[i] === elem) { - return i; - } - } - return -1; - }; - - const booleans = 'checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped'; - - // Regular expressions - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - const whitespace = '[\\x20\\t\\r\\n\\f]'; - // http://www.w3.org/TR/css3-syntax/#characters - const characterEncoding = '(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+'; - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - const identifier = characterEncoding.replace('w', 'w#'); - - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - const attributes = `\\[${whitespace}*(${characterEncoding})${whitespace - }*(?:([*^$|!~]?=)${whitespace}*(?:(['"])((?:\\\\.|[^\\\\])*?)\\3|(${identifier})|)|)${whitespace}*\\]`; - - // Prefer arguments quoted, - // then not containing pseudos/brackets, - // then attribute selectors/non-parenthetical expressions, - // then anything else - // These preferences are here to reduce the number of selectors - // needing tokenize in the PSEUDO preFilter - const pseudos = `:(${characterEncoding})(?:\\(((['"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|${attributes.replace(3, 8)})*)|.*)\\)|)`; - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - const rtrim = new RegExp(`^${whitespace}+|((?:^|[^\\\\])(?:\\\\.)*)${whitespace}+$`, 'g'); - - const rcomma = new RegExp(`^${whitespace}*,${whitespace}*`); - const rcombinators = new RegExp(`^${whitespace}*([>+~]|${whitespace})${whitespace}*`); - - const rattributeQuotes = new RegExp(`=${whitespace}*([^\\]'"]*?)${whitespace}*\\]`, 'g'); - - const rpseudo = new RegExp(pseudos); - const ridentifier = new RegExp(`^${identifier}$`); - - const matchExpr = { - ID: new RegExp(`^#(${characterEncoding})`), - CLASS: new RegExp(`^\\.(${characterEncoding})`), - TAG: new RegExp(`^(${characterEncoding.replace('w', 'w*')})`), - ATTR: new RegExp(`^${attributes}`), - PSEUDO: new RegExp(`^${pseudos}`), - CHILD: new RegExp(`^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(${whitespace - }*(even|odd|(([+-]|)(\\d*)n|)${whitespace}*(?:([+-]|)${whitespace - }*(\\d+)|))${whitespace}*\\)|)`, 'i'), - bool: new RegExp(`^(?:${booleans})$`, 'i'), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - needsContext: new RegExp(`^${whitespace}*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(${ - whitespace}*((?:-\\d)?\\d*)${whitespace}*\\)|)(?=[^-]|$)`, 'i'), - }; - - const rinputs = /^(?:input|select|textarea|button)$/i; - const rheader = /^h\d$/i; - - const rnative = /^[^{]+\{\s*\[native \w/; - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - const rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/; - - const rsibling = /[+~]/; - const rescape = /'|\\/g; - - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - const runescape = new RegExp(`\\\\([\\da-f]{1,6}${whitespace}?|(${whitespace})|.)`, 'ig'); - const funescape = function (_, escaped, escapedWhitespace) { - const high = `0x${escaped}` - 0x10000; - // NaN means non-codepoint - // Support: Firefox - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace - ? escaped - : high < 0 - // BMP codepoint - ? String.fromCharCode(high + 0x10000) - // Supplemental Plane codepoint (surrogate pair) - : String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00); - }; - - // Optimize for push.apply( _, NodeList ) - try { - push.apply( - (arr = slice.call(preferredDoc.childNodes)), - preferredDoc.childNodes, - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[preferredDoc.childNodes.length].nodeType; - } catch (e) { - push = { - apply: arr.length - - // Leverage slice if possible - ? function (target, els) { - push_native.apply(target, slice.call(els)); - } - - // Support: IE<9 - // Otherwise append directly - : function (target, els) { - let j = target.length; - let i = 0; - // Can't trust NodeList.length - while ((target[j++] = els[i++])) {} - target.length = j - 1; - }, - }; - } - - function Sizzle(selector, context, results, seed) { - let match; let elem; let m; let nodeType; - // QSA vars - let i; let groups; let old; let nid; let newContext; let - newSelector; - - if ((context ? context.ownerDocument || context : preferredDoc) !== document) { - setDocument(context); - } - - context = context || document; - results = results || []; - - if (!selector || typeof selector !== 'string') { - return results; - } - - if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) { - return []; - } - - if (documentIsHTML && !seed) { - // Shortcuts - if ((match = rquickExpr.exec(selector))) { - // Speed-up: Sizzle("#ID") - if ((m = match[1])) { - if (nodeType === 9) { - elem = context.getElementById(m); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if (elem && elem.parentNode) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if (elem.id === m) { - results.push(elem); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if (context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) - && contains(context, elem) && elem.id === m) { - results.push(elem); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if (match[2]) { - push.apply(results, context.getElementsByTagName(selector)); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ((m = match[3]) && support.getElementsByClassName && context.getElementsByClassName) { - push.apply(results, context.getElementsByClassName(m)); - return results; - } - } - - // QSA path - if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) { - nid = old = expando; - newContext = context; - newSelector = nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if (nodeType === 1 && context.nodeName.toLowerCase() !== 'object') { - groups = tokenize(selector); - - if ((old = context.getAttribute('id'))) { - nid = old.replace(rescape, '\\$&'); - } else { - context.setAttribute('id', nid); - } - nid = `[id='${nid}'] `; - - i = groups.length; - while (i--) { - groups[i] = nid + toSelector(groups[i]); - } - newContext = rsibling.test(selector) && testContext(context.parentNode) || context; - newSelector = groups.join(','); - } - - if (newSelector) { - try { - push.apply(results, - newContext.querySelectorAll(newSelector)); - return results; - } catch (qsaError) { - } finally { - if (!old) { - context.removeAttribute('id'); - } - } - } - } - } - - // All others - return select(selector.replace(rtrim, '$1'), context, results, seed); - } - - /** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ - function createCache() { - const keys = []; - - function cache(key, value) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if (keys.push(`${key} `) > Expr.cacheLength) { - // Only keep the most recent entries - delete cache[keys.shift()]; - } - return (cache[`${key} `] = value); - } - return cache; - } - - /** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ - function markFunction(fn) { - fn[expando] = true; - return fn; - } - - /** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ - function assert(fn) { - let div = document.createElement('div'); - - try { - return !!fn(div); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if (div.parentNode) { - div.parentNode.removeChild(div); - } - // release memory in IE - div = null; - } - } - - /** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ - function addHandle(attrs, handler) { - const arr = attrs.split('|'); - let i = attrs.length; - - while (i--) { - Expr.attrHandle[arr[i]] = handler; - } - } - - /** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ - function siblingCheck(a, b) { - let cur = b && a; - const diff = cur && a.nodeType === 1 && b.nodeType === 1 - && (~b.sourceIndex || MAX_NEGATIVE) - - (~a.sourceIndex || MAX_NEGATIVE); - - // Use IE sourceIndex if available on both nodes - if (diff) { - return diff; - } - - // Check if b follows a - if (cur) { - while ((cur = cur.nextSibling)) { - if (cur === b) { - return -1; - } - } - } - - return a ? 1 : -1; - } - - /** - * Returns a function to use in pseudos for input types - * @param {String} type - */ - function createInputPseudo(type) { - return function (elem) { - const name = elem.nodeName.toLowerCase(); - return name === 'input' && elem.type === type; - }; - } - - /** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ - function createButtonPseudo(type) { - return function (elem) { - const name = elem.nodeName.toLowerCase(); - return (name === 'input' || name === 'button') && elem.type === type; - }; - } - - /** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ - function createPositionalPseudo(fn) { - return markFunction((argument) => { - argument = +argument; - return markFunction((seed, matches) => { - let j; - const matchIndexes = fn([], seed.length, argument); - let i = matchIndexes.length; - - // Match elements found at the specified indexes - while (i--) { - if (seed[(j = matchIndexes[i])]) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); - } - - /** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ - function testContext(context) { - return context && typeof context.getElementsByTagName !== strundefined && context; - } - - // Expose support vars for convenience - support = Sizzle.support = {}; - - /** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ - isXML = Sizzle.isXML = function (elem) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - const documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== 'HTML' : false; - }; - - /** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ - setDocument = Sizzle.setDocument = function (node) { - let hasCompare; - const doc = node ? node.ownerDocument || node : preferredDoc; - const parent = doc.defaultView; - - // If no document and documentElement is available, return - if (doc === document || doc.nodeType !== 9 || !doc.documentElement) { - return document; - } - - // Set our document - document = doc; - docElem = doc.documentElement; - - // Support tests - documentIsHTML = !isXML(doc); - - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if (parent && parent !== parent.top) { - // IE11 does not have attachEvent, so all must suffer - if (parent.addEventListener) { - parent.addEventListener('unload', () => { - setDocument(); - }, false); - } else if (parent.attachEvent) { - parent.attachEvent('onunload', () => { - setDocument(); - }); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) - support.attributes = assert((div) => { - div.className = 'i'; - return !div.getAttribute('className'); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert((div) => { - div.appendChild(doc.createComment('')); - return !div.getElementsByTagName('*').length; - }); - - // Check if getElementsByClassName can be trusted - support.getElementsByClassName = rnative.test(doc.getElementsByClassName) && assert((div) => { - div.innerHTML = "
"; - - // Support: Safari<4 - // Catch class over-caching - div.firstChild.className = 'i'; - // Support: Opera<10 - // Catch gEBCN failure to find non-leading classes - return div.getElementsByClassName('i').length === 2; - }); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert((div) => { - docElem.appendChild(div).id = expando; - return !doc.getElementsByName || !doc.getElementsByName(expando).length; - }); - - // ID find and filter - if (support.getById) { - Expr.find.ID = function (id, context) { - if (typeof context.getElementById !== strundefined && documentIsHTML) { - const m = context.getElementById(id); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }; - Expr.filter.ID = function (id) { - const attrId = id.replace(runescape, funescape); - return function (elem) { - return elem.getAttribute('id') === attrId; - }; - }; - } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find.ID; - - Expr.filter.ID = function (id) { - const attrId = id.replace(runescape, funescape); - return function (elem) { - const node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode('id'); - return node && node.value === attrId; - }; - }; - } - - // Tag - Expr.find.TAG = support.getElementsByTagName - ? function (tag, context) { - if (typeof context.getElementsByTagName !== strundefined) { - return context.getElementsByTagName(tag); - } - } - : function (tag, context) { - let elem; - const tmp = []; - let i = 0; - const results = context.getElementsByTagName(tag); - - // Filter out possible comments - if (tag === '*') { - while ((elem = results[i++])) { - if (elem.nodeType === 1) { - tmp.push(elem); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find.CLASS = support.getElementsByClassName && function (className, context) { - if (typeof context.getElementsByClassName !== strundefined && documentIsHTML) { - return context.getElementsByClassName(className); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ((support.qsa = rnative.test(doc.querySelectorAll))) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert((div) => { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // Support: IE8, Opera 10-12 - // Nothing should be selected when empty strings follow ^= or $= or *= - if (div.querySelectorAll("[t^='']").length) { - rbuggyQSA.push(`[*^$]=${whitespace}*(?:''|"")`); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if (!div.querySelectorAll('[selected]').length) { - rbuggyQSA.push(`\\[${whitespace}*(?:value|${booleans})`); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if (!div.querySelectorAll(':checked').length) { - rbuggyQSA.push(':checked'); - } - }); - - assert((div) => { - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - const input = doc.createElement('input'); - input.setAttribute('type', 'hidden'); - div.appendChild(input).setAttribute('name', 'D'); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if (div.querySelectorAll('[name=d]').length) { - rbuggyQSA.push(`name${whitespace}*[*^$|!~]?=`); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if (!div.querySelectorAll(':enabled').length) { - rbuggyQSA.push(':enabled', ':disabled'); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll('*,:x'); - rbuggyQSA.push(',.*:'); - }); - } - - if ((support.matchesSelector = rnative.test((matches = docElem.webkitMatchesSelector - || docElem.mozMatchesSelector - || docElem.oMatchesSelector - || docElem.msMatchesSelector)))) { - assert((div) => { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call(div, 'div'); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call(div, "[s!='']:x"); - rbuggyMatches.push('!=', pseudos); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join('|')); - rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join('|')); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test(docElem.compareDocumentPosition); - - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = hasCompare || rnative.test(docElem.contains) - ? function (a, b) { - const adown = a.nodeType === 9 ? a.documentElement : a; - const bup = b && b.parentNode; - return a === bup || !!(bup && bup.nodeType === 1 && ( - adown.contains - ? adown.contains(bup) - : a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16 - )); - } - : function (a, b) { - if (b) { - while ((b = b.parentNode)) { - if (b === a) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare - ? function (a, b) { - // Flag for duplicate removal - if (a === b) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - let compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if (compare) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = (a.ownerDocument || a) === (b.ownerDocument || b) - ? a.compareDocumentPosition(b) - - // Otherwise we know they are disconnected - : 1; - - // Disconnected nodes - if (compare & 1 - || (!support.sortDetached && b.compareDocumentPosition(a) === compare)) { - // Choose the first element that is related to our preferred document - if (a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a)) { - return -1; - } - if (b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b)) { - return 1; - } - - // Maintain original order - return sortInput - ? (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) - : 0; - } - - return compare & 4 ? -1 : 1; - } - : function (a, b) { - // Exit early if the nodes are identical - if (a === b) { - hasDuplicate = true; - return 0; - } - - let cur; - let i = 0; - const aup = a.parentNode; - const bup = b.parentNode; - const ap = [a]; - const bp = [b]; - - // Parentless nodes are either documents or disconnected - if (!aup || !bup) { - return a === doc ? -1 - : b === doc ? 1 - : aup ? -1 - : bup ? 1 - : sortInput - ? (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) - : 0; - - // If the nodes are siblings, we can do a quick check - } if (aup === bup) { - return siblingCheck(a, b); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ((cur = cur.parentNode)) { - ap.unshift(cur); - } - cur = b; - while ((cur = cur.parentNode)) { - bp.unshift(cur); - } - - // Walk down the tree looking for a discrepancy - while (ap[i] === bp[i]) { - i++; - } - - return i - // Do a sibling check if the nodes have a common ancestor - ? siblingCheck(ap[i], bp[i]) - - // Otherwise nodes in our document sort first - : ap[i] === preferredDoc ? -1 - : bp[i] === preferredDoc ? 1 - : 0; - }; - - return doc; - }; - - Sizzle.matches = function (expr, elements) { - return Sizzle(expr, null, null, elements); - }; - - Sizzle.matchesSelector = function (elem, expr) { - // Set document vars if needed - if ((elem.ownerDocument || elem) !== document) { - setDocument(elem); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace(rattributeQuotes, "='$1']"); - - if (support.matchesSelector && documentIsHTML - && (!rbuggyMatches || !rbuggyMatches.test(expr)) - && (!rbuggyQSA || !rbuggyQSA.test(expr))) { - try { - const ret = matches.call(elem, expr); - - // IE 9's matchesSelector returns false on disconnected nodes - if (ret || support.disconnectedMatch - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - || elem.document && elem.document.nodeType !== 11) { - return ret; - } - } catch (e) {} - } - - return Sizzle(expr, document, null, [elem]).length > 0; - }; - - Sizzle.contains = function (context, elem) { - // Set document vars if needed - if ((context.ownerDocument || context) !== document) { - setDocument(context); - } - return contains(context, elem); - }; - - Sizzle.attr = function (elem, name) { - // Set document vars if needed - if ((elem.ownerDocument || elem) !== document) { - setDocument(elem); - } - - const fn = Expr.attrHandle[name.toLowerCase()]; - // Don't get fooled by Object.prototype properties (jQuery #13807) - let val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) - ? fn(elem, name, !documentIsHTML) - : undefined; - - return val !== undefined - ? val - : support.attributes || !documentIsHTML - ? elem.getAttribute(name) - : (val = elem.getAttributeNode(name)) && val.specified - ? val.value - : null; - }; - - Sizzle.error = function (msg) { - throw new Error(`Syntax error, unrecognized expression: ${msg}`); - }; - - /** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ - Sizzle.uniqueSort = function (results) { - let elem; - const duplicates = []; - let j = 0; - let i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice(0); - results.sort(sortOrder); - - if (hasDuplicate) { - while ((elem = results[i++])) { - if (elem === results[i]) { - j = duplicates.push(i); - } - } - while (j--) { - results.splice(duplicates[j], 1); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; - }; - - /** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ - getText = Sizzle.getText = function (elem) { - let node; - let ret = ''; - let i = 0; - const { nodeType } = elem; - - if (!nodeType) { - // If no nodeType, this is expected to be an array - while ((node = elem[i++])) { - // Do not traverse comment nodes - ret += getText(node); - } - } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if (typeof elem.textContent === 'string') { - return elem.textContent; - } - // Traverse its children - for (elem = elem.firstChild; elem; elem = elem.nextSibling) { - ret += getText(elem); - } - } else if (nodeType === 3 || nodeType === 4) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; - }; - - Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - '>': { dir: 'parentNode', first: true }, - ' ': { dir: 'parentNode' }, - '+': { dir: 'previousSibling', first: true }, - '~': { dir: 'previousSibling' }, - }, - - preFilter: { - ATTR(match) { - match[1] = match[1].replace(runescape, funescape); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = (match[4] || match[5] || '').replace(runescape, funescape); - - if (match[2] === '~=') { - match[3] = ` ${match[3]} `; - } - - return match.slice(0, 4); - }, - - CHILD(match) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if (match[1].slice(0, 3) === 'nth') { - // nth-* requires argument - if (!match[3]) { - Sizzle.error(match[0]); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === 'even' || match[3] === 'odd')); - match[5] = +((match[7] + match[8]) || match[3] === 'odd'); - - // other types prohibit arguments - } else if (match[3]) { - Sizzle.error(match[0]); - } - - return match; - }, - - PSEUDO(match) { - let excess; - const unquoted = !match[5] && match[2]; - - if (matchExpr.CHILD.test(match[0])) { - return null; - } - - // Accept quoted arguments as-is - if (match[3] && match[4] !== undefined) { - match[2] = match[4]; - - // Strip excess characters from unquoted arguments - } else if (unquoted && rpseudo.test(unquoted) - // Get excess from tokenize (recursively) - && (excess = tokenize(unquoted, true)) - // advance to the next closing parenthesis - && (excess = unquoted.indexOf(')', unquoted.length - excess) - unquoted.length)) { - // excess is a negative index - match[0] = match[0].slice(0, excess); - match[2] = unquoted.slice(0, excess); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice(0, 3); - }, - }, - - filter: { - - TAG(nodeNameSelector) { - const nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase(); - return nodeNameSelector === '*' - ? function () { return true; } - : function (elem) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - CLASS(className) { - let pattern = classCache[`${className} `]; - - return pattern - || (pattern = new RegExp(`(^|${whitespace})${className}(${whitespace}|$)`)) - && classCache(className, (elem) => pattern.test(typeof elem.className === 'string' && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute('class') || '')); - }, - - ATTR(name, operator, check) { - return function (elem) { - let result = Sizzle.attr(elem, name); - - if (result == null) { - return operator === '!='; - } - if (!operator) { - return true; - } - - result += ''; - - return operator === '=' ? result === check - : operator === '!=' ? result !== check - : operator === '^=' ? check && result.indexOf(check) === 0 - : operator === '*=' ? check && result.indexOf(check) > -1 - : operator === '$=' ? check && result.slice(-check.length) === check - : operator === '~=' ? (` ${result} `).indexOf(check) > -1 - : operator === '|=' ? result === check || result.slice(0, check.length + 1) === `${check}-` - : false; - }; - }, - - CHILD(type, what, argument, first, last) { - const simple = type.slice(0, 3) !== 'nth'; - const forward = type.slice(-4) !== 'last'; - const ofType = what === 'of-type'; - - return first === 1 && last === 0 - - // Shortcut for :nth-*(n) - ? function (elem) { - return !!elem.parentNode; - } - - : function (elem, context, xml) { - let cache; let outerCache; let node; let diff; let nodeIndex; let start; - let dir = simple !== forward ? 'nextSibling' : 'previousSibling'; - const parent = elem.parentNode; - const name = ofType && elem.nodeName.toLowerCase(); - const useCache = !xml && !ofType; - - if (parent) { - // :(first|last|only)-(child|of-type) - if (simple) { - while (dir) { - node = elem; - while ((node = node[dir])) { - if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === 'only' && !start && 'nextSibling'; - } - return true; - } - - start = [forward ? parent.firstChild : parent.lastChild]; - - // non-xml :nth-child(...) stores cache data on `parent` - if (forward && useCache) { - // Seek `elem` from a previously-cached index - outerCache = parent[expando] || (parent[expando] = {}); - cache = outerCache[type] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[nodeIndex]; - - while ((node = ++nodeIndex && node && node[dir] - - // Fallback to seeking `elem` from the start - || (diff = nodeIndex = 0) || start.pop())) { - // When found, cache indexes on `parent` and break - if (node.nodeType === 1 && ++diff && node === elem) { - outerCache[type] = [dirruns, nodeIndex, diff]; - break; - } - } - - // Use previously-cached element index if available - } else if (useCache && (cache = (elem[expando] || (elem[expando] = {}))[type]) && cache[0] === dirruns) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ((node = ++nodeIndex && node && node[dir] - || (diff = nodeIndex = 0) || start.pop())) { - if ((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff) { - // Cache the index of each encountered element - if (useCache) { - (node[expando] || (node[expando] = {}))[type] = [dirruns, diff]; - } - - if (node === elem) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || (diff % first === 0 && diff / first >= 0); - } - }; - }, - - PSEUDO(pseudo, argument) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - let args; - const fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] - || Sizzle.error(`unsupported pseudo: ${pseudo}`); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if (fn[expando]) { - return fn(argument); - } - - // But maintain support for old signatures - if (fn.length > 1) { - args = [pseudo, pseudo, '', argument]; - return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) - ? markFunction((seed, matches) => { - let idx; - const matched = fn(seed, argument); - let i = matched.length; - while (i--) { - idx = indexOf.call(seed, matched[i]); - seed[idx] = !(matches[idx] = matched[i]); - } - }) - : function (elem) { - return fn(elem, 0, args); - }; - } - - return fn; - }, - }, - - pseudos: { - // Potentially complex pseudos - not: markFunction((selector) => { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - const input = []; - const results = []; - const matcher = compile(selector.replace(rtrim, '$1')); - - return matcher[expando] - ? markFunction((seed, matches, context, xml) => { - let elem; - const unmatched = matcher(seed, null, xml, []); - let i = seed.length; - - // Match elements unmatched by `matcher` - while (i--) { - if ((elem = unmatched[i])) { - seed[i] = !(matches[i] = elem); - } - } - }) - : function (elem, context, xml) { - input[0] = elem; - matcher(input, null, xml, results); - return !results.pop(); - }; - }), - - has: markFunction((selector) => function (elem) { - return Sizzle(selector, elem).length > 0; - }), - - contains: markFunction((text) => function (elem) { - return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - lang: markFunction((lang) => { - // lang value must be a valid identifier - if (!ridentifier.test(lang || '')) { - Sizzle.error(`unsupported lang: ${lang}`); - } - lang = lang.replace(runescape, funescape).toLowerCase(); - return function (elem) { - let elemLang; - do { - if ((elemLang = documentIsHTML - ? elem.lang - : elem.getAttribute('xml:lang') || elem.getAttribute('lang'))) { - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf(`${lang}-`) === 0; - } - } while ((elem = elem.parentNode) && elem.nodeType === 1); - return false; - }; - }), - - // Miscellaneous - target(elem) { - const hash = window.location && window.location.hash; - return hash && hash.slice(1) === elem.id; - }, - - root(elem) { - return elem === docElem; - }, - - focus(elem) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - enabled(elem) { - return elem.disabled === false; - }, - - disabled(elem) { - return elem.disabled === true; - }, - - checked(elem) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - const nodeName = elem.nodeName.toLowerCase(); - return (nodeName === 'input' && !!elem.checked) || (nodeName === 'option' && !!elem.selected); - }, - - selected(elem) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if (elem.parentNode) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - empty(elem) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for (elem = elem.firstChild; elem; elem = elem.nextSibling) { - if (elem.nodeType < 6) { - return false; - } - } - return true; - }, - - parent(elem) { - return !Expr.pseudos.empty(elem); - }, - - // Element/input types - header(elem) { - return rheader.test(elem.nodeName); - }, - - input(elem) { - return rinputs.test(elem.nodeName); - }, - - button(elem) { - const name = elem.nodeName.toLowerCase(); - return name === 'input' && elem.type === 'button' || name === 'button'; - }, - - text(elem) { - let attr; - return elem.nodeName.toLowerCase() === 'input' - && elem.type === 'text' - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - && ((attr = elem.getAttribute('type')) == null || attr.toLowerCase() === 'text'); - }, - - // Position-in-collection - first: createPositionalPseudo(() => [0]), - - last: createPositionalPseudo((matchIndexes, length) => [length - 1]), - - eq: createPositionalPseudo((matchIndexes, length, argument) => [argument < 0 ? argument + length : argument]), - - even: createPositionalPseudo((matchIndexes, length) => { - let i = 0; - for (; i < length; i += 2) { - matchIndexes.push(i); - } - return matchIndexes; - }), - - odd: createPositionalPseudo((matchIndexes, length) => { - let i = 1; - for (; i < length; i += 2) { - matchIndexes.push(i); - } - return matchIndexes; - }), - - lt: createPositionalPseudo((matchIndexes, length, argument) => { - let i = argument < 0 ? argument + length : argument; - for (; --i >= 0;) { - matchIndexes.push(i); - } - return matchIndexes; - }), - - gt: createPositionalPseudo((matchIndexes, length, argument) => { - let i = argument < 0 ? argument + length : argument; - for (; ++i < length;) { - matchIndexes.push(i); - } - return matchIndexes; - }), - }, - }; - - Expr.pseudos.nth = Expr.pseudos.eq; - - // Add button/input type pseudos - for (i in { - radio: true, checkbox: true, file: true, password: true, image: true, - }) { - Expr.pseudos[i] = createInputPseudo(i); - } - for (i in { submit: true, reset: true }) { - Expr.pseudos[i] = createButtonPseudo(i); - } - - // Easy API for creating new setFilters - function setFilters() {} - setFilters.prototype = Expr.filters = Expr.pseudos; - Expr.setFilters = new setFilters(); - - function tokenize(selector, parseOnly) { - let matched; let match; let tokens; let type; - let soFar; let groups; let preFilters; - const cached = tokenCache[`${selector} `]; - - if (cached) { - return parseOnly ? 0 : cached.slice(0); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while (soFar) { - // Comma and first run - if (!matched || (match = rcomma.exec(soFar))) { - if (match) { - // Don't consume trailing commas as valid - soFar = soFar.slice(match[0].length) || soFar; - } - groups.push((tokens = [])); - } - - matched = false; - - // Combinators - if ((match = rcombinators.exec(soFar))) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace(rtrim, ' '), - }); - soFar = soFar.slice(matched.length); - } - - // Filters - for (type in Expr.filter) { - if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] - || (match = preFilters[type](match)))) { - matched = match.shift(); - tokens.push({ - value: matched, - type, - matches: match, - }); - soFar = soFar.slice(matched.length); - } - } - - if (!matched) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly - ? soFar.length - : soFar - ? Sizzle.error(selector) - // Cache the tokens - : tokenCache(selector, groups).slice(0); - } - - function toSelector(tokens) { - let i = 0; - const len = tokens.length; - let selector = ''; - for (; i < len; i++) { - selector += tokens[i].value; - } - return selector; - } - - function addCombinator(matcher, combinator, base) { - const { dir } = combinator; - const checkNonElements = base && dir === 'parentNode'; - const doneName = done++; - - return combinator.first - // Check against closest ancestor/preceding element - ? function (elem, context, xml) { - while ((elem = elem[dir])) { - if (elem.nodeType === 1 || checkNonElements) { - return matcher(elem, context, xml); - } - } - } - - // Check against all ancestor/preceding elements - : function (elem, context, xml) { - let oldCache; let outerCache; - const newCache = [dirruns, doneName]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if (xml) { - while ((elem = elem[dir])) { - if (elem.nodeType === 1 || checkNonElements) { - if (matcher(elem, context, xml)) { - return true; - } - } - } - } else { - while ((elem = elem[dir])) { - if (elem.nodeType === 1 || checkNonElements) { - outerCache = elem[expando] || (elem[expando] = {}); - if ((oldCache = outerCache[dir]) - && oldCache[0] === dirruns && oldCache[1] === doneName) { - // Assign to newCache so results back-propagate to previous elements - return (newCache[2] = oldCache[2]); - } - // Reuse newcache so results back-propagate to previous elements - outerCache[dir] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ((newCache[2] = matcher(elem, context, xml))) { - return true; - } - } - } - } - }; - } - - function elementMatcher(matchers) { - return matchers.length > 1 - ? function (elem, context, xml) { - let i = matchers.length; - while (i--) { - if (!matchers[i](elem, context, xml)) { - return false; - } - } - return true; - } - : matchers[0]; - } - - function condense(unmatched, map, filter, context, xml) { - let elem; - const newUnmatched = []; - let i = 0; - const len = unmatched.length; - const mapped = map != null; - - for (; i < len; i++) { - if ((elem = unmatched[i])) { - if (!filter || filter(elem, context, xml)) { - newUnmatched.push(elem); - if (mapped) { - map.push(i); - } - } - } - } - - return newUnmatched; - } - - function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) { - if (postFilter && !postFilter[expando]) { - postFilter = setMatcher(postFilter); - } - if (postFinder && !postFinder[expando]) { - postFinder = setMatcher(postFinder, postSelector); - } - return markFunction((seed, results, context, xml) => { - let temp; let i; let elem; - const preMap = []; - const postMap = []; - const preexisting = results.length; - - // Get initial elements from seed or context - const elems = seed || multipleContexts(selector || '*', context.nodeType ? [context] : context, []); - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - const matcherIn = preFilter && (seed || !selector) - ? condense(elems, preMap, preFilter, context, xml) - : elems; - - let matcherOut = matcher - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - ? postFinder || (seed ? preFilter : preexisting || postFilter) - - // ...intermediate processing is necessary - ? [] - - // ...otherwise use results directly - : results - : matcherIn; - - // Find primary matches - if (matcher) { - matcher(matcherIn, matcherOut, context, xml); - } - - // Apply postFilter - if (postFilter) { - temp = condense(matcherOut, postMap); - postFilter(temp, [], context, xml); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while (i--) { - if ((elem = temp[i])) { - matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem); - } - } - } - - if (seed) { - if (postFinder || preFilter) { - if (postFinder) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while (i--) { - if ((elem = matcherOut[i])) { - // Restore matcherIn since elem is not yet a final match - temp.push((matcherIn[i] = elem)); - } - } - postFinder(null, (matcherOut = []), temp, xml); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while (i--) { - if ((elem = matcherOut[i]) - && (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1) { - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results - ? matcherOut.splice(preexisting, matcherOut.length) - : matcherOut, - ); - if (postFinder) { - postFinder(null, results, matcherOut, xml); - } else { - push.apply(results, matcherOut); - } - } - }); - } - - function matcherFromTokens(tokens) { - let checkContext; let matcher; let j; - const len = tokens.length; - const leadingRelative = Expr.relative[tokens[0].type]; - const implicitRelative = leadingRelative || Expr.relative[' ']; - let i = leadingRelative ? 1 : 0; - - // The foundational matcher ensures that elements are reachable from top-level context(s) - const matchContext = addCombinator((elem) => elem === checkContext, implicitRelative, true); - const matchAnyContext = addCombinator((elem) => indexOf.call(checkContext, elem) > -1, implicitRelative, true); - let matchers = [function (elem, context, xml) { - return (!leadingRelative && (xml || context !== outermostContext)) || ( - (checkContext = context).nodeType - ? matchContext(elem, context, xml) - : matchAnyContext(elem, context, xml)); - }]; - - for (; i < len; i++) { - if ((matcher = Expr.relative[tokens[i].type])) { - matchers = [addCombinator(elementMatcher(matchers), matcher)]; - } else { - matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); - - // Return special upon seeing a positional matcher - if (matcher[expando]) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for (; j < len; j++) { - if (Expr.relative[tokens[j].type]) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher(matchers), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice(0, i - 1).concat({ value: tokens[i - 2].type === ' ' ? '*' : '' }), - ).replace(rtrim, '$1'), - matcher, - i < j && matcherFromTokens(tokens.slice(i, j)), - j < len && matcherFromTokens((tokens = tokens.slice(j))), - j < len && toSelector(tokens), - ); - } - matchers.push(matcher); - } - } - - return elementMatcher(matchers); - } - - function matcherFromGroupMatchers(elementMatchers, setMatchers) { - const bySet = setMatchers.length > 0; - const byElement = elementMatchers.length > 0; - const superMatcher = function (seed, context, xml, results, outermost) { - let elem; let j; let matcher; - let matchedCount = 0; - let i = '0'; - const unmatched = seed && []; - let setMatched = []; - const contextBackup = outermostContext; - // We must always have either seed elements or outermost context - const elems = seed || byElement && Expr.find.TAG('*', outermost); - // Use integer dirruns iff this is the outermost matcher - const dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); - const len = elems.length; - - if (outermost) { - outermostContext = context !== document && context; - } - - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for (; i !== len && (elem = elems[i]) != null; i++) { - if (byElement && elem) { - j = 0; - while ((matcher = elementMatchers[j++])) { - if (matcher(elem, context, xml)) { - results.push(elem); - break; - } - } - if (outermost) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if (bySet) { - // They will have gone through all possible matchers - if ((elem = !matcher && elem)) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if (seed) { - unmatched.push(elem); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if (bySet && i !== matchedCount) { - j = 0; - while ((matcher = setMatchers[j++])) { - matcher(unmatched, setMatched, context, xml); - } - - if (seed) { - // Reintegrate element matches to eliminate the need for sorting - if (matchedCount > 0) { - while (i--) { - if (!(unmatched[i] || setMatched[i])) { - setMatched[i] = pop.call(results); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense(setMatched); - } - - // Add matches to results - push.apply(results, setMatched); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if (outermost && !seed && setMatched.length > 0 - && (matchedCount + setMatchers.length) > 1) { - Sizzle.uniqueSort(results); - } - } - - // Override manipulation of globals by nested matchers - if (outermost) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet - ? markFunction(superMatcher) - : superMatcher; - } - - compile = Sizzle.compile = function (selector, group /* Internal Use Only */) { - let i; - const setMatchers = []; - const elementMatchers = []; - let cached = compilerCache[`${selector} `]; - - if (!cached) { - // Generate a function of recursive functions that can be used to check each element - if (!group) { - group = tokenize(selector); - } - i = group.length; - while (i--) { - cached = matcherFromTokens(group[i]); - if (cached[expando]) { - setMatchers.push(cached); - } else { - elementMatchers.push(cached); - } - } - - // Cache the compiled function - cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); - } - return cached; - }; - - function multipleContexts(selector, contexts, results) { - let i = 0; - const len = contexts.length; - for (; i < len; i++) { - Sizzle(selector, contexts[i], results); - } - return results; - } - - function select(selector, context, results, seed) { - let i; let tokens; let token; let type; let find; - const match = tokenize(selector); - - if (!seed) { - // Try to minimize operations if there is only one group - if (match.length === 1) { - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice(0); - if (tokens.length > 2 && (token = tokens[0]).type === 'ID' - && support.getById && context.nodeType === 9 && documentIsHTML - && Expr.relative[tokens[1].type]) { - context = (Expr.find.ID(token.matches[0].replace(runescape, funescape), context) || [])[0]; - if (!context) { - return results; - } - selector = selector.slice(tokens.shift().value.length); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr.needsContext.test(selector) ? 0 : tokens.length; - while (i--) { - token = tokens[i]; - - // Abort if we hit a combinator - if (Expr.relative[(type = token.type)]) { - break; - } - if ((find = Expr.find[type])) { - // Search, expanding context for leading sibling combinators - if ((seed = find( - token.matches[0].replace(runescape, funescape), - rsibling.test(tokens[0].type) && testContext(context.parentNode) || context, - ))) { - // If seed is empty or no tokens remain, we can return early - tokens.splice(i, 1); - selector = seed.length && toSelector(tokens); - if (!selector) { - push.apply(results, seed); - return results; - } - - break; - } - } - } - } - } - - // Compile and execute a filtering function - // Provide `match` to avoid retokenization if we modified the selector above - compile(selector, match)( - seed, - context, - !documentIsHTML, - results, - rsibling.test(selector) && testContext(context.parentNode) || context, - ); - return results; - } - - // One-time assignments - - // Sort stability - support.sortStable = expando.split('').sort(sortOrder).join('') === expando; - - // Support: Chrome<14 - // Always assume duplicates if they aren't passed to the comparison function - support.detectDuplicates = !!hasDuplicate; - - // Initialize against the default document - setDocument(); - - // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) - // Detached nodes confoundingly follow *each other* - support.sortDetached = assert((div1) => - // Should return 1, but returns 4 (following) - div1.compareDocumentPosition(document.createElement('div')) & 1); - - // Support: IE<8 - // Prevent attribute/property "interpolation" - // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx - if (!assert((div) => { - div.innerHTML = ""; - return div.firstChild.getAttribute('href') === '#'; - })) { - addHandle('type|href|height|width', (elem, name, isXML) => { - if (!isXML) { - return elem.getAttribute(name, name.toLowerCase() === 'type' ? 1 : 2); - } - }); - } - - // Support: IE<9 - // Use defaultValue in place of getAttribute("value") - if (!support.attributes || !assert((div) => { - div.innerHTML = ''; - div.firstChild.setAttribute('value', ''); - return div.firstChild.getAttribute('value') === ''; - })) { - addHandle('value', (elem, name, isXML) => { - if (!isXML && elem.nodeName.toLowerCase() === 'input') { - return elem.defaultValue; - } - }); - } - - // Support: IE<9 - // Use getAttributeNode to fetch booleans when getAttribute lies - if (!assert((div) => div.getAttribute('disabled') == null)) { - addHandle(booleans, (elem, name, isXML) => { - let val; - if (!isXML) { - return elem[name] === true ? name.toLowerCase() - : (val = elem.getAttributeNode(name)) && val.specified - ? val.value - : null; - } - }); - } - - return Sizzle; -}(window)); - - jQuery.find = Sizzle; - jQuery.expr = Sizzle.selectors; - jQuery.expr[':'] = jQuery.expr.pseudos; - jQuery.unique = Sizzle.uniqueSort; - jQuery.text = Sizzle.getText; - jQuery.isXMLDoc = Sizzle.isXML; - jQuery.contains = Sizzle.contains; - - const rneedsContext = jQuery.expr.match.needsContext; - - const rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); - - const risSimple = /^.[^:#\[\.,]*$/; - - // Implement the identical functionality for filter and not - function winnow(elements, qualifier, not) { - if (jQuery.isFunction(qualifier)) { - return jQuery.grep(elements, (elem, i) => - /* jshint -W018 */ - !!qualifier.call(elem, i, elem) !== not); - } - - if (qualifier.nodeType) { - return jQuery.grep(elements, (elem) => (elem === qualifier) !== not); - } - - if (typeof qualifier === 'string') { - if (risSimple.test(qualifier)) { - return jQuery.filter(qualifier, elements, not); - } - - qualifier = jQuery.filter(qualifier, elements); - } - - return jQuery.grep(elements, (elem) => (indexOf.call(qualifier, elem) >= 0) !== not); - } - - jQuery.filter = function (expr, elems, not) { - const elem = elems[0]; - - if (not) { - expr = `:not(${expr})`; - } - - return elems.length === 1 && elem.nodeType === 1 - ? jQuery.find.matchesSelector(elem, expr) ? [elem] : [] - : jQuery.find.matches(expr, jQuery.grep(elems, (elem) => elem.nodeType === 1)); - }; - - jQuery.fn.extend({ - find(selector) { - let i; - const len = this.length; - let ret = []; - const self = this; - - if (typeof selector !== 'string') { - return this.pushStack(jQuery(selector).filter(function () { - for (i = 0; i < len; i++) { - if (jQuery.contains(self[i], this)) { - return true; - } - } - })); - } - - for (i = 0; i < len; i++) { - jQuery.find(selector, self[i], ret); - } - - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack(len > 1 ? jQuery.unique(ret) : ret); - ret.selector = this.selector ? `${this.selector} ${selector}` : selector; - return ret; - }, - filter(selector) { - return this.pushStack(winnow(this, selector || [], false)); - }, - not(selector) { - return this.pushStack(winnow(this, selector || [], true)); - }, - is(selector) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === 'string' && rneedsContext.test(selector) - ? jQuery(selector) - : selector || [], - false, - ).length; - }, - }); - - // Initialize a jQuery object - - // A central reference to the root jQuery(document) - let rootjQuery; - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - const rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/; - - const init = jQuery.fn.init = function (selector, context) { - let match; let - elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if (!selector) { - return this; - } - - // Handle HTML strings - if (typeof selector === 'string') { - if (selector[0] === '<' && selector[selector.length - 1] === '>' && selector.length >= 3) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [null, selector, null]; - } else { - match = rquickExpr.exec(selector); - } - - // Match html or make sure no context is specified for #id - if (match && (match[1] || !context)) { - // HANDLE: $(html) -> $(array) - if (match[1]) { - context = context instanceof jQuery ? context[0] : context; - - // scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge(this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true, - )); - - // HANDLE: $(html, props) - if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { - for (match in context) { - // Properties of context are called as methods if possible - if (jQuery.isFunction(this[match])) { - this[match](context[match]); - - // ...and otherwise set as attributes - } else { - this.attr(match, context[match]); - } - } - } - - return this; - - // HANDLE: $(#id) - } - elem = document.getElementById(match[2]); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if (elem && elem.parentNode) { - // Inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - - // HANDLE: $(expr, $(...)) - } if (!context || context.jquery) { - return (context || rootjQuery).find(selector); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } - return this.constructor(context).find(selector); - - // HANDLE: $(DOMElement) - } if (selector.nodeType) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } if (jQuery.isFunction(selector)) { - return typeof rootjQuery.ready !== 'undefined' - ? rootjQuery.ready(selector) - // Execute immediately if ready is not present - : selector(jQuery); - } - - if (selector.selector !== undefined) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray(selector, this); - }; - - // Give the init function the jQuery prototype for later instantiation - init.prototype = jQuery.fn; - - // Initialize central reference - rootjQuery = jQuery(document); - - const rparentsprev = /^(?:parents|prev(?:Until|All))/; - // methods guaranteed to produce a unique set when starting from a unique set - const guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true, - }; - - jQuery.extend({ - dir(elem, dir, until) { - const matched = []; - const truncate = until !== undefined; - - while ((elem = elem[dir]) && elem.nodeType !== 9) { - if (elem.nodeType === 1) { - if (truncate && jQuery(elem).is(until)) { - break; - } - matched.push(elem); - } - } - return matched; - }, - - sibling(n, elem) { - const matched = []; - - for (; n; n = n.nextSibling) { - if (n.nodeType === 1 && n !== elem) { - matched.push(n); - } - } - - return matched; - }, - }); - - jQuery.fn.extend({ - has(target) { - const targets = jQuery(target, this); - const l = targets.length; - - return this.filter(function () { - let i = 0; - for (; i < l; i++) { - if (jQuery.contains(this, targets[i])) { - return true; - } - } - }); - }, - - closest(selectors, context) { - let cur; - let i = 0; - const l = this.length; - const matched = []; - const pos = rneedsContext.test(selectors) || typeof selectors !== 'string' - ? jQuery(selectors, context || this.context) - : 0; - - for (; i < l; i++) { - for (cur = this[i]; cur && cur !== context; cur = cur.parentNode) { - // Always skip document fragments - if (cur.nodeType < 11 && (pos - ? pos.index(cur) > -1 - - // Don't pass non-elements to Sizzle - : cur.nodeType === 1 - && jQuery.find.matchesSelector(cur, selectors))) { - matched.push(cur); - break; - } - } - } - - return this.pushStack(matched.length > 1 ? jQuery.unique(matched) : matched); - }, - - // Determine the position of an element within - // the matched set of elements - index(elem) { - // No argument, return index in parent - if (!elem) { - return (this[0] && this[0].parentNode) ? this.first().prevAll().length : -1; - } - - // index in selector - if (typeof elem === 'string') { - return indexOf.call(jQuery(elem), this[0]); - } - - // Locate the position of the desired element - return indexOf.call(this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem); - }, - - add(selector, context) { - return this.pushStack( - jQuery.unique( - jQuery.merge(this.get(), jQuery(selector, context)), - ), - ); - }, - - addBack(selector) { - return this.add(selector == null - ? this.prevObject : this.prevObject.filter(selector)); - }, - }); - - function sibling(cur, dir) { - while ((cur = cur[dir]) && cur.nodeType !== 1) {} - return cur; - } - - jQuery.each({ - parent(elem) { - const parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents(elem) { - return jQuery.dir(elem, 'parentNode'); - }, - parentsUntil(elem, i, until) { - return jQuery.dir(elem, 'parentNode', until); - }, - next(elem) { - return sibling(elem, 'nextSibling'); - }, - prev(elem) { - return sibling(elem, 'previousSibling'); - }, - nextAll(elem) { - return jQuery.dir(elem, 'nextSibling'); - }, - prevAll(elem) { - return jQuery.dir(elem, 'previousSibling'); - }, - nextUntil(elem, i, until) { - return jQuery.dir(elem, 'nextSibling', until); - }, - prevUntil(elem, i, until) { - return jQuery.dir(elem, 'previousSibling', until); - }, - siblings(elem) { - return jQuery.sibling((elem.parentNode || {}).firstChild, elem); - }, - children(elem) { - return jQuery.sibling(elem.firstChild); - }, - contents(elem) { - return elem.contentDocument || jQuery.merge([], elem.childNodes); - }, - }, (name, fn) => { - jQuery.fn[name] = function (until, selector) { - let matched = jQuery.map(this, fn, until); - - if (name.slice(-5) !== 'Until') { - selector = until; - } - - if (selector && typeof selector === 'string') { - matched = jQuery.filter(selector, matched); - } - - if (this.length > 1) { - // Remove duplicates - if (!guaranteedUnique[name]) { - jQuery.unique(matched); - } - - // Reverse order for parents* and prev-derivatives - if (rparentsprev.test(name)) { - matched.reverse(); - } - } - - return this.pushStack(matched); - }; - }); - const rnotwhite = (/\S+/g); - - // String to Object options format cache - const optionsCache = {}; - - // Convert String-formatted options into Object-formatted ones and store in cache - function createOptions(options) { - const object = optionsCache[options] = {}; - jQuery.each(options.match(rnotwhite) || [], (_, flag) => { - object[flag] = true; - }); - return object; - } - - /* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ - jQuery.Callbacks = function (options) { - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === 'string' - ? (optionsCache[options] || createOptions(options)) - : jQuery.extend({}, options); - - let // Last fire value (for non-forgettable lists) - memory; - // Flag to know if list was already fired - let fired; - // Flag to know if list is currently firing - let firing; - // First callback to fire (used internally by add and fireWith) - let firingStart; - // End of the loop when firing - let firingLength; - // Index of currently firing callback (modified by remove if needed) - let firingIndex; - // Actual callback list - let list = []; - // Stack of fire calls for repeatable lists - let stack = !options.once && []; - // Fire callbacks - var fire = function (data) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for (; list && firingIndex < firingLength; firingIndex++) { - if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if (list) { - if (stack) { - if (stack.length) { - fire(stack.shift()); - } - } else if (memory) { - list = []; - } else { - self.disable(); - } - } - }; - // Actual Callbacks object - var self = { - // Add a callback or a collection of callbacks to the list - add() { - if (list) { - // First, we save the current length - const start = list.length; - (function add(args) { - jQuery.each(args, (_, arg) => { - const type = jQuery.type(arg); - if (type === 'function') { - if (!options.unique || !self.has(arg)) { - list.push(arg); - } - } else if (arg && arg.length && type !== 'string') { - // Inspect recursively - add(arg); - } - }); - }(arguments)); - // Do we need to add the callbacks to the - // current firing batch? - if (firing) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if (memory) { - firingStart = start; - fire(memory); - } - } - return this; - }, - // Remove a callback from the list - remove() { - if (list) { - jQuery.each(arguments, (_, arg) => { - let index; - while ((index = jQuery.inArray(arg, list, index)) > -1) { - list.splice(index, 1); - // Handle firing indexes - if (firing) { - if (index <= firingLength) { - firingLength--; - } - if (index <= firingIndex) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has(fn) { - return fn ? jQuery.inArray(fn, list) > -1 : !!(list && list.length); - }, - // Remove all callbacks from the list - empty() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled() { - return !list; - }, - // Lock the list in its current state - lock() { - stack = undefined; - if (!memory) { - self.disable(); - } - return this; - }, - // Is it locked? - locked() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith(context, args) { - if (list && (!fired || stack)) { - args = args || []; - args = [context, args.slice ? args.slice() : args]; - if (firing) { - stack.push(args); - } else { - fire(args); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire() { - self.fireWith(this, arguments); - return this; - }, - // To know if the callbacks have already been called at least once - fired() { - return !!fired; - }, - }; - - return self; - }; - - jQuery.extend({ - - Deferred(func) { - const tuples = [ - // action, add listener, listener list, final state - ['resolve', 'done', jQuery.Callbacks('once memory'), 'resolved'], - ['reject', 'fail', jQuery.Callbacks('once memory'), 'rejected'], - ['notify', 'progress', jQuery.Callbacks('memory')], - ]; - let state = 'pending'; - var promise = { - state() { - return state; - }, - always() { - deferred.done(arguments).fail(arguments); - return this; - }, - then(/* fnDone, fnFail, fnProgress */) { - let fns = arguments; - return jQuery.Deferred((newDefer) => { - jQuery.each(tuples, (i, tuple) => { - const fn = jQuery.isFunction(fns[i]) && fns[i]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[tuple[1]](function () { - const returned = fn && fn.apply(this, arguments); - if (returned && jQuery.isFunction(returned.promise)) { - returned.promise() - .done(newDefer.resolve) - .fail(newDefer.reject) - .progress(newDefer.notify); - } else { - newDefer[`${tuple[0]}With`](this === promise ? newDefer.promise() : this, fn ? [returned] : arguments); - } - }); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise(obj) { - return obj != null ? jQuery.extend(obj, promise) : promise; - }, - }; - var deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each(tuples, (i, tuple) => { - const list = tuple[2]; - const stateString = tuple[3]; - - // promise[ done | fail | progress ] = list.add - promise[tuple[1]] = list.add; - - // Handle state - if (stateString) { - list.add(() => { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[i ^ 1][2].disable, tuples[2][2].lock); - } - - // deferred[ resolve | reject | notify ] - deferred[tuple[0]] = function () { - deferred[`${tuple[0]}With`](this === deferred ? promise : this, arguments); - return this; - }; - deferred[`${tuple[0]}With`] = list.fireWith; - }); - - // Make the deferred a promise - promise.promise(deferred); - - // Call given func if any - if (func) { - func.call(deferred, deferred); - } - - // All done! - return deferred; - }, - - // Deferred helper - when(subordinate /* , ..., subordinateN */) { - let i = 0; - const resolveValues = slice.call(arguments); - const { length } = resolveValues; - - // the count of uncompleted subordinates - let remaining = length !== 1 || (subordinate && jQuery.isFunction(subordinate.promise)) ? length : 0; - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - const deferred = remaining === 1 ? subordinate : jQuery.Deferred(); - - // Update function for both resolve and progress values - const updateFunc = function (i, contexts, values) { - return function (value) { - contexts[i] = this; - values[i] = arguments.length > 1 ? slice.call(arguments) : value; - if (values === progressValues) { - deferred.notifyWith(contexts, values); - } else if (!(--remaining)) { - deferred.resolveWith(contexts, values); - } - }; - }; - - let progressValues; let progressContexts; let - resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if (length > 1) { - progressValues = new Array(length); - progressContexts = new Array(length); - resolveContexts = new Array(length); - for (; i < length; i++) { - if (resolveValues[i] && jQuery.isFunction(resolveValues[i].promise)) { - resolveValues[i].promise() - .done(updateFunc(i, resolveContexts, resolveValues)) - .fail(deferred.reject) - .progress(updateFunc(i, progressContexts, progressValues)); - } else { - --remaining; - } - } - } - - // if we're not waiting on anything, resolve the master - if (!remaining) { - deferred.resolveWith(resolveContexts, resolveValues); - } - - return deferred.promise(); - }, - }); - - // The deferred used on DOM ready - let readyList; - - jQuery.fn.ready = function (fn) { - // Add the callback - jQuery.ready.promise().done(fn); - - return this; - }; - - jQuery.extend({ - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady(hold) { - if (hold) { - jQuery.readyWait++; - } else { - jQuery.ready(true); - } - }, - - // Handle when the DOM is ready - ready(wait) { - // Abort if there are pending holds or we're already ready - if (wait === true ? --jQuery.readyWait : jQuery.isReady) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if (wait !== true && --jQuery.readyWait > 0) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith(document, [jQuery]); - - // Trigger any bound ready events - if (jQuery.fn.trigger) { - jQuery(document).trigger('ready').off('ready'); - } - }, - }); - - /** - * The ready event handler and self cleanup method - */ - function completed() { - document.removeEventListener('DOMContentLoaded', completed, false); - window.removeEventListener('load', completed, false); - jQuery.ready(); - } - - jQuery.ready.promise = function (obj) { - if (!readyList) { - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if (document.readyState === 'complete') { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout(jQuery.ready); - } else { - // Use the handy event callback - document.addEventListener('DOMContentLoaded', completed, false); - - // A fallback to window.onload, that will always work - window.addEventListener('load', completed, false); - } - } - return readyList.promise(obj); - }; - - // Kick off the DOM ready check even if the user does not - jQuery.ready.promise(); - - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - const access = jQuery.access = function (elems, fn, key, value, chainable, emptyGet, raw) { - let i = 0; - const len = elems.length; - let bulk = key == null; - - // Sets many values - if (jQuery.type(key) === 'object') { - chainable = true; - for (i in key) { - jQuery.access(elems, fn, i, key[i], true, emptyGet, raw); - } - - // Sets one value - } else if (value !== undefined) { - chainable = true; - - if (!jQuery.isFunction(value)) { - raw = true; - } - - if (bulk) { - // Bulk operations run against the entire set - if (raw) { - fn.call(elems, value); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function (elem, key, value) { - return bulk.call(jQuery(elem), value); - }; - } - } - - if (fn) { - for (; i < len; i++) { - fn(elems[i], key, raw ? value : value.call(elems[i], i, fn(elems[i], key))); - } - } - } - - return chainable - ? elems - - // Gets - : bulk - ? fn.call(elems) - : len ? fn(elems[0], key) : emptyGet; - }; - - /** - * Determines whether an object can have data - */ - jQuery.acceptData = function (owner) { - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - /* jshint -W018 */ - return owner.nodeType === 1 || owner.nodeType === 9 || !(+owner.nodeType); - }; - - function Data() { - // Support: Android < 4, - // Old WebKit does not have Object.preventExtensions/freeze method, - // return new empty object instead with no [[set]] accessor - Object.defineProperty(this.cache = {}, 0, { - get() { - return {}; - }, - }); - - this.expando = jQuery.expando + Math.random(); - } - - Data.uid = 1; - Data.accepts = jQuery.acceptData; - - Data.prototype = { - key(owner) { - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return the key for a frozen object. - if (!Data.accepts(owner)) { - return 0; - } - - const descriptor = {}; - // Check if the owner object already has a cache key - let unlock = owner[this.expando]; - - // If not, create one - if (!unlock) { - unlock = Data.uid++; - - // Secure it in a non-enumerable, non-writable property - try { - descriptor[this.expando] = { value: unlock }; - Object.defineProperties(owner, descriptor); - - // Support: Android < 4 - // Fallback to a less secure definition - } catch (e) { - descriptor[this.expando] = unlock; - jQuery.extend(owner, descriptor); - } - } - - // Ensure the cache object - if (!this.cache[unlock]) { - this.cache[unlock] = {}; - } - - return unlock; - }, - set(owner, data, value) { - let prop; - // There may be an unlock assigned to this node, - // if there is no entry for this "owner", create one inline - // and set the unlock as though an owner entry had always existed - const unlock = this.key(owner); - const cache = this.cache[unlock]; - - // Handle: [ owner, key, value ] args - if (typeof data === 'string') { - cache[data] = value; - - // Handle: [ owner, { properties } ] args - } else { - // Fresh assignments by object are shallow copied - if (jQuery.isEmptyObject(cache)) { - jQuery.extend(this.cache[unlock], data); - // Otherwise, copy the properties one-by-one to the cache object - } else { - for (prop in data) { - cache[prop] = data[prop]; - } - } - } - return cache; - }, - get(owner, key) { - // Either a valid cache is found, or will be created. - // New caches will be created and the unlock returned, - // allowing direct access to the newly created - // empty data object. A valid owner object must be provided. - const cache = this.cache[this.key(owner)]; - - return key === undefined - ? cache : cache[key]; - }, - access(owner, key, value) { - let stored; - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if (key === undefined - || ((key && typeof key === 'string') && value === undefined)) { - stored = this.get(owner, key); - - return stored !== undefined - ? stored : this.get(owner, jQuery.camelCase(key)); - } - - // [*]When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set(owner, key, value); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove(owner, key) { - let i; let name; let camel; - const unlock = this.key(owner); - const cache = this.cache[unlock]; - - if (key === undefined) { - this.cache[unlock] = {}; - } else { - // Support array or space separated string of keys - if (jQuery.isArray(key)) { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = key.concat(key.map(jQuery.camelCase)); - } else { - camel = jQuery.camelCase(key); - // Try the string as a key before any manipulation - if (key in cache) { - name = [key, camel]; - } else { - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - name = camel; - name = name in cache - ? [name] : (name.match(rnotwhite) || []); - } - } - - i = name.length; - while (i--) { - delete cache[name[i]]; - } - } - }, - hasData(owner) { - return !jQuery.isEmptyObject( - this.cache[owner[this.expando]] || {}, - ); - }, - discard(owner) { - if (owner[this.expando]) { - delete this.cache[owner[this.expando]]; - } - }, - }; - const data_priv = new Data(); - - const data_user = new Data(); - - /* - Implementation Summary - - 1. Enforce API surface and semantic compatibility with 1.9.x branch - 2. Improve the module's maintainability by reducing the storage - paths to a single mechanism. - 3. Use the same single mechanism to support "private" and "user" data. - 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) - 5. Avoid exposing implementation details on user objects (eg. expando properties) - 6. Provide a clear path for implementation upgrade to WeakMap in 2014 -*/ - const rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/; - const rmultiDash = /([A-Z])/g; - - function dataAttr(elem, key, data) { - let name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if (data === undefined && elem.nodeType === 1) { - name = `data-${key.replace(rmultiDash, '-$1').toLowerCase()}`; - data = elem.getAttribute(name); - - if (typeof data === 'string') { - try { - data = data === 'true' ? true - : data === 'false' ? false - : data === 'null' ? null - // Only convert to a number if it doesn't change the string - : `${+data}` === data ? +data - : rbrace.test(data) ? jQuery.parseJSON(data) - : data; - } catch (e) {} - - // Make sure we set the data so it isn't changed later - data_user.set(elem, key, data); - } else { - data = undefined; - } - } - return data; - } - - jQuery.extend({ - hasData(elem) { - return data_user.hasData(elem) || data_priv.hasData(elem); - }, - - data(elem, name, data) { - return data_user.access(elem, name, data); - }, - - removeData(elem, name) { - data_user.remove(elem, name); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to data_priv methods, these can be deprecated. - _data(elem, name, data) { - return data_priv.access(elem, name, data); - }, - - _removeData(elem, name) { - data_priv.remove(elem, name); - }, - }); - - jQuery.fn.extend({ - data(key, value) { - let i; let name; let data; - const elem = this[0]; - const attrs = elem && elem.attributes; - - // Gets all values - if (key === undefined) { - if (this.length) { - data = data_user.get(elem); - - if (elem.nodeType === 1 && !data_priv.get(elem, 'hasDataAttrs')) { - i = attrs.length; - while (i--) { - name = attrs[i].name; - - if (name.indexOf('data-') === 0) { - name = jQuery.camelCase(name.slice(5)); - dataAttr(elem, name, data[name]); - } - } - data_priv.set(elem, 'hasDataAttrs', true); - } - } - - return data; - } - - // Sets multiple values - if (typeof key === 'object') { - return this.each(function () { - data_user.set(this, key); - }); - } - - return access(this, function (value) { - let data; - const camelKey = jQuery.camelCase(key); - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if (elem && value === undefined) { - // Attempt to get data from the cache - // with the key as-is - data = data_user.get(elem, key); - if (data !== undefined) { - return data; - } - - // Attempt to get data from the cache - // with the key camelized - data = data_user.get(elem, camelKey); - if (data !== undefined) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr(elem, camelKey, undefined); - if (data !== undefined) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each(function () { - // First, attempt to store a copy or reference of any - // data that might've been store with a camelCased key. - const data = data_user.get(this, camelKey); - - // For HTML5 data-* attribute interop, we have to - // store property names with dashes in a camelCase form. - // This might not apply to all properties...* - data_user.set(this, camelKey, value); - - // *... In the case of properties that might _actually_ - // have dashes, we need to also store a copy of that - // unchanged property. - if (key.indexOf('-') !== -1 && data !== undefined) { - data_user.set(this, key, value); - } - }); - }, null, value, arguments.length > 1, null, true); - }, - - removeData(key) { - return this.each(function () { - data_user.remove(this, key); - }); - }, - }); - - jQuery.extend({ - queue(elem, type, data) { - let queue; - - if (elem) { - type = `${type || 'fx'}queue`; - queue = data_priv.get(elem, type); - - // Speed up dequeue by getting out quickly if this is just a lookup - if (data) { - if (!queue || jQuery.isArray(data)) { - queue = data_priv.access(elem, type, jQuery.makeArray(data)); - } else { - queue.push(data); - } - } - return queue || []; - } - }, - - dequeue(elem, type) { - type = type || 'fx'; - - const queue = jQuery.queue(elem, type); - let startLength = queue.length; - let fn = queue.shift(); - const hooks = jQuery._queueHooks(elem, type); - const next = function () { - jQuery.dequeue(elem, type); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if (fn === 'inprogress') { - fn = queue.shift(); - startLength--; - } - - if (fn) { - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if (type === 'fx') { - queue.unshift('inprogress'); - } - - // clear up the last queue stop function - delete hooks.stop; - fn.call(elem, next, hooks); - } - - if (!startLength && hooks) { - hooks.empty.fire(); - } - }, - - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks(elem, type) { - const key = `${type}queueHooks`; - return data_priv.get(elem, key) || data_priv.access(elem, key, { - empty: jQuery.Callbacks('once memory').add(() => { - data_priv.remove(elem, [`${type}queue`, key]); - }), - }); - }, - }); - - jQuery.fn.extend({ - queue(type, data) { - let setter = 2; - - if (typeof type !== 'string') { - data = type; - type = 'fx'; - setter--; - } - - if (arguments.length < setter) { - return jQuery.queue(this[0], type); - } - - return data === undefined - ? this - : this.each(function () { - const queue = jQuery.queue(this, type, data); - - // ensure a hooks for this queue - jQuery._queueHooks(this, type); - - if (type === 'fx' && queue[0] !== 'inprogress') { - jQuery.dequeue(this, type); - } - }); - }, - dequeue(type) { - return this.each(function () { - jQuery.dequeue(this, type); - }); - }, - clearQueue(type) { - return this.queue(type || 'fx', []); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise(type, obj) { - let tmp; - let count = 1; - const defer = jQuery.Deferred(); - const elements = this; - let i = this.length; - const resolve = function () { - if (!(--count)) { - defer.resolveWith(elements, [elements]); - } - }; - - if (typeof type !== 'string') { - obj = type; - type = undefined; - } - type = type || 'fx'; - - while (i--) { - tmp = data_priv.get(elements[i], `${type}queueHooks`); - if (tmp && tmp.empty) { - count++; - tmp.empty.add(resolve); - } - } - resolve(); - return defer.promise(obj); - }, - }); - const pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; - - const cssExpand = ['Top', 'Right', 'Bottom', 'Left']; - - const isHidden = function (elem, el) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css(elem, 'display') === 'none' || !jQuery.contains(elem.ownerDocument, elem); - }; - - const rcheckableType = (/^(?:checkbox|radio)$/i); - - (function () { - const fragment = document.createDocumentFragment(); - const div = fragment.appendChild(document.createElement('div')); - - // #11217 - WebKit loses check when the name is after the checked attribute - div.innerHTML = ""; - - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode(true).cloneNode(true).lastChild.checked; - - // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE9-IE11+ - div.innerHTML = ''; - support.noCloneChecked = !!div.cloneNode(true).lastChild.defaultValue; - }()); - const strundefined = typeof undefined; - - support.focusinBubbles = 'onfocusin' in window; - - const - rkeyEvent = /^key/; - const rmouseEvent = /^(?:mouse|contextmenu)|click/; - const rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; - const rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; - - function returnTrue() { - return true; - } - - function returnFalse() { - return false; - } - - function safeActiveElement() { - try { - return document.activeElement; - } catch (err) { } - } - - /* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ - jQuery.event = { - - global: {}, - - add(elem, types, handler, data, selector) { - let handleObjIn; let eventHandle; let tmp; - let events; let t; let handleObj; - let special; let handlers; let type; let namespaces; let origType; - const elemData = data_priv.get(elem); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if (!elemData) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if (handler.handler) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if (!handler.guid) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if (!(events = elemData.events)) { - events = elemData.events = {}; - } - if (!(eventHandle = elemData.handle)) { - eventHandle = elemData.handle = function (e) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type - ? jQuery.event.dispatch.apply(elem, arguments) : undefined; - }; - } - - // Handle multiple events separated by a space - types = (types || '').match(rnotwhite) || ['']; - t = types.length; - while (t--) { - tmp = rtypenamespace.exec(types[t]) || []; - type = origType = tmp[1]; - namespaces = (tmp[2] || '').split('.').sort(); - - // There *must* be a type, no attaching namespace-only handlers - if (!type) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[type] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = (selector ? special.delegateType : special.bindType) || type; - - // Update special based on newly reset type - special = jQuery.event.special[type] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type, - origType, - data, - handler, - guid: handler.guid, - selector, - needsContext: selector && jQuery.expr.match.needsContext.test(selector), - namespace: namespaces.join('.'), - }, handleObjIn); - - // Init the event handler queue if we're the first - if (!(handlers = events[type])) { - handlers = events[type] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) { - if (elem.addEventListener) { - elem.addEventListener(type, eventHandle, false); - } - } - } - - if (special.add) { - special.add.call(elem, handleObj); - - if (!handleObj.handler.guid) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if (selector) { - handlers.splice(handlers.delegateCount++, 0, handleObj); - } else { - handlers.push(handleObj); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[type] = true; - } - }, - - // Detach an event or set of events from an element - remove(elem, types, handler, selector, mappedTypes) { - let j; let origCount; let tmp; - let events; let t; let handleObj; - let special; let handlers; let type; let namespaces; let origType; - const elemData = data_priv.hasData(elem) && data_priv.get(elem); - - if (!elemData || !(events = elemData.events)) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = (types || '').match(rnotwhite) || ['']; - t = types.length; - while (t--) { - tmp = rtypenamespace.exec(types[t]) || []; - type = origType = tmp[1]; - namespaces = (tmp[2] || '').split('.').sort(); - - // Unbind all events (on this namespace, if provided) for the element - if (!type) { - for (type in events) { - jQuery.event.remove(elem, type + types[t], handler, selector, true); - } - continue; - } - - special = jQuery.event.special[type] || {}; - type = (selector ? special.delegateType : special.bindType) || type; - handlers = events[type] || []; - tmp = tmp[2] && new RegExp(`(^|\\.)${namespaces.join('\\.(?:.*\\.|)')}(\\.|$)`); - - // Remove matching events - origCount = j = handlers.length; - while (j--) { - handleObj = handlers[j]; - - if ((mappedTypes || origType === handleObj.origType) - && (!handler || handler.guid === handleObj.guid) - && (!tmp || tmp.test(handleObj.namespace)) - && (!selector || selector === handleObj.selector || selector === '**' && handleObj.selector)) { - handlers.splice(j, 1); - - if (handleObj.selector) { - handlers.delegateCount--; - } - if (special.remove) { - special.remove.call(elem, handleObj); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if (origCount && !handlers.length) { - if (!special.teardown || special.teardown.call(elem, namespaces, elemData.handle) === false) { - jQuery.removeEvent(elem, type, elemData.handle); - } - - delete events[type]; - } - } - - // Remove the expando if it's no longer used - if (jQuery.isEmptyObject(events)) { - delete elemData.handle; - data_priv.remove(elem, 'events'); - } - }, - - trigger(event, data, elem, onlyHandlers) { - let i; let cur; let tmp; let bubbleType; let ontype; let handle; let special; - const eventPath = [elem || document]; - let type = hasOwn.call(event, 'type') ? event.type : event; - let namespaces = hasOwn.call(event, 'namespace') ? event.namespace.split('.') : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if (elem.nodeType === 3 || elem.nodeType === 8) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if (rfocusMorph.test(type + jQuery.event.triggered)) { - return; - } - - if (type.indexOf('.') >= 0) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split('.'); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(':') < 0 && `on${type}`; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[jQuery.expando] - ? event - : new jQuery.Event(type, typeof event === 'object' && event); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join('.'); - event.namespace_re = event.namespace - ? new RegExp(`(^|\\.)${namespaces.join('\\.(?:.*\\.|)')}(\\.|$)`) - : null; - - // Clean up the event in case it is being reused - event.result = undefined; - if (!event.target) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null - ? [event] - : jQuery.makeArray(data, [event]); - - // Allow special events to draw outside the lines - special = jQuery.event.special[type] || {}; - if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if (!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)) { - bubbleType = special.delegateType || type; - if (!rfocusMorph.test(bubbleType + type)) { - cur = cur.parentNode; - } - for (; cur; cur = cur.parentNode) { - eventPath.push(cur); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if (tmp === (elem.ownerDocument || document)) { - eventPath.push(tmp.defaultView || tmp.parentWindow || window); - } - } - - // Fire handlers on the event path - i = 0; - while ((cur = eventPath[i++]) && !event.isPropagationStopped()) { - event.type = i > 1 - ? bubbleType - : special.bindType || type; - - // jQuery handler - handle = (data_priv.get(cur, 'events') || {})[event.type] && data_priv.get(cur, 'handle'); - if (handle) { - handle.apply(cur, data); - } - - // Native handler - handle = ontype && cur[ontype]; - if (handle && handle.apply && jQuery.acceptData(cur)) { - event.result = handle.apply(cur, data); - if (event.result === false) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if (!onlyHandlers && !event.isDefaultPrevented()) { - if ((!special._default || special._default.apply(eventPath.pop(), data) === false) - && jQuery.acceptData(elem)) { - // Call a native DOM method on the target with the same name name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if (ontype && jQuery.isFunction(elem[type]) && !jQuery.isWindow(elem)) { - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ontype]; - - if (tmp) { - elem[ontype] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[type](); - jQuery.event.triggered = undefined; - - if (tmp) { - elem[ontype] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch(event) { - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix(event); - - let i; let j; let ret; let matched; let handleObj; - let handlerQueue = []; - const args = slice.call(arguments); - const handlers = (data_priv.get(this, 'events') || {})[event.type] || []; - const special = jQuery.event.special[event.type] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if (special.preDispatch && special.preDispatch.call(this, event) === false) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call(this, event, handlers); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) { - event.currentTarget = matched.elem; - - j = 0; - while ((handleObj = matched.handlers[j++]) && !event.isImmediatePropagationStopped()) { - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if (!event.namespace_re || event.namespace_re.test(handleObj.namespace)) { - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ((jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler) - .apply(matched.elem, args); - - if (ret !== undefined) { - if ((event.result = ret) === false) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if (special.postDispatch) { - special.postDispatch.call(this, event); - } - - return event.result; - }, - - handlers(event, handlers) { - let i; let matches; let sel; let handleObj; - const handlerQueue = []; - const { delegateCount } = handlers; - let cur = event.target; - - // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if (delegateCount && cur.nodeType && (!event.button || event.type !== 'click')) { - for (; cur !== this; cur = cur.parentNode || this) { - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if (cur.disabled !== true || event.type !== 'click') { - matches = []; - for (i = 0; i < delegateCount; i++) { - handleObj = handlers[i]; - - // Don't conflict with Object.prototype properties (#13203) - sel = `${handleObj.selector} `; - - if (matches[sel] === undefined) { - matches[sel] = handleObj.needsContext - ? jQuery(sel, this).index(cur) >= 0 - : jQuery.find(sel, this, null, [cur]).length; - } - if (matches[sel]) { - matches.push(handleObj); - } - } - if (matches.length) { - handlerQueue.push({ elem: cur, handlers: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if (delegateCount < handlers.length) { - handlerQueue.push({ elem: this, handlers: handlers.slice(delegateCount) }); - } - - return handlerQueue; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - props: 'altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which'.split(' '), - - fixHooks: {}, - - keyHooks: { - props: 'char charCode key keyCode'.split(' '), - filter(event, original) { - // Add which for key events - if (event.which == null) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - }, - }, - - mouseHooks: { - props: 'button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement'.split(' '), - filter(event, original) { - let eventDoc; let doc; let body; - const { button } = original; - - // Calculate pageX/Y if missing and clientX/Y available - if (event.pageX == null && original.clientX != null) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); - event.pageY = original.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if (!event.which && button !== undefined) { - event.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0))); - } - - return event; - }, - }, - - fix(event) { - if (event[jQuery.expando]) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - let i; let prop; let copy; - const { type } = event; - const originalEvent = event; - let fixHook = this.fixHooks[type]; - - if (!fixHook) { - this.fixHooks[type] = fixHook = rmouseEvent.test(type) ? this.mouseHooks - : rkeyEvent.test(type) ? this.keyHooks - : {}; - } - copy = fixHook.props ? this.props.concat(fixHook.props) : this.props; - - event = new jQuery.Event(originalEvent); - - i = copy.length; - while (i--) { - prop = copy[i]; - event[prop] = originalEvent[prop]; - } - - // Support: Cordova 2.5 (WebKit) (#13255) - // All events should have a target; Cordova deviceready doesn't - if (!event.target) { - event.target = document; - } - - // Support: Safari 6.0+, Chrome < 28 - // Target should not be a text node (#504, #13143) - if (event.target.nodeType === 3) { - event.target = event.target.parentNode; - } - - return fixHook.filter ? fixHook.filter(event, originalEvent) : event; - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true, - }, - focus: { - // Fire native event if possible so blur/focus sequence is correct - trigger() { - if (this !== safeActiveElement() && this.focus) { - this.focus(); - return false; - } - }, - delegateType: 'focusin', - }, - blur: { - trigger() { - if (this === safeActiveElement() && this.blur) { - this.blur(); - return false; - } - }, - delegateType: 'focusout', - }, - click: { - // For checkbox, fire native event so checked state will be right - trigger() { - if (this.type === 'checkbox' && this.click && jQuery.nodeName(this, 'input')) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default(event) { - return jQuery.nodeName(event.target, 'a'); - }, - }, - - beforeunload: { - postDispatch(event) { - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if (event.result !== undefined) { - event.originalEvent.returnValue = event.result; - } - }, - }, - }, - - simulate(type, elem, event, bubble) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - const e = jQuery.extend( - new jQuery.Event(), - event, - { - type, - isSimulated: true, - originalEvent: {}, - }, - ); - if (bubble) { - jQuery.event.trigger(e, null, elem); - } else { - jQuery.event.dispatch.call(elem, e); - } - if (e.isDefaultPrevented()) { - event.preventDefault(); - } - }, - }; - - jQuery.removeEvent = function (elem, type, handle) { - if (elem.removeEventListener) { - elem.removeEventListener(type, handle, false); - } - }; - - jQuery.Event = function (src, props) { - // Allow instantiation without the 'new' keyword - if (!(this instanceof jQuery.Event)) { - return new jQuery.Event(src, props); - } - - // Event object - if (src && src.type) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented - // Support: Android < 4.0 - || src.defaultPrevented === undefined - && src.getPreventDefault && src.getPreventDefault() - ? returnTrue - : returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if (props) { - jQuery.extend(this, props); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[jQuery.expando] = true; - }; - - // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding - // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html - jQuery.Event.prototype = { - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - - preventDefault() { - const e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if (e && e.preventDefault) { - e.preventDefault(); - } - }, - stopPropagation() { - const e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if (e && e.stopPropagation) { - e.stopPropagation(); - } - }, - stopImmediatePropagation() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - }; - - // Create mouseenter/leave events using mouseover/out and event-time checks - // Support: Chrome 15+ - jQuery.each({ - mouseenter: 'mouseover', - mouseleave: 'mouseout', - }, (orig, fix) => { - jQuery.event.special[orig] = { - delegateType: fix, - bindType: fix, - - handle(event) { - let ret; - const target = this; - const related = event.relatedTarget; - const { handleObj } = event; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if (!related || (related !== target && !jQuery.contains(target, related))) { - event.type = handleObj.origType; - ret = handleObj.handler.apply(this, arguments); - event.type = fix; - } - return ret; - }, - }; - }); - - // Create "bubbling" focus and blur events - // Support: Firefox, Chrome, Safari - if (!support.focusinBubbles) { - jQuery.each({ focus: 'focusin', blur: 'focusout' }, (orig, fix) => { - // Attach a single capturing handler on the document while someone wants focusin/focusout - const handler = function (event) { - jQuery.event.simulate(fix, event.target, jQuery.event.fix(event), true); - }; - - jQuery.event.special[fix] = { - setup() { - const doc = this.ownerDocument || this; - const attaches = data_priv.access(doc, fix); - - if (!attaches) { - doc.addEventListener(orig, handler, true); - } - data_priv.access(doc, fix, (attaches || 0) + 1); - }, - teardown() { - const doc = this.ownerDocument || this; - const attaches = data_priv.access(doc, fix) - 1; - - if (!attaches) { - doc.removeEventListener(orig, handler, true); - data_priv.remove(doc, fix); - } else { - data_priv.access(doc, fix, attaches); - } - }, - }; - }); - } - - jQuery.fn.extend({ - - on(types, selector, data, fn, /* INTERNAL */ one) { - let origFn; let - type; - - // Types can be a map of types/handlers - if (typeof types === 'object') { - // ( types-Object, selector, data ) - if (typeof selector !== 'string') { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for (type in types) { - this.on(type, selector, data, types[type], one); - } - return this; - } - - if (data == null && fn == null) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if (fn == null) { - if (typeof selector === 'string') { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if (fn === false) { - fn = returnFalse; - } else if (!fn) { - return this; - } - - if (one === 1) { - origFn = fn; - fn = function (event) { - // Can use an empty set, since event contains the info - jQuery().off(event); - return origFn.apply(this, arguments); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || (origFn.guid = jQuery.guid++); - } - return this.each(function () { - jQuery.event.add(this, types, fn, data, selector); - }); - }, - one(types, selector, data, fn) { - return this.on(types, selector, data, fn, 1); - }, - off(types, selector, fn) { - let handleObj; let - type; - if (types && types.preventDefault && types.handleObj) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery(types.delegateTarget).off( - handleObj.namespace ? `${handleObj.origType}.${handleObj.namespace}` : handleObj.origType, - handleObj.selector, - handleObj.handler, - ); - return this; - } - if (typeof types === 'object') { - // ( types-object [, selector] ) - for (type in types) { - this.off(type, selector, types[type]); - } - return this; - } - if (selector === false || typeof selector === 'function') { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if (fn === false) { - fn = returnFalse; - } - return this.each(function () { - jQuery.event.remove(this, types, fn, selector); - }); - }, - - trigger(type, data) { - return this.each(function () { - jQuery.event.trigger(type, data, this); - }); - }, - triggerHandler(type, data) { - const elem = this[0]; - if (elem) { - return jQuery.event.trigger(type, data, elem, true); - } - }, - }); - - const - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi; - const rtagName = /<([\w:]+)/; - const rhtml = /<|&#?\w+;/; - const rnoInnerhtml = /<(?:script|style|link)/i; - // checked="checked" or checked - const rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i; - const rscriptType = /^$|\/(?:java|ecma)script/i; - const rscriptTypeMasked = /^true\/(.*)/; - const rcleanScript = /^\s*\s*$/g; - - // We have to close these tags to support XHTML (#13200) - const wrapMap = { - - // Support: IE 9 - option: [1, "'], - - thead: [1, '', '
'], - col: [2, '', '
'], - tr: [2, '', '
'], - td: [3, '', '
'], - - _default: [0, '', ''], - }; - - // Support: IE 9 - wrapMap.optgroup = wrapMap.option; - - wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; - wrapMap.th = wrapMap.td; - - // Support: 1.x compatibility - // Manipulating tables requires a tbody - function manipulationTarget(elem, content) { - return jQuery.nodeName(elem, 'table') - && jQuery.nodeName(content.nodeType !== 11 ? content : content.firstChild, 'tr') - - ? elem.getElementsByTagName('tbody')[0] - || elem.appendChild(elem.ownerDocument.createElement('tbody')) - : elem; - } - - // Replace/restore the type attribute of script elements for safe DOM manipulation - function disableScript(elem) { - elem.type = `${elem.getAttribute('type') !== null}/${elem.type}`; - return elem; - } - function restoreScript(elem) { - const match = rscriptTypeMasked.exec(elem.type); - - if (match) { - elem.type = match[1]; - } else { - elem.removeAttribute('type'); - } - - return elem; - } - - // Mark scripts as having already been evaluated - function setGlobalEval(elems, refElements) { - let i = 0; - const l = elems.length; - - for (; i < l; i++) { - data_priv.set( - elems[i], 'globalEval', !refElements || data_priv.get(refElements[i], 'globalEval'), - ); - } - } - - function cloneCopyEvent(src, dest) { - let i; let l; let type; let pdataOld; let pdataCur; let udataOld; let udataCur; let - events; - - if (dest.nodeType !== 1) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if (data_priv.hasData(src)) { - pdataOld = data_priv.access(src); - pdataCur = data_priv.set(dest, pdataOld); - events = pdataOld.events; - - if (events) { - delete pdataCur.handle; - pdataCur.events = {}; - - for (type in events) { - for (i = 0, l = events[type].length; i < l; i++) { - jQuery.event.add(dest, type, events[type][i]); - } - } - } - } - - // 2. Copy user data - if (data_user.hasData(src)) { - udataOld = data_user.access(src); - udataCur = jQuery.extend({}, udataOld); - - data_user.set(dest, udataCur); - } - } - - function getAll(context, tag) { - const ret = context.getElementsByTagName ? context.getElementsByTagName(tag || '*') - : context.querySelectorAll ? context.querySelectorAll(tag || '*') - : []; - - return tag === undefined || tag && jQuery.nodeName(context, tag) - ? jQuery.merge([context], ret) - : ret; - } - - // Support: IE >= 9 - function fixInput(src, dest) { - const nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if (nodeName === 'input' && rcheckableType.test(src.type)) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if (nodeName === 'input' || nodeName === 'textarea') { - dest.defaultValue = src.defaultValue; - } - } - - jQuery.extend({ - clone(elem, dataAndEvents, deepDataAndEvents) { - let i; let l; let srcElements; let destElements; - const clone = elem.cloneNode(true); - const inPage = jQuery.contains(elem.ownerDocument, elem); - - // Support: IE >= 9 - // Fix Cloning issues - if (!support.noCloneChecked && (elem.nodeType === 1 || elem.nodeType === 11) - && !jQuery.isXMLDoc(elem)) { - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll(clone); - srcElements = getAll(elem); - - for (i = 0, l = srcElements.length; i < l; i++) { - fixInput(srcElements[i], destElements[i]); - } - } - - // Copy the events from the original to the clone - if (dataAndEvents) { - if (deepDataAndEvents) { - srcElements = srcElements || getAll(elem); - destElements = destElements || getAll(clone); - - for (i = 0, l = srcElements.length; i < l; i++) { - cloneCopyEvent(srcElements[i], destElements[i]); - } - } else { - cloneCopyEvent(elem, clone); - } - } - - // Preserve script evaluation history - destElements = getAll(clone, 'script'); - if (destElements.length > 0) { - setGlobalEval(destElements, !inPage && getAll(elem, 'script')); - } - - // Return the cloned set - return clone; - }, - - buildFragment(elems, context, scripts, selection) { - let elem; let tmp; let tag; let wrap; let contains; let j; - const fragment = context.createDocumentFragment(); - const nodes = []; - let i = 0; - const l = elems.length; - - for (; i < l; i++) { - elem = elems[i]; - - if (elem || elem === 0) { - // Add nodes directly - if (jQuery.type(elem) === 'object') { - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge(nodes, elem.nodeType ? [elem] : elem); - - // Convert non-html into a text node - } else if (!rhtml.test(elem)) { - nodes.push(context.createTextNode(elem)); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild(context.createElement('div')); - - // Deserialize a standard representation - tag = (rtagName.exec(elem) || ['', ''])[1].toLowerCase(); - wrap = wrapMap[tag] || wrapMap._default; - tmp.innerHTML = wrap[1] + elem.replace(rxhtmlTag, '<$1>') + wrap[2]; - - // Descend through wrappers to the right content - j = wrap[0]; - while (j--) { - tmp = tmp.lastChild; - } - - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge(nodes, tmp.childNodes); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Fixes #12346 - // Support: Webkit, IE - tmp.textContent = ''; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ''; - - i = 0; - while ((elem = nodes[i++])) { - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if (selection && jQuery.inArray(elem, selection) !== -1) { - continue; - } - - contains = jQuery.contains(elem.ownerDocument, elem); - - // Append to fragment - tmp = getAll(fragment.appendChild(elem), 'script'); - - // Preserve script evaluation history - if (contains) { - setGlobalEval(tmp); - } - - // Capture executables - if (scripts) { - j = 0; - while ((elem = tmp[j++])) { - if (rscriptType.test(elem.type || '')) { - scripts.push(elem); - } - } - } - } - - return fragment; - }, - - cleanData(elems) { - let data; let elem; let events; let type; let key; let j; - const { special } = jQuery.event; - let i = 0; - - for (; (elem = elems[i]) !== undefined; i++) { - if (jQuery.acceptData(elem)) { - key = elem[data_priv.expando]; - - if (key && (data = data_priv.cache[key])) { - events = Object.keys(data.events || {}); - if (events.length) { - for (j = 0; (type = events[j]) !== undefined; j++) { - if (special[type]) { - jQuery.event.remove(elem, type); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent(elem, type, data.handle); - } - } - } - if (data_priv.cache[key]) { - // Discard any remaining `private` data - delete data_priv.cache[key]; - } - } - } - // Discard any remaining `user` data - delete data_user.cache[elem[data_user.expando]]; - } - }, - }); - - jQuery.fn.extend({ - text(value) { - return access(this, function (value) { - return value === undefined - ? jQuery.text(this) - : this.empty().each(function () { - if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) { - this.textContent = value; - } - }); - }, null, value, arguments.length); - }, - - append() { - return this.domManip(arguments, function (elem) { - if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) { - const target = manipulationTarget(this, elem); - target.appendChild(elem); - } - }); - }, - - prepend() { - return this.domManip(arguments, function (elem) { - if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) { - const target = manipulationTarget(this, elem); - target.insertBefore(elem, target.firstChild); - } - }); - }, - - before() { - return this.domManip(arguments, function (elem) { - if (this.parentNode) { - this.parentNode.insertBefore(elem, this); - } - }); - }, - - after() { - return this.domManip(arguments, function (elem) { - if (this.parentNode) { - this.parentNode.insertBefore(elem, this.nextSibling); - } - }); - }, - - remove(selector, keepData /* Internal Use Only */) { - let elem; - const elems = selector ? jQuery.filter(selector, this) : this; - let i = 0; - - for (; (elem = elems[i]) != null; i++) { - if (!keepData && elem.nodeType === 1) { - jQuery.cleanData(getAll(elem)); - } - - if (elem.parentNode) { - if (keepData && jQuery.contains(elem.ownerDocument, elem)) { - setGlobalEval(getAll(elem, 'script')); - } - elem.parentNode.removeChild(elem); - } - } - - return this; - }, - - empty() { - let elem; - let i = 0; - - for (; (elem = this[i]) != null; i++) { - if (elem.nodeType === 1) { - // Prevent memory leaks - jQuery.cleanData(getAll(elem, false)); - - // Remove any remaining nodes - elem.textContent = ''; - } - } - - return this; - }, - - clone(dataAndEvents, deepDataAndEvents) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map(function () { - return jQuery.clone(this, dataAndEvents, deepDataAndEvents); - }); - }, - - html(value) { - return access(this, function (value) { - let elem = this[0] || {}; - let i = 0; - const l = this.length; - - if (value === undefined && elem.nodeType === 1) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if (typeof value === 'string' && !rnoInnerhtml.test(value) - && !wrapMap[(rtagName.exec(value) || ['', ''])[1].toLowerCase()]) { - value = value.replace(rxhtmlTag, '<$1>'); - - try { - for (; i < l; i++) { - elem = this[i] || {}; - - // Remove element nodes and prevent memory leaks - if (elem.nodeType === 1) { - jQuery.cleanData(getAll(elem, false)); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch (e) {} - } - - if (elem) { - this.empty().append(value); - } - }, null, value, arguments.length); - }, - - replaceWith() { - let arg = arguments[0]; - - // Make the changes, replacing each context element with the new content - this.domManip(arguments, function (elem) { - arg = this.parentNode; - - jQuery.cleanData(getAll(this)); - - if (arg) { - arg.replaceChild(elem, this); - } - }); - - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, - - detach(selector) { - return this.remove(selector, true); - }, - - domManip(args, callback) { - // Flatten any nested arrays - args = concat.apply([], args); - - let fragment; let first; let scripts; let hasScripts; let node; let doc; - let i = 0; - const l = this.length; - const set = this; - const iNoClone = l - 1; - const value = args[0]; - const isFunction = jQuery.isFunction(value); - - // We can't cloneNode fragments that contain checked, in WebKit - if (isFunction - || (l > 1 && typeof value === 'string' - && !support.checkClone && rchecked.test(value))) { - return this.each(function (index) { - const self = set.eq(index); - if (isFunction) { - args[0] = value.call(this, index, self.html()); - } - self.domManip(args, callback); - }); - } - - if (l) { - fragment = jQuery.buildFragment(args, this[0].ownerDocument, false, this); - first = fragment.firstChild; - - if (fragment.childNodes.length === 1) { - fragment = first; - } - - if (first) { - scripts = jQuery.map(getAll(fragment, 'script'), disableScript); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for (; i < l; i++) { - node = fragment; - - if (i !== iNoClone) { - node = jQuery.clone(node, true, true); - - // Keep references to cloned scripts for later restoration - if (hasScripts) { - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge(scripts, getAll(node, 'script')); - } - } - - callback.call(this[i], node, i); - } - - if (hasScripts) { - doc = scripts[scripts.length - 1].ownerDocument; - - // Reenable scripts - jQuery.map(scripts, restoreScript); - - // Evaluate executable scripts on first document insertion - for (i = 0; i < hasScripts; i++) { - node = scripts[i]; - if (rscriptType.test(node.type || '') - && !data_priv.access(node, 'globalEval') && jQuery.contains(doc, node)) { - if (node.src) { - // Optional AJAX dependency, but won't run scripts if not present - if (jQuery._evalUrl) { - jQuery._evalUrl(node.src); - } - } else { - jQuery.globalEval(node.textContent.replace(rcleanScript, '')); - } - } - } - } - } - } - - return this; - }, - }); - - jQuery.each({ - appendTo: 'append', - prependTo: 'prepend', - insertBefore: 'before', - insertAfter: 'after', - replaceAll: 'replaceWith', - }, (name, original) => { - jQuery.fn[name] = function (selector) { - let elems; - const ret = []; - const insert = jQuery(selector); - const last = insert.length - 1; - let i = 0; - - for (; i <= last; i++) { - elems = i === last ? this : this.clone(true); - jQuery(insert[i])[original](elems); - - // Support: QtWebKit - // .get() because push.apply(_, arraylike) throws - push.apply(ret, elems.get()); - } - - return this.pushStack(ret); - }; - }); - - let iframe; - const elemdisplay = {}; - - /** - * Retrieve the actual display of a element - * @param {String} name nodeName of the element - * @param {Object} doc Document object - */ - // Called only from within defaultDisplay - function actualDisplay(name, doc) { - const elem = jQuery(doc.createElement(name)).appendTo(doc.body); - - // getDefaultComputedStyle might be reliably used only on attached element - const display = window.getDefaultComputedStyle - - // Use of this method is a temporary fix (more like optmization) until something better comes along, - // since it was removed from specification and supported only in FF - ? window.getDefaultComputedStyle(elem[0]).display : jQuery.css(elem[0], 'display'); - - // We don't have any data stored on the element, - // so use "detach" method as fast way to get rid of the element - elem.detach(); - - return display; - } - - /** - * Try to determine the default display value of an element - * @param {String} nodeName - */ - function defaultDisplay(nodeName) { - let doc = document; - let display = elemdisplay[nodeName]; - - if (!display) { - display = actualDisplay(nodeName, doc); - - // If the simple way fails, read from inside an iframe - if (display === 'none' || !display) { - // Use the already-created iframe if possible - iframe = (iframe || jQuery("