UNPKG

sprotty

Version:

A next-gen framework for graphical views

217 lines 7.99 kB
"use strict"; /******************************************************************************** * Copyright (c) 2017-2018 TypeFox and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the Eclipse * Public License v. 2.0 are satisfied: GNU General Public License, version 2 * with the GNU Classpath Exception which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ Object.defineProperty(exports, "__esModule", { value: true }); exports.ModelIndexImpl = exports.createRandomId = exports.SModelRootImpl = exports.SChildElementImpl = exports.SParentElementImpl = exports.isParent = exports.SModelElementImpl = void 0; const geometry_1 = require("sprotty-protocol/lib/utils/geometry"); const iterable_1 = require("../../utils/iterable"); /** * Base class for all elements of the internal diagram model. * Each model element must have a unique ID and a type that is used to look up its view. */ class SModelElementImpl { get root() { let current = this; while (current) { if (current instanceof SModelRootImpl) return current; else if (current instanceof SChildElementImpl) current = current.parent; else current = undefined; } throw new Error('Element has no root'); } get index() { return this.root.index; } /** * A feature is a symbol identifying some functionality that can be enabled or disabled for * a model element. The set of supported features is determined by the `features` property. */ hasFeature(feature) { return this.features !== undefined && this.features.has(feature); } } exports.SModelElementImpl = SModelElementImpl; function isParent(element) { const children = element.children; return children !== undefined && children.constructor === Array; } exports.isParent = isParent; /** * A parent element may contain child elements, thus the diagram model forms a tree. */ class SParentElementImpl extends SModelElementImpl { constructor() { super(...arguments); this.children = []; } add(child, index) { const children = this.children; if (index === undefined) { children.push(child); } else { if (index < 0 || index > this.children.length) { throw new Error(`Child index ${index} out of bounds (0..${children.length})`); } children.splice(index, 0, child); } child.parent = this; this.index.add(child); } remove(child) { const children = this.children; const i = children.indexOf(child); if (i < 0) { throw new Error(`No such child ${child.id}`); } children.splice(i, 1); this.index.remove(child); } removeAll(filter) { const children = this.children; if (filter !== undefined) { for (let i = children.length - 1; i >= 0; i--) { if (filter(children[i])) { const child = children.splice(i, 1)[0]; this.index.remove(child); } } } else { children.forEach(child => { this.index.remove(child); }); children.splice(0, children.length); } } move(child, newIndex) { const children = this.children; const i = children.indexOf(child); if (i === -1) { throw new Error(`No such child ${child.id}`); } else { if (newIndex < 0 || newIndex > children.length - 1) { throw new Error(`Child index ${newIndex} out of bounds (0..${children.length})`); } children.splice(i, 1); children.splice(newIndex, 0, child); } } /** * Transform the given bounds from the local coordinate system of this element to the coordinate * system of its parent. This function should consider any transformation that is applied to the * view of this element and its contents. * The base implementation assumes that this element does not define a local coordinate system, * so it leaves the bounds unchanged. */ localToParent(point) { return (0, geometry_1.isBounds)(point) ? point : { x: point.x, y: point.y, width: -1, height: -1 }; } /** * Transform the given bounds from the coordinate system of this element's parent to its local * coordinate system. This function should consider any transformation that is applied to the * view of this element and its contents. * The base implementation assumes that this element does not define a local coordinate system, * so it leaves the bounds unchanged. */ parentToLocal(point) { return (0, geometry_1.isBounds)(point) ? point : { x: point.x, y: point.y, width: -1, height: -1 }; } } exports.SParentElementImpl = SParentElementImpl; /** * A child element is contained in a parent element. All elements except the model root are child * elements. In order to keep the model class hierarchy simple, every child element is also a * parent element, although for many elements the array of children is empty (i.e. they are * leafs in the model element tree). */ class SChildElementImpl extends SParentElementImpl { } exports.SChildElementImpl = SChildElementImpl; /** * Base class for the root element of the diagram model tree. */ class SModelRootImpl extends SParentElementImpl { constructor(index = new ModelIndexImpl()) { super(); this.canvasBounds = geometry_1.Bounds.EMPTY; // Override the index property from SModelElement, which has a getter, with a data property Object.defineProperty(this, 'index', { value: index, writable: false }); } } exports.SModelRootImpl = SModelRootImpl; const ID_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'; function createRandomId(length = 8) { let id = ''; for (let i = 0; i < length; i++) { id += ID_CHARS.charAt(Math.floor(Math.random() * ID_CHARS.length)); } return id; } exports.createRandomId = createRandomId; /** * This index implementation is for the _internal model_ that is used for rendering. */ class ModelIndexImpl { constructor() { this.id2element = new Map(); } add(element) { if (!element.id) { do { element.id = createRandomId(); } while (this.contains(element)); } else if (this.contains(element)) { throw new Error('Duplicate ID in model: ' + element.id); } this.id2element.set(element.id, element); if (element instanceof SParentElementImpl) { for (const child of element.children) { this.add(child); } } } remove(element) { this.id2element.delete(element.id); if (element instanceof SParentElementImpl) { for (const child of element.children) { this.remove(child); } } } contains(element) { return this.id2element.has(element.id); } getById(id) { return this.id2element.get(id); } getAttachedElements(element) { return []; } all() { return (0, iterable_1.mapIterable)(this.id2element, ([key, value]) => value); } } exports.ModelIndexImpl = ModelIndexImpl; //# sourceMappingURL=smodel.js.map