UNPKG

ngx-diagrams

Version:

> Light Weight, Modular, Typed Diagram Engine for Angular

3,647 lines 168 kB
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/common'), require('@angular/core'), require('rxjs'), require('rxjs/operators'), require('paths-js/path'), require('pathfinding'), require('dagre')) :
    typeof define === 'function' && define.amd ? define('ngx-diagrams', ['exports', '@angular/common', '@angular/core', 'rxjs', 'rxjs/operators', 'paths-js/path', 'pathfinding', 'dagre'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global['ngx-diagrams'] = {}, global.ng.common, global.ng.core, global.rxjs, global.rxjs.operators, global.Path, global.pathfinding, global.dagre));
}(this, (function (exports, common, i0, rxjs, operators, SVGPath, PF, dagre) { 'use strict';

    function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

    var SVGPath__default = /*#__PURE__*/_interopDefaultLegacy(SVGPath);

    /*! *****************************************************************************
    Copyright (c) Microsoft Corporation.

    Permission to use, copy, modify, and/or distribute this software for any
    purpose with or without fee is hereby granted.

    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
    REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
    AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
    OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
    PERFORMANCE OF THIS SOFTWARE.
    ***************************************************************************** */
    /* global Reflect, Promise */
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b)
                if (Object.prototype.hasOwnProperty.call(b, p))
                    d[p] = b[p]; };
        return extendStatics(d, b);
    };
    function __extends(d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    }
    var __assign = function () {
        __assign = Object.assign || function __assign(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s)
                    if (Object.prototype.hasOwnProperty.call(s, p))
                        t[p] = s[p];
            }
            return t;
        };
        return __assign.apply(this, arguments);
    };
    function __rest(s, e) {
        var t = {};
        for (var p in s)
            if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
                t[p] = s[p];
        if (s != null && typeof Object.getOwnPropertySymbols === "function")
            for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
                if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                    t[p[i]] = s[p[i]];
            }
        return t;
    }
    function __decorate(decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
            r = Reflect.decorate(decorators, target, key, desc);
        else
            for (var i = decorators.length - 1; i >= 0; i--)
                if (d = decorators[i])
                    r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    }
    function __param(paramIndex, decorator) {
        return function (target, key) { decorator(target, key, paramIndex); };
    }
    function __metadata(metadataKey, metadataValue) {
        if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
            return Reflect.metadata(metadataKey, metadataValue);
    }
    function __awaiter(thisArg, _arguments, P, generator) {
        function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
        return new (P || (P = Promise))(function (resolve, reject) {
            function fulfilled(value) { try {
                step(generator.next(value));
            }
            catch (e) {
                reject(e);
            } }
            function rejected(value) { try {
                step(generator["throw"](value));
            }
            catch (e) {
                reject(e);
            } }
            function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        });
    }
    function __generator(thisArg, body) {
        var _ = { label: 0, sent: function () { if (t[0] & 1)
                throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
        return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
        function verb(n) { return function (v) { return step([n, v]); }; }
        function step(op) {
            if (f)
                throw new TypeError("Generator is already executing.");
            while (_)
                try {
                    if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done)
                        return t;
                    if (y = 0, t)
                        op = [op[0] & 2, t.value];
                    switch (op[0]) {
                        case 0:
                        case 1:
                            t = op;
                            break;
                        case 4:
                            _.label++;
                            return { value: op[1], done: false };
                        case 5:
                            _.label++;
                            y = op[1];
                            op = [0];
                            continue;
                        case 7:
                            op = _.ops.pop();
                            _.trys.pop();
                            continue;
                        default:
                            if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
                                _ = 0;
                                continue;
                            }
                            if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
                                _.label = op[1];
                                break;
                            }
                            if (op[0] === 6 && _.label < t[1]) {
                                _.label = t[1];
                                t = op;
                                break;
                            }
                            if (t && _.label < t[2]) {
                                _.label = t[2];
                                _.ops.push(op);
                                break;
                            }
                            if (t[2])
                                _.ops.pop();
                            _.trys.pop();
                            continue;
                    }
                    op = body.call(thisArg, _);
                }
                catch (e) {
                    op = [6, e];
                    y = 0;
                }
                finally {
                    f = t = 0;
                }
            if (op[0] & 5)
                throw op[1];
            return { value: op[0] ? op[1] : void 0, done: true };
        }
    }
    var __createBinding = Object.create ? (function (o, m, k, k2) {
        if (k2 === undefined)
            k2 = k;
        Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } });
    }) : (function (o, m, k, k2) {
        if (k2 === undefined)
            k2 = k;
        o[k2] = m[k];
    });
    function __exportStar(m, o) {
        for (var p in m)
            if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p))
                __createBinding(o, m, p);
    }
    function __values(o) {
        var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
        if (m)
            return m.call(o);
        if (o && typeof o.length === "number")
            return {
                next: function () {
                    if (o && i >= o.length)
                        o = void 0;
                    return { value: o && o[i++], done: !o };
                }
            };
        throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
    }
    function __read(o, n) {
        var m = typeof Symbol === "function" && o[Symbol.iterator];
        if (!m)
            return o;
        var i = m.call(o), r, ar = [], e;
        try {
            while ((n === void 0 || n-- > 0) && !(r = i.next()).done)
                ar.push(r.value);
        }
        catch (error) {
            e = { error: error };
        }
        finally {
            try {
                if (r && !r.done && (m = i["return"]))
                    m.call(i);
            }
            finally {
                if (e)
                    throw e.error;
            }
        }
        return ar;
    }
    function __spread() {
        for (var ar = [], i = 0; i < arguments.length; i++)
            ar = ar.concat(__read(arguments[i]));
        return ar;
    }
    function __spreadArrays() {
        for (var s = 0, i = 0, il = arguments.length; i < il; i++)
            s += arguments[i].length;
        for (var r = Array(s), k = 0, i = 0; i < il; i++)
            for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
                r[k] = a[j];
        return r;
    }
    ;
    function __await(v) {
        return this instanceof __await ? (this.v = v, this) : new __await(v);
    }
    function __asyncGenerator(thisArg, _arguments, generator) {
        if (!Symbol.asyncIterator)
            throw new TypeError("Symbol.asyncIterator is not defined.");
        var g = generator.apply(thisArg, _arguments || []), i, q = [];
        return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
        function verb(n) { if (g[n])
            i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
        function resume(n, v) { try {
            step(g[n](v));
        }
        catch (e) {
            settle(q[0][3], e);
        } }
        function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
        function fulfill(value) { resume("next", value); }
        function reject(value) { resume("throw", value); }
        function settle(f, v) { if (f(v), q.shift(), q.length)
            resume(q[0][0], q[0][1]); }
    }
    function __asyncDelegator(o) {
        var i, p;
        return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
        function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; }
    }
    function __asyncValues(o) {
        if (!Symbol.asyncIterator)
            throw new TypeError("Symbol.asyncIterator is not defined.");
        var m = o[Symbol.asyncIterator], i;
        return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
        function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
        function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); }
    }
    function __makeTemplateObject(cooked, raw) {
        if (Object.defineProperty) {
            Object.defineProperty(cooked, "raw", { value: raw });
        }
        else {
            cooked.raw = raw;
        }
        return cooked;
    }
    ;
    var __setModuleDefault = Object.create ? (function (o, v) {
        Object.defineProperty(o, "default", { enumerable: true, value: v });
    }) : function (o, v) {
        o["default"] = v;
    };
    function __importStar(mod) {
        if (mod && mod.__esModule)
            return mod;
        var result = {};
        if (mod != null)
            for (var k in mod)
                if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
                    __createBinding(result, mod, k);
        __setModuleDefault(result, mod);
        return result;
    }
    function __importDefault(mod) {
        return (mod && mod.__esModule) ? mod : { default: mod };
    }
    function __classPrivateFieldGet(receiver, privateMap) {
        if (!privateMap.has(receiver)) {
            throw new TypeError("attempted to get private field on non-instance");
        }
        return privateMap.get(receiver);
    }
    function __classPrivateFieldSet(receiver, privateMap, value) {
        if (!privateMap.has(receiver)) {
            throw new TypeError("attempted to set private field on non-instance");
        }
        privateMap.set(receiver, value);
        return value;
    }

    var BaseAction = /** @class */ (function () {
        function BaseAction(mouseX, mouseY) {
            this.mouseX = mouseX;
            this.mouseY = mouseY;
            this.ms = new Date().getTime();
        }
        return BaseAction;
    }());

    var MoveCanvasAction = /** @class */ (function (_super) {
        __extends(MoveCanvasAction, _super);
        function MoveCanvasAction(mouseX, mouseY, diagramModel) {
            var _this = _super.call(this, mouseX, mouseY) || this;
            _this.initialOffsetX = diagramModel.getOffsetX();
            _this.initialOffsetY = diagramModel.getOffsetY();
            return _this;
        }
        return MoveCanvasAction;
    }(BaseAction));

    var SelectingAction = /** @class */ (function (_super) {
        __extends(SelectingAction, _super);
        function SelectingAction(mouseX, mouseY) {
            var _this = _super.call(this, mouseX, mouseY) || this;
            _this.mouseX2 = mouseX;
            _this.mouseY2 = mouseY;
            return _this;
        }
        SelectingAction.prototype.getBoxDimensions = function () {
            this.dimensions = {
                left: this.mouseX2 > this.mouseX ? this.mouseX : this.mouseX2,
                top: this.mouseY2 > this.mouseY ? this.mouseY : this.mouseY2,
                width: Math.abs(this.mouseX2 - this.mouseX),
                height: Math.abs(this.mouseY2 - this.mouseY),
                right: this.mouseX2 < this.mouseX ? this.mouseX : this.mouseX2,
                bottom: this.mouseY2 < this.mouseY ? this.mouseY : this.mouseY2,
            };
            return this.dimensions;
        };
        SelectingAction.prototype.containsElement = function (_a, diagramModel) {
            var x = _a.x, y = _a.y;
            var z = diagramModel.getZoomLevel() / 100.0;
            var dimensions = this.getBoxDimensions();
            return (x * z + diagramModel.getOffsetX() > dimensions.left &&
                x * z + diagramModel.getOffsetX() < dimensions.right &&
                y * z + diagramModel.getOffsetY() > dimensions.top &&
                y * z + diagramModel.getOffsetY() < dimensions.bottom);
        };
        return SelectingAction;
    }(BaseAction));

    var ROUTING_SCALING_FACTOR = 10;
    var PathFinding = /** @class */ (function () {
        function PathFinding(diagramEngine, heuristic) {
            if (heuristic === void 0) { heuristic = PF.Heuristic.manhattan; }
            this.diagramEngine = diagramEngine;
            this.pathFinderInstance = new PF.AStarFinder({
                heuristic: heuristic,
                diagonalMovement: PF.DiagonalMovement.Always,
                weight: 0,
            });
        }
        /**
         * Taking as argument a fully unblocked walking matrix, this method
         * finds a direct path from point A to B.
         */
        PathFinding.prototype.calculateDirectPath = function (from, to) {
            var matrix = this.diagramEngine.getCanvasMatrix();
            var grid = new PF.Grid(matrix);
            var fromX = this.diagramEngine.translateRoutingX(Math.floor(from.x / ROUTING_SCALING_FACTOR));
            var toX = this.diagramEngine.translateRoutingX(Math.floor(to.x / ROUTING_SCALING_FACTOR));
            var fromY = this.diagramEngine.translateRoutingX(Math.floor(from.y / ROUTING_SCALING_FACTOR));
            var toY = this.diagramEngine.translateRoutingX(Math.floor(to.y / ROUTING_SCALING_FACTOR));
            var path = this.pathFinderInstance.findPath(fromX, fromY, toX, toY, grid);
            return path;
        };
        /**
         * Using @link{#calculateDirectPath}'s result as input, we here
         * determine the first walkable point found in the matrix that includes
         * blocked paths.
         */
        PathFinding.prototype.calculateLinkStartEndCoords = function (matrix, path) {
            var startIndex = path.findIndex(function (point) { return matrix[point[1]][point[0]] === 0; });
            var endIndex = path.length -
                1 -
                path
                    .slice()
                    .reverse()
                    .findIndex(function (point) { return matrix[point[1]][point[0]] === 0; });
            // are we trying to create a path exclusively through blocked areas?
            // if so, let's fallback to the linear routing
            if (startIndex === -1 || endIndex === -1) {
                return undefined;
            }
            var pathToStart = path.slice(0, startIndex);
            var pathToEnd = path.slice(endIndex);
            return {
                start: {
                    x: path[startIndex][0],
                    y: path[startIndex][1],
                },
                end: {
                    x: path[endIndex][0],
                    y: path[endIndex][1],
                },
                pathToStart: pathToStart,
                pathToEnd: pathToEnd,
            };
        };
        /**
         * Puts everything together: merges the paths from/to the centre of the ports,
         * with the path calculated around other elements.
         */
        PathFinding.prototype.calculateDynamicPath = function (routingMatrix, start, end, pathToStart, pathToEnd) {
            var _this = this;
            // generate the path based on the matrix with obstacles
            var grid = new PF.Grid(routingMatrix);
            var dynamicPath = this.pathFinderInstance.findPath(start.x, start.y, end.x, end.y, grid);
            // aggregate everything to have the calculated path ready for rendering
            var pathCoords = pathToStart
                .concat(dynamicPath, pathToEnd)
                .map(function (coords) { return [
                _this.diagramEngine.translateRoutingX(coords[0], true),
                _this.diagramEngine.translateRoutingY(coords[1], true),
            ]; });
            return PF.Util.compressPath(pathCoords);
        };
        return PathFinding;
    }());

    (function (LOG_LEVEL) {
        LOG_LEVEL[LOG_LEVEL["LOG"] = 0] = "LOG";
        LOG_LEVEL[LOG_LEVEL["ERROR"] = 1] = "ERROR";
    })(exports.LOG_LEVEL || (exports.LOG_LEVEL = {}));
    // @internal
    exports.__DEV__ = true;
    // @internal
    exports.__LOG__ = exports.LOG_LEVEL.ERROR;
    function enableDiagramProdMode() {
        exports.__DEV__ = false;
    }
    // @internal
    function setLogLevel(level) {
        exports.__LOG__ = level;
    }
    // @internal
    function isDev() {
        return exports.__DEV__;
    }
    // @internal
    function log(message, level) {
        if (level === void 0) { level = exports.LOG_LEVEL.LOG; }
        var args = [];
        for (var _i = 2; _i < arguments.length; _i++) {
            args[_i - 2] = arguments[_i];
        }
        if (isDev() && exports.__LOG__ === level) {
            if (exports.__LOG__ === exports.LOG_LEVEL.ERROR) {
                console.error.apply(console, __spread([message], args));
            }
            console.log.apply(console, __spread([message], args));
        }
    }
    /**
     * rxjs log operator
     * @internal
     */
    function withLog(message, level) {
        if (level === void 0) { level = exports.LOG_LEVEL.LOG; }
        var args = [];
        for (var _i = 2; _i < arguments.length; _i++) {
            args[_i - 2] = arguments[_i];
        }
        return function (source) { return isDev()
            ? source.pipe(operators.tap(function (val) { return log.apply(void 0, __spread([message, level, val], args)); }))
            : source; };
    }
    /**
     * rxjs entity properties operator
     * @internal
     */
    function entityProperty(destroyedNotifier, replayBy, logMessage) {
        if (replayBy === void 0) { replayBy = 1; }
        if (logMessage === void 0) { logMessage = ''; }
        return function (source) { return source.pipe(operators.distinctUntilChanged(function (a, b) { return a instanceof Map || b instanceof Map ? false : a === b; }), operators.shareReplay(replayBy), withLog(logMessage), operators.takeUntil(destroyedNotifier)); };
    }
    /**
     * Generates a unique ID
     */
    function UID() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = (Math.random() * 16) | 0;
            var v = c === 'x' ? r : (r & 0x3) | 0x8;
            return v.toString(16);
        });
    }
    function isArray(val) {
        return Array.isArray(val);
    }
    function isString(val) {
        return typeof val === 'string';
    }
    function isFunction(val) {
        return typeof val === 'function';
    }
    // @internal
    function isNil(v) {
        return v === null || v === undefined;
    }
    function coerceArray(value) {
        if (isNil(value)) {
            return [];
        }
        return Array.isArray(value) ? value : [value];
    }
    function isEmptyArray(arr) {
        return !arr || !isArray(arr) || arr.length === 0;
    }
    function mapToArray(map) {
        var result = [];
        for (var key in map) {
            if (!isNil(map[key])) {
                result.push(map[key]);
            }
        }
        return result;
    }
    function mapToEntries(map) {
        var result = [];
        for (var key in map) {
            result.push([key, map[key]]);
        }
        return result;
    }
    function unique(arr) {
        return __spread(new Set(arr));
    }
    function arrayToMap(arr) {
        var e_1, _a;
        var result = {};
        try {
            for (var arr_1 = __values(arr), arr_1_1 = arr_1.next(); !arr_1_1.done; arr_1_1 = arr_1.next()) {
                var val = arr_1_1.value;
                if (!isNil(val)) {
                    result[val.id] = val;
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (arr_1_1 && !arr_1_1.done && (_a = arr_1.return)) _a.call(arr_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
        return result;
    }
    function generateLinePath(firstPoint, lastPoint) {
        return "M" + firstPoint.x$ + "," + firstPoint.y + " L " + lastPoint.x$ + "," + lastPoint.y;
    }
    function generateCurvePath(firstPoint, lastPoint, curvy) {
        if (curvy === void 0) { curvy = 0; }
        var isHorizontal = Math.abs(firstPoint.x - lastPoint.x) > Math.abs(firstPoint.y - lastPoint.y);
        var curvyX = isHorizontal ? curvy : 0;
        var curvyY = isHorizontal ? 0 : curvy;
        return "M" + firstPoint.x + "," + firstPoint.y + " C " + (firstPoint.x + curvyX) + "," + (firstPoint.y + curvyY) + "\n    " + (lastPoint.x - curvyX) + "," + (lastPoint.y - curvyY) + " " + lastPoint.x + "," + lastPoint.y;
    }
    function generateDynamicPath(pathCoords) {
        var path = SVGPath__default['default']();
        path = path.moveto(pathCoords[0][0] * ROUTING_SCALING_FACTOR, pathCoords[0][1] * ROUTING_SCALING_FACTOR);
        pathCoords.slice(1).forEach(function (coords) {
            path = path.lineto(coords[0] * ROUTING_SCALING_FACTOR, coords[1] * ROUTING_SCALING_FACTOR);
        });
        return path.print();
    }

    function OutsideZone(targetClass, functionName, descriptor) {
        var source = descriptor.value;
        descriptor.value = function () {
            var _this = this;
            var data = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                data[_i] = arguments[_i];
            }
            if (!this.ngZone) {
                throw new Error("Class with 'OutsideZone' decorator should have 'ngZone' class property with 'NgZone' class.");
            }
            return this.ngZone.runOutsideAngular(function () { return source.call.apply(source, __spread([_this], data)); });
        };
        return descriptor;
    }

    var ValueState = /** @class */ (function () {
        function ValueState(value, operator) {
            this.stream$ = new rxjs.BehaviorSubject(value);
            this.value$ = operator ? this.stream$.pipe(operator) : this.stream$.asObservable();
        }
        Object.defineProperty(ValueState.prototype, "value", {
            get: function () {
                return this.stream$.getValue();
            },
            enumerable: false,
            configurable: true
        });
        ValueState.prototype.set = function (value) {
            this.stream$.next(value);
            return this;
        };
        ValueState.prototype.emit = function () {
            this.stream$.next(this.value);
        };
        ValueState.prototype.select = function (project) {
            var mapFn = project || (function (v) { return v; });
            return this.value$.pipe(operators.map(function (value) { return mapFn(value); }), operators.distinctUntilChanged());
        };
        return ValueState;
    }());
    function createValueState(value, operator) {
        return new ValueState(value, operator);
    }
    var EntityState = /** @class */ (function (_super) {
        __extends(EntityState, _super);
        function EntityState(value, entityPipe) {
            return _super.call(this, value, entityPipe) || this;
        }
        EntityState.prototype.destroy = function () {
            this.clear();
            this.stream$ = null;
            this.value$ = null;
        };
        EntityState.prototype.clear = function (destroy) {
            if (destroy === void 0) { destroy = true; }
            if (destroy) {
                this.forEach(function (entity) { return entity.destroy(); });
            }
            this.value.clear();
            return this;
        };
        EntityState.prototype.get = function (id) {
            return this.value.get(id);
        };
        EntityState.prototype.has = function (id) {
            return this.value.has(id);
        };
        EntityState.prototype.add = function (entity) {
            this.value.set(entity.id, entity);
            return this;
        };
        EntityState.prototype.addMany = function (entities) {
            var e_1, _b;
            try {
                for (var entities_1 = __values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) {
                    var entity = entities_1_1.value;
                    this.add(entity);
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (entities_1_1 && !entities_1_1.done && (_b = entities_1.return)) _b.call(entities_1);
                }
                finally { if (e_1) throw e_1.error; }
            }
            return this;
        };
        EntityState.prototype.remove = function (id, destroy) {
            if (destroy === void 0) { destroy = true; }
            var _a;
            if (destroy) {
                (_a = this.value.get(id)) === null || _a === void 0 ? void 0 : _a.destroy();
            }
            this.value.delete(id);
            return this;
        };
        EntityState.prototype.array = function () {
            return Array.from(this.value.values());
        };
        EntityState.prototype.array$ = function () {
            return this.select(function (value) { return Array.from(value.values()); });
        };
        EntityState.prototype.forEach = function (cb) {
            this.value.forEach(cb);
        };
        EntityState.prototype.map = function (cb) {
            return this.array().map(cb);
        };
        return EntityState;
    }(ValueState));
    function createEntityState(value, entityPipe) {
        if (value === void 0) { value = []; }
        if (isArray(value)) {
            return new EntityState(new Map(value), entityPipe);
        }
        else {
            return new EntityState(new Map(mapToEntries(value)), entityPipe);
        }
    }

    // region events
    var BaseEvent = /** @class */ (function () {
        function BaseEvent(entity, options) {
            var _this = this;
            this.id = UID();
            this.entity = entity;
            this.entityId = entity.id;
            this.firing = true;
            this.stopPropagation = function () { return (_this.firing = false); };
            this.propogate = options ? options.propagate : null;
        }
        return BaseEvent;
    }());
    var LockEvent = /** @class */ (function (_super) {
        __extends(LockEvent, _super);
        function LockEvent(entity, locked) {
            if (locked === void 0) { locked = false; }
            var _this = _super.call(this, entity) || this;
            _this.locked = locked;
            return _this;
        }
        return LockEvent;
    }(BaseEvent));
    var ParentChangeEvent = /** @class */ (function (_super) {
        __extends(ParentChangeEvent, _super);
        function ParentChangeEvent(entity, parent) {
            var _this = _super.call(this, entity) || this;
            _this.parent = parent;
            return _this;
        }
        return ParentChangeEvent;
    }(BaseEvent));
    var SelectionEvent = /** @class */ (function (_super) {
        __extends(SelectionEvent, _super);
        function SelectionEvent(entity, selected) {
            var _this = _super.call(this, entity) || this;
            _this.isSelected = selected;
            return _this;
        }
        return SelectionEvent;
    }(BaseEvent));
    var PaintedEvent = /** @class */ (function (_super) {
        __extends(PaintedEvent, _super);
        function PaintedEvent(entity, painted) {
            if (painted === void 0) { painted = false; }
            var _this = _super.call(this, entity) || this;
            _this.isPainted = painted;
            return _this;
        }
        return PaintedEvent;
    }(BaseEvent));
    // endregion

    var BaseEntity = /** @class */ (function () {
        function BaseEntity(id, logPrefix) {
            if (logPrefix === void 0) { logPrefix = ''; }
            this.destroyed$ = new rxjs.Subject();
            this.locked$ = createValueState(false, this.entityPipe('locked'));
            this._id = id || UID();
            this._logPrefix = "" + logPrefix;
        }
        Object.defineProperty(BaseEntity.prototype, "id", {
            get: function () {
                return this._id;
            },
            set: function (id) {
                this._id = id;
            },
            enumerable: false,
            configurable: true
        });
        BaseEntity.prototype.log = function (message) {
            var args = [];
            for (var _i = 1; _i < arguments.length; _i++) {
                args[_i - 1] = arguments[_i];
            }
            log.apply(void 0, __spread([this._logPrefix + " " + message + ": ", exports.LOG_LEVEL.LOG], args));
        };
        BaseEntity.prototype.withLog = function (message) {
            var args = [];
            for (var _i = 1; _i < arguments.length; _i++) {
                args[_i - 1] = arguments[_i];
            }
            return withLog.apply(void 0, __spread([this._logPrefix + " " + message + ": ", exports.LOG_LEVEL.LOG], args));
        };
        BaseEntity.prototype.entityPipe = function (logMessage) {
            if (logMessage === void 0) { logMessage = ''; }
            return entityProperty(this.onEntityDestroy(), 0, this._logPrefix + ": " + logMessage);
        };
        BaseEntity.prototype.getLocked = function () {
            return this.locked$.value;
        };
        BaseEntity.prototype.setLocked = function (locked) {
            if (locked === void 0) { locked = true; }
            this.locked$.set(locked).emit();
        };
        // eslint-disable-next-line
        BaseEntity.prototype.doClone = function (lookupTable, clone) {
            if (lookupTable === void 0) { lookupTable = {}; }
            /*noop*/
        };
        BaseEntity.prototype.clone = function (lookupTable) {
            if (lookupTable === void 0) { lookupTable = {}; }
            // try and use an existing clone first
            if (lookupTable[this.id]) {
                return lookupTable[this.id];
            }
            var clone = Object.assign({}, this);
            clone.id = UID();
            // clone.clearListeners();
            lookupTable[this.id] = clone;
            this.doClone(lookupTable, clone);
            return clone;
        };
        BaseEntity.prototype.serialize = function () {
            return {
                id: this.id,
                locked: this.getLocked()
            };
        };
        BaseEntity.prototype.lockChanges = function () {
            var _this = this;
            return this.locked$.select(function (locked) { return new LockEvent(_this, locked); });
        };
        BaseEntity.prototype.destroy = function () {
            this.log('entity destroyed');
            this.destroyed$.next();
            this.destroyed$.complete();
        };
        BaseEntity.prototype.onEntityDestroy = function () {
            var _this = this;
            return this.destroyed$.pipe(operators.map(function (opts) { return new BaseEvent(_this, opts); }));
        };
        return BaseEntity;
    }());

    var BaseModel = /** @class */ (function (_super) {
        __extends(BaseModel, _super);
        function BaseModel(type, id, logPrefix) {
            if (logPrefix === void 0) { logPrefix = '[Base]'; }
            var _this = _super.call(this, id, logPrefix) || this;
            _this.parent$ = createValueState(null, _this.entityPipe('ParentsChange'));
            _this.selected$ = createValueState(false, _this.entityPipe('SelectedChange'));
            _this.hovered$ = createValueState(false, _this.entityPipe('HoveredChange'));
            _this.painted$ = createValueState(false, _this.entityPipe('PaintedChange'));
            _this._type = type;
            return _this;
        }
        BaseModel.prototype.serialize = function () {
            return Object.assign(Object.assign({}, _super.prototype.serialize.call(this)), { type: this.getType() });
        };
        BaseModel.prototype.getParent = function () {
            return this.parent$.value;
        };
        BaseModel.prototype.setParent = function (parent) {
            this.parent$.set(parent).emit();
        };
        BaseModel.prototype.parentChanges = function () {
            var _this = this;
            return this.parent$.select(function (p) { return new ParentChangeEvent(_this, p); });
        };
        BaseModel.prototype.getPainted = function () {
            return this.painted$.value;
        };
        BaseModel.prototype.setPainted = function (painted) {
            if (painted === void 0) { painted = true; }
            this.painted$.set(painted).emit();
        };
        BaseModel.prototype.getHovered = function () {
            return this.hovered$.value;
        };
        BaseModel.prototype.setHovered = function (painted) {
            if (painted === void 0) { painted = true; }
            this.hovered$.set(painted).emit();
        };
        BaseModel.prototype.selectHovered = function () {
            return this.hovered$.value$;
        };
        BaseModel.prototype.paintChanges = function () {
            var _this = this;
            return this.painted$.select(function (p) { return new PaintedEvent(_this, p); });
        };
        BaseModel.prototype.getType = function () {
            return this._type;
        };
        BaseModel.prototype.getSelected = function () {
            return this.selected$.value;
        };
        BaseModel.prototype.selectSelected = function () {
            return this.selected$.select();
        };
        BaseModel.prototype.setSelected = function (selected) {
            if (selected === void 0) { selected = true; }
            this.selected$.set(selected).emit();
        };
        BaseModel.prototype.selectionChanges = function () {
            var _this = this;
            return this.selected$.select(function (selected) { return new SelectionEvent(_this, selected); });
        };
        BaseModel.prototype.getSelectedEntities = function () {
            return this.getSelected() ? [this] : [];
        };
        return BaseModel;
    }(BaseEntity));

    var PointModel = /** @class */ (function (_super) {
        __extends(PointModel, _super);
        function PointModel(link, coords, id, logPrefix) {
            if (logPrefix === void 0) { logPrefix = '[Point]'; }
            var _this = _super.call(this, link.getType(), id, logPrefix) || this;
            _this.coords$ = createValueState(coords, _this.entityPipe('coords'));
            _this.setParent(link);
            return _this;
        }
        PointModel.prototype.serialize = function () {
            return Object.assign(Object.assign({}, _super.prototype.serialize.call(this)), { coords: this.getCoords() });
        };
        PointModel.prototype.isConnectedToPort = function () {
            return this.getParent().getPortForPoint(this) !== null;
        };
        PointModel.prototype.getLink = function () {
            return this.getParent();
        };
        PointModel.prototype.destroy = function () {
            if (this.getParent) {
                this.getParent().removePoint(this);
            }
            _super.prototype.destroy.call(this);
        };
        PointModel.prototype.setCoords = function (newCoords) {
            this.coords$.set(Object.assign(Object.assign({}, this.getCoords()), newCoords)).emit();
        };
        PointModel.prototype.selectCoords = function () {
            return this.coords$.value$;
        };
        PointModel.prototype.getCoords = function () {
            return this.coords$.value;
        };
        PointModel.prototype.selectX = function () {
            return this.coords$.select(function (coords) { return coords.x; });
        };
        PointModel.prototype.selectY = function () {
            return this.coords$.select(function (coords) { return coords.y; });
        };
        return PointModel;
    }(BaseModel));

    var LinkModel = /** @class */ (function (_super) {
        __extends(LinkModel, _super);
        function LinkModel(linkType, id, logPrefix) {
            if (linkType === void 0) { linkType = 'default'; }
            if (logPrefix === void 0) { logPrefix = '[Link]'; }
            var _this = _super.call(this, linkType, id, logPrefix) || this;
            _this.label$ = createValueState(null, _this.entityPipe('label'));
            _this.points = [new PointModel(_this, { x: 0, y: 0 }), new PointModel(_this, { x: 0, y: 0 })];
            _this.extras = {};
            _this.sourcePort = null;
            _this.targetPort = null;
            return _this;
        }
        LinkModel.prototype.serialize = function () {
            var _a;
            var serializedPoints = this.points.map(function (point) { return point.serialize(); });
            var label = (_a = this.getLabel()) === null || _a === void 0 ? void 0 : _a.serialize();
            return Object.assign(Object.assign({}, _super.prototype.serialize.call(this)), { name: this.getName(), sourcePort: this.getSourcePort().id, targetPort: this.getTargetPort().id, extras: this.getExtras(), points: serializedPoints, label: label });
        };
        LinkModel.prototype.setName = function (name) {
            this.name = name;
        };
        LinkModel.prototype.getName = function () {
            return this.name;
        };
        LinkModel.prototype.getExtras = function () {
            return this.extras;
        };
        LinkModel.prototype.setExtras = function (extras) {
            this.extras = extras;
        };
        LinkModel.prototype.destroy = function () {
            if (this.sourcePort) {
                this.sourcePort.removeLink(this);
            }
            if (this.targetPort) {
                this.targetPort.removeLink(this);
            }
            _super.prototype.destroy.call(this);
        };
        LinkModel.prototype.doClone = function (lookupTable, clone) {
            if (lookupTable === void 0) { lookupTable = {}; }
            clone.setPoints(this.getPoints().map(function (point) {
                return point.clone(lookupTable);
            }));
            if (this.sourcePort) {
                clone.setSourcePort(this.sourcePort.clone(lookupTable));
            }
            if (this.targetPort) {
                clone.setTargetPort(this.targetPort.clone(lookupTable));
            }
        };
        LinkModel.prototype.isLastPoint = function (point) {
            var index = this.getPointIndex(point);
            return index === this.points.length - 1;
        };
        LinkModel.prototype.getPointIndex = function (point) {
            return this.points.indexOf(point);
        };
        LinkModel.prototype.getPointModel = function (id) {
            var e_1, _b;
            try {
                for (var _c = __values(this.points), _d = _c.next(); !_d.done; _d = _c.next()) {
                    var point = _d.value;
                    if (point.id === id) {
                        return point;
                    }
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
                }
                finally { if (e_1) throw e_1.error; }
            }
            return null;
        };
        LinkModel.prototype.getPortForPoint = function (point) {
            if (this.sourcePort !== null && this.getFirstPoint().id === point.id) {
                return this.sourcePort;
            }
            if (this.targetPort !== null && this.getLastPoint().id === point.id) {
                return this.targetPort;
            }
            return null;
        };
        LinkModel.prototype.getPointForPort = function (port) {
            if (this.sourcePort !== null && this.sourcePort.id === port.id) {
                return this.getFirstPoint();
            }
            if (this.targetPort !== null && this.targetPort.id === port.id) {
                return this.getLastPoint();
            }
            return null;
        };
        LinkModel.prototype.getFirstPoint = function () {
            return this.points[0];
        };
        LinkModel.prototype.getLastPoint = function () {
            return this.points[this.points.length - 1];
        };
        LinkModel.prototype.setSourcePort = function (port) {
            if (port !== null) {
                port.addLink(this);
            }
            if (this.sourcePort !== null) {
                this.sourcePort.removeLink(this);
            }
            this.sourcePort = port;
        };
        LinkModel.prototype.getSourcePort = function () {
            return this.sourcePort;
        };
        LinkModel.prototype.getTargetPort = function () {
            return this.targetPort;
        };
        LinkModel.prototype.setTargetPort = function (port) {
            if (port !== null) {
                port.addLink(this);
            }
            if (this.targetPort !== null) {
                this.targetPort.removeLink(this);
            }
            this.targetPort = port;
        };
        LinkModel.prototype.point = function (_b) {
            var x = _b.x, y = _b.y;
            return this.addPoint(this.generatePoint({ x: x, y: y }));
        };
        LinkModel.prototype.getPoints = function () {
            return this.points;
        };
        LinkModel.prototype.setPoints = function (points) {
            var _this = this;
            points.forEach(function (point) {
                point.setParent(_this);
            });
            this.points = points;
        };
        LinkModel.prototype.setLabel = function (label) {
            label.setParent(this);
            this.label$.set(label).emit();
        };
        LinkModel.prototype.selectLabel = function () {
            return this.label$.value$;
        };
        LinkModel.prototype.getLabel = function () {
            return this.label$.value;
        };
        LinkModel.prototype.resetLabel = function () {
            var currentLabel = this.getLabel();
            if (currentLabel) {
                currentLabel.setParent(null);
                currentLabel.setPainted(false);
            }
            this.setLabel(null);
        };
        LinkModel.prototype.removePoint = function (pointModel) {
            this.points.splice(this.getPointIndex(pointModel), 1);
        };
        LinkModel.prototype.removePointsBefore = function (pointModel) {
            this.points.splice(0, this.getPointIndex(pointModel));
        };
        LinkModel.prototype.removePointsAfter = function (pointModel) {
            this.points.splice(this.getPointIndex(pointModel) + 1);
        };
        LinkModel.prototype.removeMiddlePoints = function () {
            if (this.points.length > 2) {
                this.points.splice(0, this.points.length - 2);
            }
        };
        LinkModel.prototype.addPoint = function (pointModel, index) {
            if (index === void 0) { index = 1; }
            pointModel.setParent(this);
            pointModel.setLocked(this.getLocked());
            this.points.splice(index, 0, pointModel);
            return pointModel;
        };
        LinkModel.prototype.generatePoint = function (_b) {
            var _c = _b.x, x = _c === void 0 ? 0 : _c, _d = _b.y, y = _d === void 0 ? 0 : _d;
            return new PointModel(this, { x: x, y: y });
        };
        LinkModel.prototype.setLocked = function (locked) {
            if (locked === void 0) { locked = true; }
            _super.prototype.setLocked.call(this, locked);
            this.points.forEach(function (point) { return point.setLocked(locked); });
        };
        return LinkModel;
    }(BaseModel));

    var MoveItemsAction = /** @class */ (function (_super) {
        __extends(MoveItemsAction, _super);
        function MoveItemsAction(mouseX, mouseY, diagramEngine) {
            var _this = _super.call(this, mouseX, mouseY) || this;
            _this.moved = false;
            var selectedItems = diagramEngine.getDiagramModel().getSelectedItems();
            // dont allow items which are locked to move and links which generate their position based on points.
            selectedItems = selectedItems.filter(function (item) {
                return !diagramEngine.isModelLocked(item) && !(item instanceof LinkModel);
            });
            _this.selectionModels = selectedItems.map(function (item) {
                var _a = item.getCoords(), initialX = _a.x, initialY = _a.y;
                return {
                    model: item,
                    initialX: initialX,
                    initialY: initialY,
                };
            });
            return _this;
        }
        return MoveItemsAction;
    }(BaseAction));

    // TODO: refactor into entity-created.action, and fire every time a new entity is created!
    var LinkCreatedAction = /** @class */ (function (_super) {
        __extends(LinkCreatedAction, _super);
        function LinkCreatedAction(mouseX, mouseY, link) {
            var _this = _super.call(this, mouseX, mouseY) || this;
            _this.sourcePort = link.getSourcePort();
            _this.targetPort = link.getTargetPort();
            _this.link = link;
            return _this;
        }
        LinkCreatedAction.prototype.getOutPortNode = function () {
            return this.sourcePort.getParent();
        };
        LinkCreatedAction.prototype.getInPortNode = function () {
            return this.targetPort.getParent();
        };
        return LinkCreatedAction;
    }(BaseAction));

    var LooseLinkDestroyed = /** @class */ (function (_super) {
        __extends(LooseLinkDestroyed, _super);
        function LooseLinkDestroyed(mouseX, mouseY, link) {
            var _this = _super.call(this, mouseX, mouseY) || this;
            _this.sourcePort = link.getSourcePort();
            _this.link = link;
            return _this;
        }
        LooseLinkDestroyed.prototype.getOutPortNode = function () {
            return this.sourcePort.getParent();
        };
        return LooseLinkDestroyed;
    }(BaseAction));

    var InvalidLinkDestroyed = /** @class */ (function (_super) {
        __extends(InvalidLinkDestroyed, _super);
        function InvalidLinkDestroyed(mouseX, mouseY, link) {
            var _this = _super.call(this, mouseX, mouseY) || this;
            _this.sourcePort = link.getSourcePort();
            _this.link = link;
            return _this;
        }
        InvalidLinkDestroyed.prototype.getOutPortNode = function () {
            return this.sourcePort.getParent();
        };
        return InvalidLinkDestroyed;
    }(BaseAction));

    var NodeModel = /** @class */ (function (_super) {
        __extends(NodeModel, _super);
        function NodeModel(nodeType, id, extras, x, y, width, height, logPrefix) {
            if (nodeType === void 0) { nodeType = 'default'; }
            if (extras === void 0) { extras = {}; }
            if (x === void 0) { x = 0; }
            if (y === void 0) { y = 0; }
            if (width === void 0) { width = 0; }
            if (height === void 0) { height = 0; }
            if (logPrefix === void 0) { logPrefix = '[Node]'; }
            var _this = _super.call(this, nodeType, id, logPrefix) || this;
            _this.diagramEngine$ = createValueState(null, _this.entityPipe('diagramEngine'));
            _this.extras$ = createValueState({}, _this.entityPipe('extras'));
            _this.ports$ = createEntityState([], _this.entityPipe('ports'));
            _this.coords$ = createValueState({ x: 0, y: 0 }, _this.entityPipe('coords'));
            _this.dimensions$ = createValueState({ width: 0, height: 0 }, _this.entityPipe('dimensions'));
            _this.setExtras(extras);
            _this.setDimensions({ width: width, height: height });
            _this.setCoords({ x: x, y: y });
            return _this;
        }
        NodeModel.prototype.getDiagramEngine = function () {
            return this.diagramEngine$.value;
        };
        NodeModel.prototype.selectDiagramEngine = function () {
            return this.diagramEngine$.value$;
        };
        NodeModel.prototype.setDiagramEngine = function (diagramEngine) {
            this.diagramEngine$.set(diagramEngine).emit();
        };
        NodeModel.prototype.getCoords = function () {
            return this.coords$.value;
        };
        NodeModel.prototype.setCoords = function (_a) {
            var x = _a.x, y = _a.y;
            var _b = this.getCoords(), oldX = _b.x, oldY = _b.y;
            this.getPorts().forEach(function (port) {
                port.getLinks().forEach(function (link) {
                    var point = link.getPointForPort(port);
                    var _a = point.getCoords(), pointX = _a.x, pointY = _a.y;
                    point.setCoords({ x: pointX + x - oldX, y: pointY + y - oldY });
                });
            });
            this.coords$.set({ x: x, y: y }).emit();
        };
        NodeModel.prototype.serialize = function () {
            var serializedPorts = this.getPortsArray().map(function (port) { return port.serialize(); });
            return Object.assign(Object.assign(Object.assign(Object.assign({}, _super.prototype.serialize.call(this)), { nodeType: this.getType(), extras: this.getExtras(), width: this.getWidth(), height: this.getHeight() }), this.getCoords()), { ports: serializedPorts });
        };
        // TODO: override selectionChanges and replace this with it (convert to rx)
        NodeModel.prototype.getSelectedEntities = function () {
            var entities = _super.prototype.getSelectedEntities.call(this);
            // add the points of each link that are selected here
            if (this.getSelected()) {
                this.getPorts().forEach(function (port) {
                    var points = port.getLinksArray().map(function (link) { return link.getPointForPort(port); });
                    entities = entities.concat(points);
                });
            }
            this.log('selectedEntities', entities);
            return entities;
        };
        // TODO: map to BaseEvent
        NodeModel.prototype.coordsChanges = function () {
            return this.coords$.value$;
        };
        NodeModel.prototype.selectCoords = function () {
            return this.coords$.value$;
        };
        NodeModel.prototype.selectX = function () {
            return this.coords$.select(function (coords) { return coords.x; });
        };
        NodeModel.prototype.selectY = function () {
            return this.coords$.select(function (coords) { return coords.y; });
        };
        /**
         * Assign a port to the node and set the node as its getParent
         * @returns the inserted port
         */
        NodeModel.prototype.addPort = function (port) {
            port.setParent(this);
            this.ports$.add(port).emit();
            return port;
        };
        NodeModel.prototype.removePort = function (portOrId) {
            var portId = typeof portOrId === 'string' ? portOrId : portOrId.id;
            this.ports$.remove(portId).emit();
            return portId;
        };
        NodeModel.prototype.getPort = function (id) {
            return this.ports$.get(id);
        };
        NodeModel.prototype.selectPorts = function (selector) {
            // TODO: implement selector
            // TODO: create coerce func
            return this.ports$.array$().pipe(this.withLog('selectPorts'));
        };
        NodeModel.prototype.getPorts = function () {
            return this.ports$.value;
        };
        NodeModel.prototype.getPortsArray = function () {
            return this.ports$.array();
        };
        NodeModel.prototype.setDimensions = function (dimensions) {
            this.dimensions$.set(Object.assign(Object.assign({}, this.getDimensions()), dimensions)).emit();
        };
        NodeModel.prototype.getDimensions = function () {
            return this.dimensions$.value;
        };
        // TODO: return BaseEvent extension
        NodeModel.prototype.dimensionChanges = function () {
            return this.dimensions$.select();
        };
        NodeModel.prototype.getHeight = function () {
            return this.getDimensions().height;
        };
        NodeModel.prototype.setHeight = function (height) {
            return this.setDimensions({ height: height });
        };
        NodeModel.prototype.getWidth = function () {
            return this.getDimensions().width;
        };
        NodeModel.prototype.setWidth = function (width) {
            return this.setDimensions({ width: width });
        };
        NodeModel.prototype.selectWidth = function () {
            return this.dimensions$.select(function (d) { return d.width; }).pipe(this.withLog('selectWidth'));
        };
        NodeModel.prototype.selectHeight = function () {
            return this.dimensions$.select(function (d) { return d.height; }).pipe(this.withLog('selectHeight'));
        };
        NodeModel.prototype.setExtras = function (extras) {
            this.extras$.set(extras).emit();
        };
        NodeModel.prototype.getExtras = function () {
            return this.extras$.value;
        };
        NodeModel.prototype.selectExtras = function (selector) {
            return this.extras$.select(selector);
        };
        NodeModel.prototype.destroy = function () {
            _super.prototype.destroy.call(this);
            this.removeAllPorts();
        };
        NodeModel.prototype.removeAllPorts = function () {
            this.ports$.clear().emit();
        };
        return NodeModel;
    }(BaseModel));

    var PortModel = /** @class */ (function (_super) {
        __extends(PortModel, _super);
        function PortModel(name, type, id, maximumLinks, linkType, magnetic, logPrefix) {
            if (magnetic === void 0) { magnetic = true; }
            if (logPrefix === void 0) { logPrefix = '[Port]'; }
            var _this = _super.call(this, type, id, logPrefix) || this;
            _this.links$ = createEntityState([], _this.entityPipe('links'));
            _this.x$ = createValueState(0, _this.entityPipe('x'));
            _this.y$ = createValueState(0, _this.entityPipe('y'));
            _this.width$ = createValueState(0, _this.entityPipe('y'));
            _this.height$ = createValueState(0, _this.entityPipe('y'));
            _this.magnetic$ = createValueState(true, _this.entityPipe('magnetic'));
            _this.canCreateLinks$ = createValueState(true, _this.entityPipe('magnetic'));
            _this.name = name;
            _this.maximumLinks = maximumLinks;
            _this.linkType = linkType;
            _this.setMagnetic(magnetic);
            return _this;
        }
        PortModel.prototype.serialize = function () {
            return Object.assign(Object.assign(Object.assign({}, _super.prototype.serialize.call(this)), { name: this.getName(), linkType: this.getLinkType(), maximumLinks: this.getMaximumLinks(), type: this.getType(), magnetic: this.getMagnetic(), height: this.getHeight(), width: this.getWidth(), canCreateLinks: this.getCanCreateLinks() }), this.getCoords());
        };
        PortModel.prototype.getNode = function () {
            return this.getParent();
        };
        PortModel.prototype.getName = function () {
            return this.name;
        };
        PortModel.prototype.getCanCreateLinks = function () {
            var numberOfLinks = this.getLinks().size;
            if (this.maximumLinks && numberOfLinks >= this.maximumLinks) {
                return false;
            }
            return this.canCreateLinks$.value;
        };
        PortModel.prototype.getCoords = function () {
            return { x: this.getX(), y: this.getY() };
        };
        PortModel.prototype.selectCanCreateLinks = function () {
            return this.canCreateLinks$.value$;
        };
        PortModel.prototype.setCanCreateLinks = function (value) {
            this.canCreateLinks$.set(value).emit();
        };
        PortModel.prototype.getMagnetic = function () {
            return this.magnetic$.value;
        };
        PortModel.prototype.selectMagnetic = function () {
            return this.magnetic$.value$;
        };
        PortModel.prototype.setMagnetic = function (magnetic) {
            this.magnetic$.set(magnetic).emit();
        };
        PortModel.prototype.selectX = function () {
            return this.x$.value$;
        };
        PortModel.prototype.selectY = function () {
            return this.y$.value$;
        };
        PortModel.prototype.getY = function () {
            return this.y$.value;
        };
        PortModel.prototype.getX = function () {
            return this.x$.value;
        };
        PortModel.prototype.getHeight = function () {
            return this.height$.value;
        };
        PortModel.prototype.getWidth = function () {
            return this.width$.value;
        };
        PortModel.prototype.getMaximumLinks = function () {
            return this.maximumLinks;
        };
        PortModel.prototype.setMaximumLinks = function (maximumLinks) {
            this.maximumLinks = maximumLinks;
        };
        PortModel.prototype.getLinkType = function () {
            return this.linkType;
        };
        PortModel.prototype.setLinkType = function (type) {
            this.linkType = type;
        };
        PortModel.prototype.removeLink = function (linkOrId) {
            var linkId = isString(linkOrId) ? linkOrId : linkOrId.id;
            this.links$.remove(linkId, false).emit();
        };
        PortModel.prototype.addLink = function (link) {
            this.links$.add(link).emit();
        };
        PortModel.prototype.getLinks = function () {
            return this.links$.value;
        };
        PortModel.prototype.getLinksArray = function () {
            return this.links$.array();
        };
        PortModel.prototype.selectLinks = function () {
            return this.links$.value$;
        };
        PortModel.prototype.updateCoords = function (_a) {
            var x = _a.x, y = _a.y, width = _a.width, height = _a.height;
            this.x$.set(x).emit();
            this.y$.set(y).emit();
            this.width$.set(width).emit();
            this.height$.set(height).emit();
        };
        PortModel.prototype.canLinkToPort = function (port) {
            return true;
        };
        PortModel.prototype.isLocked = function () {
            return _super.prototype.getLocked.call(this);
        };
        PortModel.prototype.createLinkModel = function () {
            if (this.getCanCreateLinks()) {
                return new LinkModel();
            }
        };
        PortModel.prototype.destroy = function () {
            _super.prototype.destroy.call(this);
            this.links$.clear().emit();
        };
        return PortModel;
    }(BaseModel));

    var NgxDiagramComponent = /** @class */ (function () {
        function NgxDiagramComponent(document, ngZone, renderer, cdRef, elRef) {
            this.document = document;
            this.ngZone = ngZone;
            this.renderer = renderer;
            this.cdRef = cdRef;
            this.elRef = elRef;
            this.allowCanvasZoom = true;
            this.allowCanvasTranslation = true;
            this.inverseZoom = true;
            this.allowLooseLinks = true;
            this.maxZoomOut = null;
            this.maxZoomIn = null;
            this.portMagneticRadius = 30;
            this.smartRouting = false;
            this.actionStartedFiring = new i0.EventEmitter();
            this.actionStillFiring = new i0.EventEmitter();
            this.actionStoppedFiring = new i0.EventEmitter();
            this.action$ = new rxjs.BehaviorSubject(null);
            this.nodesRendered$ = new rxjs.BehaviorSubject(false);
            this.destroyed$ = new rxjs.ReplaySubject(1);
        }
        Object.defineProperty(NgxDiagramComponent.prototype, "host", {
            get: function () {
                return this.elRef.nativeElement;
            },
            enumerable: false,
            configurable: true
        });
        // TODO: handle destruction of container, resetting all observables to avoid memory leaks!
        NgxDiagramComponent.prototype.ngAfterViewInit = function () {
            if (this.diagramModel) {
                this.initNodes();
                this.initLinks();
                this.initSubs();
            }
        };
        NgxDiagramComponent.prototype.ngOnDestroy = function () {
            this.destroyed$.next(true);
            this.destroyed$.complete();
        };
        /**
         * fire the action registered and notify subscribers
         */
        NgxDiagramComponent.prototype.fireAction = function () {
            if (this.action$.value) {
                this.actionStillFiring.emit(this.action$.value);
            }
        };
        /**
         * Unregister the action, post firing and notify subscribers
         */
        NgxDiagramComponent.prototype.stopFiringAction = function (shouldSkipEvent) {
            if (!shouldSkipEvent) {
                this.actionStoppedFiring.emit(this.action$.value);
            }
            this.action$.next(null);
        };
        /**
         * Register the new action, pre firing and notify subscribers
         */
        NgxDiagramComponent.prototype.startFiringAction = function (action) {
            this.action$.next(action);
            this.actionStartedFiring.emit(action);
        };
        NgxDiagramComponent.prototype.selectAction = function () {
            return this.action$;
        };
        NgxDiagramComponent.prototype.shouldDrawSelectionBox = function () {
            var action = this.action$.getValue();
            if (action instanceof SelectingAction) {
                action.getBoxDimensions();
                return true;
            }
            return false;
        };
        NgxDiagramComponent.prototype.getMouseElement = function (event) {
            var target = event.target;
            // is it a port?
            var element = target.closest('[data-portid]');
            if (element) {
                // get the relevant node and return the port.
                var nodeEl = target.closest('[data-nodeid]');
                return {
                    model: this.diagramModel
                        .getNode(nodeEl.getAttribute('data-nodeid'))
                        .getPort(element.getAttribute('data-portid')),
                    element: element
                };
            }
            // look for a point
            element = target.closest('[data-pointid]');
            if (element) {
                return {
                    model: this.diagramModel
                        .getLink(element.getAttribute('data-linkid'))
                        .getPointModel(element.getAttribute('data-pointid')),
                    element: element
                };
            }
            // look for a link
            element = target.closest('[data-linkid]');
            if (element) {
                return {
                    model: this.diagramModel.getLink(element.getAttribute('data-linkid')),
                    element: element
                };
            }
            // a node maybe
            element = target.closest('[data-nodeid]');
            if (element) {
                return {
                    model: this.diagramModel.getNode(element.getAttribute('data-nodeid')),
                    element: element
                };
            }
            // just the canvas
            return null;
        };
        NgxDiagramComponent.prototype.onMouseUp = function (event) {
            var _this = this;
            var diagramEngine = this.diagramModel.getDiagramEngine();
            var action = this.action$.getValue();
            // are we going to connect a link to something?
            if (action instanceof MoveItemsAction) {
                var element_1 = this.getMouseElement(event);
                action.selectionModels.forEach(function (model) {
                    // only care about points connecting to things
                    if (!model || !(model.model instanceof PointModel)) {
                        return;
                    }
                    var el;
                    if (model.magnet) {
                        el = model.magnet;
                    }
                    else if (element_1 && element_1.model) {
                        el = element_1.model;
                    }
                    if (el instanceof PortModel && !diagramEngine.isModelLocked(el)) {
                        var link = model.model.getLink();
                        if (link.getTargetPort() !== null) {
                            // if this was a valid link already and we are adding a node in the middle, create 2 links from the original
                            if (link.getTargetPort() !== el && link.getSourcePort() !== el) {
                                var targetPort = link.getTargetPort();
                                var newLink = link.clone({});
                                newLink.setSourcePort(el);
                                newLink.setTargetPort(targetPort);
                                link.setTargetPort(el);
                                targetPort.removeLink(link);
                                newLink.removePointsBefore(newLink.getPoints()[link.getPointIndex(model.model)]);
                                link.removePointsAfter(model.model);
                                diagramEngine.getDiagramModel().addLink(newLink);
                                // if we are connecting to the same target or source, destroy tweener points
                            }
                            else if (link.getTargetPort() === el) {
                                link.removePointsAfter(model.model);
                            }
                            else if (link.getSourcePort() === el) {
                                link.removePointsBefore(model.model);
                            }
                        }
                        else {
                            link.setTargetPort(el);
                            var targetPort = link.getTargetPort();
                            var srcPort = link.getSourcePort();
                            if (targetPort.id !== srcPort.id && srcPort.canLinkToPort(targetPort)) {
                                // link is valid, fire the event
                                _this.startFiringAction(new LinkCreatedAction(event.clientX, event.clientY, link));
                            }
                        }
                    }
                    // reset current magent
                    model.magnet = undefined;
                });
                // check for / destroy any loose links in any models which have been moved
                if (!this.allowLooseLinks) {
                    action.selectionModels.forEach(function (model) {
                        // only care about points connecting to things
                        if (!model || !(model.model instanceof PointModel)) {
                            return;
                        }
                        var selectedPoint = model.model;
                        var link = selectedPoint.getLink();
                        if (link.getSourcePort() === null || link.getTargetPort() === null) {
                            link.destroy();
                            _this.startFiringAction(new LooseLinkDestroyed(event.clientX, event.clientY, link));
                        }
                    });
                }
                // destroy any invalid links
                action.selectionModels.forEach(function (model) {
                    // only care about points connecting to things
                    if (!model || !(model.model instanceof PointModel)) {
                        return;
                    }
                    var link = model.model.getLink();
                    var sourcePort = link.getSourcePort();
                    var targetPort = link.getTargetPort();
                    if (sourcePort !== null && targetPort !== null) {
                        if (!sourcePort.canLinkToPort(targetPort)) {
                            // link not allowed
                            link.destroy();
                            _this.startFiringAction(new InvalidLinkDestroyed(event.clientX, event.clientY, link));
                        }
                        else if (targetPort
                            .getLinksArray()
                            .some(function (link) { return link !== link && (link.getSourcePort() === sourcePort || link.getTargetPort() === sourcePort); })) {
                            // link is a duplicate
                            link.destroy();
                        }
                    }
                });
                this.stopFiringAction();
            }
            else {
                this.stopFiringAction();
            }
            this.action$.next(null);
        };
        /**
         * @description Mouse Move Event Handler
         * @param event MouseEvent
         */
        NgxDiagramComponent.prototype.onMouseMove = function (event) {
            var _this = this;
            var action = this.action$.getValue();
            if (action === null || action === undefined) {
                return;
            }
            if (action instanceof SelectingAction) {
                var relative = this.diagramModel.getDiagramEngine().getRelativePoint(event.clientX, event.clientY);
                this.diagramModel.getNodes().forEach(function (node) {
                    if (action.containsElement(node.getCoords(), _this.diagramModel)) {
                        node.setSelected();
                    }
                    else {
                        node.setSelected(false);
                    }
                });
                this.diagramModel.getLinks().forEach(function (link) {
                    var allSelected = true;
                    link.getPoints().forEach(function (point) {
                        if (action.containsElement(point.getCoords(), _this.diagramModel)) {
                            point.setSelected();
                        }
                        else {
                            point.setSelected(false);
                            allSelected = false;
                        }
                    });
                    if (allSelected) {
                        link.setSelected();
                    }
                });
                action.mouseX2 = relative.x;
                action.mouseY2 = relative.y;
                this.fireAction();
                this.action$.next(action);
                return;
            }
            else if (action instanceof MoveItemsAction) {
                var coords_1 = {
                    x: event.clientX - action.mouseX,
                    y: event.clientY - action.mouseY
                };
                var amountZoom_1 = this.diagramModel.getZoomLevel() / 100;
                action.selectionModels.forEach(function (selectionModel) {
                    var e_1, _a;
                    // reset all previous magnets if any
                    selectionModel.magnet = undefined;
                    // in this case we need to also work out the relative grid position
                    if (selectionModel.model instanceof NodeModel ||
                        (selectionModel.model instanceof PointModel && !selectionModel.model.isConnectedToPort())) {
                        var newCoords = {
                            x: selectionModel.initialX + coords_1.x / amountZoom_1,
                            y: selectionModel.initialY + coords_1.y / amountZoom_1
                        };
                        var gridRelativeCoords = _this.diagramModel.getGridPosition(newCoords);
                        // magnetic inputs handling
                        if (selectionModel.model instanceof PointModel && _this.portMagneticRadius) {
                            // get all ports on canvas, check distances, if smaller then defined radius, magnetize!
                            var portsMap = _this.diagramModel.getAllPorts({ filter: function (p) { return p.getMagnetic(); } });
                            try {
                                for (var _b = __values(portsMap.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
                                    var port = _c.value;
                                    var portCoords = port.getCoords();
                                    var distance = Math.hypot(portCoords.x - newCoords.x, portCoords.y - newCoords.y);
                                    if (distance <= _this.portMagneticRadius) {
                                        var portCenter = _this.diagramModel.getDiagramEngine().getPortCenter(port);
                                        selectionModel.model.setCoords(portCenter);
                                        selectionModel.magnet = port;
                                        return;
                                    }
                                }
                            }
                            catch (e_1_1) { e_1 = { error: e_1_1 }; }
                            finally {
                                try {
                                    if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
                                }
                                finally { if (e_1) throw e_1.error; }
                            }
                        }
                        selectionModel.model.setCoords(gridRelativeCoords);
                        if (selectionModel.model instanceof NodeModel) {
                            // update port coordinates as well
                            selectionModel.model.getPorts().forEach(function (port) {
                                var portCoords = _this.diagramModel.getDiagramEngine().getPortCoords(port);
                                port.updateCoords(portCoords);
                            });
                        }
                        if (_this.diagramModel.getDiagramEngine().getSmartRouting()) {
                            setTimeout(function () {
                                _this.diagramModel.getDiagramEngine().calculateRoutingMatrix();
                            }, 1);
                        }
                    }
                    else if (selectionModel.model instanceof PointModel) {
                        // will only run here when trying to create a point on an existing link
                        // we want points that are connected to ports, to not necessarily snap to grid
                        // this stuff needs to be pixel perfect, dont touch it
                        var newCoords = _this.diagramModel.getGridPosition({ x: coords_1.x / amountZoom_1, y: coords_1.y / amountZoom_1 });
                        selectionModel.model.setCoords({
                            x: selectionModel.initialX + newCoords.x,
                            y: selectionModel.initialY + newCoords.y
                        });
                    }
                });
                this.fireAction();
            }
            else if (action instanceof MoveCanvasAction) {
                if (this.allowCanvasTranslation) {
                    this.diagramModel.setOffset(action.initialOffsetX + (event.clientX - action.mouseX), action.initialOffsetY + (event.clientY - action.mouseY));
                    this.fireAction();
                }
            }
        };
        NgxDiagramComponent.prototype.onMouseDown = function (event) {
            if (event.button === 3) {
                return;
            }
            var selectedModel = this.getMouseElement(event);
            // canvas selected
            if (selectedModel === null) {
                // multiple selection
                if (event.shiftKey) {
                    // initiate multiple selection selector
                    var relative = this.diagramModel.getDiagramEngine().getRelativePoint(event.clientX, event.clientY);
                    this.startFiringAction(new SelectingAction(relative.x, relative.y));
                }
                else {
                    // drag canvas action
                    this.diagramModel.clearSelection();
                    this.startFiringAction(new MoveCanvasAction(event.clientX, event.clientY, this.diagramModel));
                }
            }
            else if (selectedModel.model instanceof PortModel) {
                // its a port element, we want to drag a link
                if (!selectedModel.model.isLocked() && selectedModel.model.getCanCreateLinks()) {
                    var relative = this.diagramModel.getDiagramEngine().getRelativeMousePoint(event);
                    var sourcePort = selectedModel.model;
                    var link = sourcePort.createLinkModel();
                    // if we don't have a link then we have reached the max amount, or we cannot create new ones
                    if (link) {
                        link.setSourcePort(sourcePort);
                        link.removeMiddlePoints();
                        if (link.getSourcePort() !== sourcePort) {
                            link.setSourcePort(sourcePort);
                        }
                        link.setTargetPort(null);
                        link.getFirstPoint().setCoords(relative);
                        link.getLastPoint().setCoords(relative);
                        this.diagramModel.clearSelection();
                        link.getLastPoint().setSelected();
                        this.diagramModel.addLink(link);
                        this.startFiringAction(new MoveItemsAction(event.clientX, event.clientY, this.diagramModel.getDiagramEngine()));
                    }
                }
                else {
                    this.diagramModel.clearSelection();
                }
            }
            else if (selectedModel.model instanceof PointModel && selectedModel.model.isConnectedToPort()) {
                this.diagramModel.clearSelection();
            }
            else {
                // its some other element, probably want to move it
                if (!event.shiftKey && !selectedModel.model.getSelected()) {
                    this.diagramModel.clearSelection();
                }
                selectedModel.model.setSelected();
                this.startFiringAction(new MoveItemsAction(event.clientX, event.clientY, this.diagramModel.getDiagramEngine()));
            }
            this.createMouseListeners();
        };
        NgxDiagramComponent.prototype.onMouseWheel = function (event) {
            if (!this.allowCanvasZoom) {
                return;
            }
            event.preventDefault();
            event.stopPropagation();
            var currentZoomLevel = this.diagramModel.getZoomLevel();
            var oldZoomFactor = currentZoomLevel / 100;
            var scrollDelta = this.inverseZoom ? -event.deltaY : event.deltaY;
            // check if it is pinch gesture
            if (event.ctrlKey && scrollDelta % 1 !== 0) {
                /* Chrome and Firefox sends wheel event with deltaY that
                                   have fractional part, also `ctrlKey` prop of the event is true
                                   though ctrl isn't pressed
                              */
                scrollDelta /= 3;
            }
            else {
                scrollDelta /= 60;
            }
            if (currentZoomLevel + scrollDelta > 10) {
                var newZoomLvl = currentZoomLevel + scrollDelta;
                this.diagramModel.setZoomLevel(newZoomLvl);
            }
            var updatedZoomLvl = this.diagramModel.getZoomLevel();
            var zoomFactor = updatedZoomLvl / 100;
            var boundingRect = event.currentTarget.getBoundingClientRect();
            var clientWidth = boundingRect.width;
            var clientHeight = boundingRect.height;
            // compute difference between rect before and after scroll
            var widthDiff = clientWidth * zoomFactor - clientWidth * oldZoomFactor;
            var heightDiff = clientHeight * zoomFactor - clientHeight * oldZoomFactor;
            // compute mouse coords relative to canvas
            var clientX = event.clientX - boundingRect.left;
            var clientY = event.clientY - boundingRect.top;
            // compute width and height increment factor
            var xFactor = (clientX - this.diagramModel.getOffsetX()) / oldZoomFactor / clientWidth;
            var yFactor = (clientY - this.diagramModel.getOffsetY()) / oldZoomFactor / clientHeight;
            var updatedXOffset = this.diagramModel.getOffsetX() - widthDiff * xFactor;
            var updatedYOffset = this.diagramModel.getOffsetY() - heightDiff * yFactor;
            this.diagramModel.setOffset(updatedXOffset, updatedYOffset);
        };
        NgxDiagramComponent.prototype.setLayerStyles = function (x, y, zoom) {
            var nodesLayer = this.getNodesLayer();
            var linksLayer = this.getLinksLayer();
            var style = 'transform';
            var value = "translate(" + x + "px, " + y + "px) scale(" + zoom / 100.0 + ")";
            this.renderer.setStyle(nodesLayer, style, value);
            this.renderer.setStyle(linksLayer, style, value);
        };
        NgxDiagramComponent.prototype.initNodes = function () {
            var _this = this;
            this.nodes$ = this.diagramModel.selectNodes();
            this.diagramModel.getDiagramEngine().setCanvas(this.canvas.nativeElement);
            this.diagramModel.getDiagramEngine().setSmartRoutingStatus(this.smartRouting);
            this.diagramModel.setMaxZoomIn(this.maxZoomIn);
            this.diagramModel.setMaxZoomOut(this.maxZoomOut);
            this.nodes$.pipe(operators.takeUntil(this.destroyed$)).subscribe(function (nodes) {
                var e_2, _a;
                _this.nodesRendered$.next(false);
                try {
                    for (var _b = __values(nodes.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
                        var node = _c.value;
                        if (!node.getPainted()) {
                            _this.diagramModel.getDiagramEngine().generateWidgetForNode(node, _this.nodesLayer);
                            _this.cdRef.detectChanges();
                        }
                    }
                }
                catch (e_2_1) { e_2 = { error: e_2_1 }; }
                finally {
                    try {
                        if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
                    }
                    finally { if (e_2) throw e_2.error; }
                }
                _this.nodesRendered$.next(true);
            });
        };
        NgxDiagramComponent.prototype.initLinks = function () {
            var _this = this;
            this.links$ = this.diagramModel.selectLinks();
            rxjs.combineLatest([this.nodesRendered$, this.links$])
                .pipe(operators.filter(function (_a) {
                var _b = __read(_a, 1), nodesRendered = _b[0];
                return !!nodesRendered;
            }), operators.takeUntil(this.destroyed$))
                .subscribe(function (_a) {
                var e_3, _b;
                var _c = __read(_a, 2), links = _c[1];
                try {
                    for (var _d = __values(links.values()), _e = _d.next(); !_e.done; _e = _d.next()) {
                        var link = _e.value;
                        if (!link.getPainted() && link.getSourcePort().getPainted()) {
                            if (link.getSourcePort() !== null) {
                                var portCenter = _this.diagramModel.getDiagramEngine().getPortCenter(link.getSourcePort());
                                link.getPoints()[0].setCoords(portCenter);
                                var portCoords = _this.diagramModel.getDiagramEngine().getPortCoords(link.getSourcePort());
                                link.getSourcePort().updateCoords(portCoords);
                            }
                            if (link.getTargetPort() !== null) {
                                var portCenter = _this.diagramModel.getDiagramEngine().getPortCenter(link.getTargetPort());
                                link.getPoints()[link.getPoints().length - 1].setCoords(portCenter);
                                var portCoords = _this.diagramModel.getDiagramEngine().getPortCoords(link.getTargetPort());
                                link.getTargetPort().updateCoords(portCoords);
                            }
                            _this.diagramModel.getDiagramEngine().generateWidgetForLink(link, _this.linksLayer);
                            _this.cdRef.detectChanges();
                        }
                    }
                }
                catch (e_3_1) { e_3 = { error: e_3_1 }; }
                finally {
                    try {
                        if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
                    }
                    finally { if (e_3) throw e_3.error; }
                }
            });
        };
        NgxDiagramComponent.prototype.initSubs = function () {
            var _this = this;
            rxjs.combineLatest([
                this.diagramModel.selectOffsetX(),
                this.diagramModel.selectOffsetY(),
                this.diagramModel.selectZoomLevel()
            ])
                .pipe(operators.tap(function (_a) {
                var _b = __read(_a, 3), x = _b[0], y = _b[1], zoom = _b[2];
                return _this.setLayerStyles(x, y, zoom);
            }), operators.takeUntil(this.destroyed$))
                .subscribe();
        };
        NgxDiagramComponent.prototype.getNodesLayer = function () {
            return this.host.querySelector('.ngdx-nodes-layer');
        };
        NgxDiagramComponent.prototype.getLinksLayer = function () {
            return this.host.querySelector('.ngdx-links-layer');
        };
        NgxDiagramComponent.prototype.createMouseListeners = function () {
            var _this = this;
            var mouseUp$ = rxjs.fromEvent(this.document, 'mouseup').pipe(operators.tap(function (e) { return _this.onMouseUp(e); }), operators.take(1));
            var mouseMove$ = rxjs.fromEvent(this.document, 'mousemove').pipe(operators.tap(function (e) { return _this.onMouseMove(e); }), operators.takeUntil(mouseUp$));
            rxjs.merge(mouseMove$, mouseUp$).subscribe();
        };
        return NgxDiagramComponent;
    }());
    NgxDiagramComponent.decorators = [
        { type: i0.Component, args: [{
                    selector: 'ngdx-diagram',
                    template: "<div class=\"ngdx-diagram\" #canvas (mousedown)=\"onMouseDown($event)\" (wheel)=\"onMouseWheel($event)\">\n\t<!-- Nodes Layer -->\n\t<div class=\"ngdx-nodes-layer\">\n\t\t<ng-template #nodesLayer></ng-template>\n\t</div>\n\n\t<!-- Links Layer -->\n\t<div class=\"ngdx-links-layer\">\n\t\t<ng-template #linksLayer></ng-template>\n\t</div>\n\n\t<!-- Selection Box -->\n\t<div\n\t\t*ngIf=\"shouldDrawSelectionBox() && selectAction() | async as action\"\n\t\tclass=\"selector\"\n\t\t[ngStyle]=\"{\n\t\t\ttop: action.dimensions.top + 'px',\n\t\t\tleft: action.dimensions.left + 'px',\n\t\t\twidth: action.dimensions.width + 'px',\n\t\t\theight: action.dimensions.height + 'px'\n\t\t}\"></div>\n</div>\n",
                    changeDetection: i0.ChangeDetectionStrategy.OnPush,
                    styles: [".ngdx-diagram{cursor:move;display:flex;flex-grow:1;overflow:hidden;position:relative}.ngdx-diagram .selector{background-color:rgba(0,192,255,.2);border:2px solid #00c0ff;position:absolute}.ngdx-diagram .ngdx-nodes-layer{z-index:150}.ngdx-diagram .ngdx-links-layer,.ngdx-diagram .ngdx-nodes-layer{bottom:0;height:100%;left:0;overflow:visible!important;pointer-events:none;position:absolute;right:0;top:0;transform-origin:0 0;width:100%}.ngdx-diagram .ngdx-links-layer{z-index:100}"]
                },] }
    ];
    NgxDiagramComponent.ctorParameters = function () { return [
        { type: undefined, decorators: [{ type: i0.Inject, args: [common.DOCUMENT,] }] },
        { type: i0.NgZone },
        { type: i0.Renderer2 },
        { type: i0.ChangeDetectorRef },
        { type: i0.ElementRef }
    ]; };
    NgxDiagramComponent.propDecorators = {
        diagramModel: [{ type: i0.Input, args: ['model',] }],
        allowCanvasZoom: [{ type: i0.Input }],
        allowCanvasTranslation: [{ type: i0.Input }],
        inverseZoom: [{ type: i0.Input }],
        allowLooseLinks: [{ type: i0.Input }],
        maxZoomOut: [{ type: i0.Input }],
        maxZoomIn: [{ type: i0.Input }],
        portMagneticRadius: [{ type: i0.Input }],
        smartRouting: [{ type: i0.Input }],
        actionStartedFiring: [{ type: i0.Output }],
        actionStillFiring: [{ type: i0.Output }],
        actionStoppedFiring: [{ type: i0.Output }],
        nodesLayer: [{ type: i0.ViewChild, args: ['nodesLayer', { read: i0.ViewContainerRef },] }],
        linksLayer: [{ type: i0.ViewChild, args: ['linksLayer', { read: i0.ViewContainerRef },] }],
        canvas: [{ type: i0.ViewChild, args: ['canvas', { read: i0.ElementRef },] }]
    };
    __decorate([
        OutsideZone,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [MouseEvent]),
        __metadata("design:returntype", void 0)
    ], NgxDiagramComponent.prototype, "onMouseUp", null);
    __decorate([
        OutsideZone,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [MouseEvent]),
        __metadata("design:returntype", void 0)
    ], NgxDiagramComponent.prototype, "onMouseMove", null);
    __decorate([
        OutsideZone,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [MouseEvent]),
        __metadata("design:returntype", void 0)
    ], NgxDiagramComponent.prototype, "onMouseDown", null);
    __decorate([
        OutsideZone,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [WheelEvent]),
        __metadata("design:returntype", void 0)
    ], NgxDiagramComponent.prototype, "onMouseWheel", null);
    __decorate([
        OutsideZone,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Number, Number, Number]),
        __metadata("design:returntype", void 0)
    ], NgxDiagramComponent.prototype, "setLayerStyles", null);

    var AbstractFactory = /** @class */ (function () {
        function AbstractFactory(type) {
            this._type = type;
        }
        Object.defineProperty(AbstractFactory.prototype, "type", {
            get: function () {
                return this._type;
            },
            enumerable: false,
            configurable: true
        });
        return AbstractFactory;
    }());

    var AbstractLabelFactory = /** @class */ (function (_super) {
        __extends(AbstractLabelFactory, _super);
        function AbstractLabelFactory() {
            return _super !== null && _super.apply(this, arguments) || this;
        }
        return AbstractLabelFactory;
    }(AbstractFactory));

    var LabelModel = /** @class */ (function (_super) {
        __extends(LabelModel, _super);
        function LabelModel(type, id, logPrefix) {
            if (logPrefix === void 0) { logPrefix = '[Label]'; }
            var _this = _super.call(this, type, id, logPrefix) || this;
            _this.coords$ = createValueState({ x: 0, y: 0 }, _this.entityPipe('coords'));
            _this.rotation$ = createValueState(0, _this.entityPipe('rotation'));
            return _this;
        }
        LabelModel.prototype.serialize = function () {
            return Object.assign(Object.assign({}, _super.prototype.serialize.call(this)), { type: this.getType(), rotation: this.getRotation(), coords: this.getCoords() });
        };
        LabelModel.prototype.getRotation = function () {
            return this.rotation$.value;
        };
        LabelModel.prototype.getCoords = function () {
            return this.coords$.value;
        };
        LabelModel.prototype.destroy = function () {
            _super.prototype.destroy.call(this);
        };
        LabelModel.prototype.setRotation = function (angle) {
            this.rotation$.set(angle).emit();
        };
        LabelModel.prototype.selectRotation = function () {
            return this.rotation$.value$;
        };
        LabelModel.prototype.setCoords = function (newCoords) {
            this.coords$.set(Object.assign(Object.assign({}, this.coords$.value), newCoords)).emit();
        };
        LabelModel.prototype.selectCoords = function () {
            return this.coords$.value$;
        };
        return LabelModel;
    }(BaseModel));

    var DefaultLabelModel = /** @class */ (function (_super) {
        __extends(DefaultLabelModel, _super);
        function DefaultLabelModel(label, type, id, logPrefix) {
            if (label === void 0) { label = 'NO LABEL'; }
            if (type === void 0) { type = 'default'; }
            if (logPrefix === void 0) { logPrefix = '[DefaultLabel]'; }
            var _this = _super.call(this, type, id, logPrefix) || this;
            _this.label$ = createValueState('', _this.entityPipe('label'));
            _this.setLabel(label);
            return _this;
        }
        DefaultLabelModel.prototype.setLabel = function (label) {
            this.label$.set(label).emit();
        };
        DefaultLabelModel.prototype.getLabel = function () {
            return this.label$.value;
        };
        DefaultLabelModel.prototype.selectLabel = function () {
            return this.label$.value$;
        };
        return DefaultLabelModel;
    }(LabelModel));

    var DefaultLabelComponent = /** @class */ (function (_super) {
        __extends(DefaultLabelComponent, _super);
        function DefaultLabelComponent() {
            return _super.call(this, 'ngdx-default-label') || this;
        }
        return DefaultLabelComponent;
    }(DefaultLabelModel));
    DefaultLabelComponent.decorators = [
        { type: i0.Component, args: [{
                    selector: 'ngdx-default-label',
                    template: "<div\n  class=\"default-label\"\n  [ngStyle]=\"{\n\t\ttransform:\n\t\t\t'translate(' +\n\t\t\t(selectCoords() | async).x +\n\t\t\t'px, ' +\n\t\t\t(selectCoords() | async).y +\n\t\t\t'px)' +\n\t\t\t'rotate(' +\n\t\t\t(selectRotation() | async) +\n\t\t\t'deg)'\n\t}\">\n  {{ selectLabel() | async }}\n</div>\n",
                    changeDetection: i0.ChangeDetectionStrategy.OnPush,
                    styles: [".default-label{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;color:#fff;user-select:none}"]
                },] }
    ];
    DefaultLabelComponent.ctorParameters = function () { return []; };

    var DefaultLabelFactory = /** @class */ (function (_super) {
        __extends(DefaultLabelFactory, _super);
        function DefaultLabelFactory(resolver, renderer) {
            var _this = _super.call(this, 'default') || this;
            _this.resolver = resolver;
            _this.renderer = renderer;
            return _this;
        }
        DefaultLabelFactory.prototype.generateWidget = function (label, labelHost) {
            var componentRef = labelHost.createComponent(this.getRecipe());
            // attach coordinates and default positional behaviour to the generated component host
            var rootNode = componentRef.location.nativeElement;
            // default style for link
            this.renderer.setStyle(rootNode, 'position', 'absolute');
            // data attributes
            this.renderer.setAttribute(rootNode, 'data-labelid', label.id);
            // on destroy make sure to destroy the componentRef
            label.onEntityDestroy().subscribe(function () {
                componentRef.destroy();
            });
            // assign all passed properties to node initialization.
            Object.entries(label).forEach(function (_a) {
                var _b = __read(_a, 2), key = _b[0], value = _b[1];
                componentRef.instance[key] = value;
            });
            componentRef.instance.setPainted(true);
            return componentRef;
        };
        DefaultLabelFactory.prototype.getRecipe = function () {
            return this.resolver.resolveComponentFactory(DefaultLabelComponent);
        };
        DefaultLabelFactory.prototype.getNewInstance = function () {
            return new DefaultLabelModel();
        };
        return DefaultLabelFactory;
    }(AbstractLabelFactory));

    var AbstractLinkFactory = /** @class */ (function (_super) {
        __extends(AbstractLinkFactory, _super);
        function AbstractLinkFactory() {
            return _super !== null && _super.apply(this, arguments) || this;
        }
        return AbstractLinkFactory;
    }(AbstractFactory));

    var DefaultLinkModel = /** @class */ (function (_super) {
        __extends(DefaultLinkModel, _super);
        function DefaultLinkModel(_a) {
            var _b = _a === void 0 ? {} : _a, _c = _b.type, type = _c === void 0 ? 'default' : _c, id = _b.id, _d = _b.logPrefix, logPrefix = _d === void 0 ? '[DefaultLink]' : _d;
            var _this = _super.call(this, type, id, logPrefix) || this;
            _this.width$ = createValueState(3, _this.entityPipe('width'));
            _this.color$ = createValueState('rgba(255, 255, 255, 0.5)', _this.entityPipe('color'));
            _this.curvyness$ = createValueState(50, _this.entityPipe('curvyness'));
            return _this;
        }
        DefaultLinkModel.prototype.setWidth = function (width) {
            this.width$.set(width).emit();
        };
        DefaultLinkModel.prototype.setColor = function (color) {
            this.color$.set(color).emit();
        };
        DefaultLinkModel.prototype.selectWidth = function () {
            return this.width$.value$;
        };
        DefaultLinkModel.prototype.selectColor = function () {
            return this.color$.value$;
        };
        DefaultLinkModel.prototype.setCurvyness = function (curvyness) {
            this.curvyness$.set(curvyness).emit();
        };
        Object.defineProperty(DefaultLinkModel.prototype, "color", {
            get: function () {
                return this.color$.value;
            },
            enumerable: false,
            configurable: true
        });
        Object.defineProperty(DefaultLinkModel.prototype, "width", {
            get: function () {
                return this.width$.value;
            },
            enumerable: false,
            configurable: true
        });
        Object.defineProperty(DefaultLinkModel.prototype, "curvyness", {
            get: function () {
                return this.curvyness$.value;
            },
            enumerable: false,
            configurable: true
        });
        return DefaultLinkModel;
    }(LinkModel));

    var DefaultLinkComponent = /** @class */ (function (_super) {
        __extends(DefaultLinkComponent, _super);
        function DefaultLinkComponent(cdRef) {
            var _this = _super.call(this, { type: 'ngdx-default-link' }) || this;
            _this.cdRef = cdRef;
            _this.path$ = createValueState(null, _this.entityPipe('path'));
            _this.points$ = createValueState([], _this.entityPipe('points'));
            return _this;
        }
        DefaultLinkComponent.prototype.trackByPoints = function (i, point) {
            return point.id;
        };
        DefaultLinkComponent.prototype.ngOnInit = function () {
            if (this.diagramEngine.getSmartRouting()) {
                this.pathFinding = this.diagramEngine.getPathfinding();
            }
        };
        DefaultLinkComponent.prototype.ngAfterViewInit = function () {
            var _this = this;
            var firstPCoords$ = this.getFirstPoint().selectCoords();
            var lastPCoords$ = this.getLastPoint().selectCoords();
            // Observe link coords and update drawing accordingly
            rxjs.combineLatest([firstPCoords$, lastPCoords$])
                .pipe(operators.takeUntil(this.onEntityDestroy()))
                .subscribe(function (_a) {
                var _b = __read(_a, 2), firstPCoords = _b[0], lastPCoords = _b[1];
                var points = [firstPCoords, lastPCoords];
                if (_this.diagramEngine.getSmartRouting()) {
                    // first step: calculate a direct path between the points being linked
                    var directPathCoords = _this.pathFinding.calculateDirectPath(firstPCoords, lastPCoords);
                    var routingMatrix = _this.diagramEngine.getRoutingMatrix();
                    // now we need to extract, from the routing matrix, the very first walkable points
                    // so they can be used as origin and destination of the link to be created
                    var smartLink = _this.pathFinding.calculateLinkStartEndCoords(routingMatrix, directPathCoords);
                    if (smartLink) {
                        var start = smartLink.start, end = smartLink.end, pathToStart = smartLink.pathToStart, pathToEnd = smartLink.pathToEnd;
                        // second step: calculate a path avoiding hitting other elements
                        var simplifiedPath = _this.pathFinding.calculateDynamicPath(routingMatrix, start, end, pathToStart, pathToEnd);
                        var smartPath = generateDynamicPath(simplifiedPath);
                        _this.path$.set(smartPath).emit();
                    }
                }
                else {
                    // handle regular links
                    // draw the smoothing
                    // if the points are too close, just draw a straight line
                    var isHorizontal = Math.abs(firstPCoords.x - lastPCoords.x) > Math.abs(firstPCoords.y - lastPCoords.y);
                    var xOrY = isHorizontal ? 'x' : 'y';
                    var isStraight = false;
                    if (Math.abs(points[0][xOrY] - points[1][xOrY]) < 50) {
                        isStraight = true;
                    }
                    var path = generateCurvePath(firstPCoords, lastPCoords, isStraight ? 0 : _this.curvyness);
                    _this.path$.set(path).emit();
                }
                var label = _this.getLabel();
                // update label position
                if (label) {
                    label.setCoords(_this.calcCenterOfPath(firstPCoords, lastPCoords));
                    // TODO: check whether we want the label to rotate along with the line
                    // label.setRotation(this.calcLabelIncline(firstPCoords, lastPCoords));
                }
                // TODO: handle the multiple lines in between the points
                // https://github.com/projectstorm/react-diagrams/blob/master/src/defaults/widgets/DefaultLinkWidget.tsx#L344-L371
                _this.cdRef.detectChanges();
            });
            // observe link label and draw/remove accordingly
            this.selectLabel()
                .pipe(operators.filter(Boolean))
                .subscribe(function (label) {
                _this.diagramEngine.generateWidgetForLabel(label, _this.labelLayer);
                _this.cdRef.detectChanges();
            });
        };
        DefaultLinkComponent.prototype.calcLabelIncline = function (firstPoint, secondPoint) {
            var dy = secondPoint.y - firstPoint.y;
            var dx = secondPoint.x - firstPoint.x;
            if (dx === 0) {
                return 0;
            }
            var inclineAngel = (Math.atan(dy / dx) * 180) / Math.PI;
            if (inclineAngel < 0) {
                inclineAngel += 180;
            }
            return inclineAngel;
        };
        DefaultLinkComponent.prototype.calcCenterOfPath = function (firstPoint, secondPoint) {
            return { x: (firstPoint.x + secondPoint.x) / 2 + 20, y: (firstPoint.y + secondPoint.y) / 2 + 20 };
        };
        DefaultLinkComponent.prototype.selectPath = function () {
            return this.path$.value$;
        };
        return DefaultLinkComponent;
    }(DefaultLinkModel));
    DefaultLinkComponent.decorators = [
        { type: i0.Component, args: [{
                    selector: 'ngdx-default-link',
                    template: "<!-- Label Host -->\n<ng-template #labelLayer></ng-template>\n\n<svg class=\"link-container\">\n  <!-- Link -->\n  <g [id]=\"id\">\n    <path\n      [attr.stroke-width]=\"width\"\n      [attr.stroke]=\"selectColor() | async\"\n      [attr.d]=\"selectPath() | async\"\n      class=\"default-link\"\n      (mouseleave)=\"setHovered(false)\"\n      (mouseenter)=\"setHovered()\"\n      [ngClass]=\"{ hovered: selectHovered() | async }\"\n    ></path>\n    <path\n      class=\"default-link\"\n      (mouseleave)=\"setHovered(false)\"\n      (mouseenter)=\"setHovered()\"\n      [attr.stroke-width]=\"20\"\n      [attr.stroke]=\"selectColor() | async\"\n      [attr.stroke-linecap]=\"'round'\"\n      [attr.data-linkid]=\"id\"\n      [attr.stroke-opacity]=\"(selectHovered() | async) ? '0.1' : '0'\"\n      [attr.d]=\"selectPath() | async\"\n    ></path>\n  </g>\n\n  <!-- Points -->\n  <g *ngFor=\"let point of getPoints(); trackBy: trackByPoints\" [id]=\"point.id\">\n    <circle\n      fill=\"currentColor\"\n      [attr.cx]=\"point.selectX() | async\"\n      [attr.cy]=\"point.selectY() | async\"\n      r=\"5\"\n      class=\"default-point\"\n      [ngClass]=\"{ hovered: point.selectHovered() | async }\"\n    />\n    <circle\n      [attr.data-pointid]=\"point.id\"\n      [attr.data-linkid]=\"id\"\n      (mouseleave)=\"point.setHovered(false); setHovered(false)\"\n      (mouseenter)=\"point.setHovered(); setHovered()\"\n      [attr.cx]=\"point.selectX() | async\"\n      [attr.cy]=\"point.selectY() | async\"\n      r=\"15\"\n      [attr.opacity]=\"0\"\n      [ngClass]=\"{ selected: point.selectSelected() | async }\"\n    />\n  </g>\n</svg>\n",
                    changeDetection: i0.ChangeDetectionStrategy.OnPush,
                    styles: [".link-container{pointer-events:all;position:relative}.default-link,.link-container{fill:none;overflow:visible}@-webkit-keyframes dash{0%{stroke-dashoffset:24}to{stroke-dashoffset:0}}@keyframes dash{0%{stroke-dashoffset:24}to{stroke-dashoffset:0}}.default-link.hovered{-webkit-animation:dash 1s linear infinite;animation:dash 1s linear infinite;stroke:#00c0ff;stroke-dasharray:10,2}.default-point{color:#d3d3d3;overflow:visible;pointer-events:all}.default-point.hovered{color:#00c0ff}"]
                },] }
    ];
    DefaultLinkComponent.ctorParameters = function () { return [
        { type: i0.ChangeDetectorRef }
    ]; };
    DefaultLinkComponent.propDecorators = {
        labelLayer: [{ type: i0.ViewChild, args: ['labelLayer', { read: i0.ViewContainerRef, static: true },] }]
    };

    var DefaultLinkFactory = /** @class */ (function (_super) {
        __extends(DefaultLinkFactory, _super);
        function DefaultLinkFactory(resolver, renderer) {
            var _this = _super.call(this, 'default') || this;
            _this.resolver = resolver;
            _this.renderer = renderer;
            return _this;
        }
        DefaultLinkFactory.prototype.generateWidget = function (diagramEngine, link, linksHost) {
            var componentRef = linksHost.createComponent(this.getRecipe());
            // attach coordinates and default positional behaviour to the generated component host
            var rootNode = componentRef.location.nativeElement;
            // default style for link
            this.renderer.setStyle(rootNode, 'position', 'absolute');
            this.renderer.addClass(rootNode, 'label');
            // data attributes
            this.renderer.setAttribute(rootNode, 'data-linkid', link.id);
            // on destroy make sure to destroy the componentRef
            link.onEntityDestroy().subscribe(function () {
                componentRef.destroy();
            });
            // assign all passed properties to link initialization.
            Object.entries(link).forEach(function (_a) {
                var _b = __read(_a, 2), key = _b[0], value = _b[1];
                componentRef.instance[key] = value;
            });
            componentRef.instance.diagramEngine = diagramEngine;
            componentRef.instance.setPainted(true);
            return componentRef;
        };
        DefaultLinkFactory.prototype.getRecipe = function () {
            return this.resolver.resolveComponentFactory(DefaultLinkComponent);
        };
        DefaultLinkFactory.prototype.getNewInstance = function () {
            return new DefaultLinkModel();
        };
        return DefaultLinkFactory;
    }(AbstractLinkFactory));

    var DefaultPortModel = /** @class */ (function (_super) {
        __extends(DefaultPortModel, _super);
        function DefaultPortModel(_a) {
            var _b = _a === void 0 ? {} : _a, _c = _b.isInput, isInput = _c === void 0 ? true : _c, _d = _b.name, name = _d === void 0 ? 'port' : _d, _e = _b.type, type = _e === void 0 ? 'default' : _e, _f = _b.id, id = _f === void 0 ? null : _f, _g = _b.label, label = _g === void 0 ? null : _g, _h = _b.linkType, linkType = _h === void 0 ? 'default' : _h, _j = _b.maximumLinks, maximumLinks = _j === void 0 ? null : _j;
            var _this = _super.call(this, name, type, id, null, linkType) || this;
            _this.in = isInput;
            _this.label = label || name;
            if (_this.in) {
                _this.setCanCreateLinks(false);
            }
            else {
                _this.setMaximumLinks(maximumLinks);
                _this.setMagnetic(false);
            }
            return _this;
        }
        DefaultPortModel.prototype.link = function (port) {
            if (_super.prototype.getCanCreateLinks.call(this)) {
                var link = new DefaultLinkModel({ type: this.getLinkType() });
                link.setSourcePort(this);
                link.setTargetPort(port);
                return link;
            }
            return null;
        };
        DefaultPortModel.prototype.createLinkModel = function () {
            if (_super.prototype.getCanCreateLinks.call(this)) {
                return new DefaultLinkModel({ type: this.getLinkType() });
            }
        };
        DefaultPortModel.prototype.canLinkToPort = function (port) {
            if (port instanceof DefaultPortModel) {
                return this.in !== port.in;
            }
            return true;
        };
        return DefaultPortModel;
    }(PortModel));

    var DefaultNodeModel = /** @class */ (function (_super) {
        __extends(DefaultNodeModel, _super);
        function DefaultNodeModel(_a) {
            var _b = _a === void 0 ? {} : _a, _c = _b.name, name = _c === void 0 ? 'Untitled' : _c, _d = _b.type, type = _d === void 0 ? 'default' : _d, _e = _b.color, color = _e === void 0 ? 'rgb(0, 192, 255)' : _e, id = _b.id;
            var _this = _super.call(this, type, id) || this;
            _this.name = name;
            _this.color = color;
            _this.height$ = _this.selectHeight();
            _this.width$ = _this.selectWidth();
            return _this;
        }
        DefaultNodeModel.prototype.addInPort = function (_a) {
            var name = _a.name, _b = _a.type, type = _b === void 0 ? 'default' : _b, id = _a.id, _c = _a.linkType, linkType = _c === void 0 ? 'default' : _c;
            var port = new DefaultPortModel({
                isInput: true,
                name: name,
                type: type,
                id: id,
                label: null,
                linkType: linkType
            });
            this.addPort(port);
            return port;
        };
        DefaultNodeModel.prototype.addOutPort = function (_a) {
            var name = _a.name, _b = _a.type, type = _b === void 0 ? 'default' : _b, id = _a.id, _c = _a.linkType, linkType = _c === void 0 ? 'default' : _c, _d = _a.maximumLinks, maximumLinks = _d === void 0 ? null : _d;
            var port = new DefaultPortModel({
                isInput: false,
                name: name,
                type: type,
                id: id,
                label: null,
                linkType: linkType,
                maximumLinks: maximumLinks
            });
            this.addPort(port);
            return port;
        };
        return DefaultNodeModel;
    }(NodeModel));

    var DefaultNodeComponent = /** @class */ (function (_super) {
        __extends(DefaultNodeComponent, _super);
        function DefaultNodeComponent() {
            return _super.call(this, { type: 'ngdx-default-node' }) || this;
        }
        DefaultNodeComponent.prototype.ngOnInit = function () {
            var _this = this;
            // when node is painted and port isn't, draw ports
            this.paintChanges()
                .pipe(operators.filter(function (paintedE) { return paintedE.isPainted; }), operators.switchMap(function () { return _this.selectPorts(); }))
                .subscribe(function (ports) {
                ports.forEach(function (port) {
                    if (!port.getPainted()) {
                        _this.generatePort(port);
                    }
                });
            });
        };
        DefaultNodeComponent.prototype.generatePort = function (port) {
            var diagramEngine = this.getDiagramEngine();
            diagramEngine.generateWidgetForPort(port, this.portsLayer);
            port.paintChanges().subscribe(function (paintedEvent) {
                if (paintedEvent.isPainted) {
                    port.updateCoords(diagramEngine.getPortCoords(port));
                }
            });
        };
        return DefaultNodeComponent;
    }(DefaultNodeModel));
    DefaultNodeComponent.decorators = [
        { type: i0.Component, args: [{
                    selector: 'ngdx-default-node',
                    template: "<div\n  class=\"default-node\"\n  [ngStyle]=\"{\n    'height.px': height$ | async,\n    'width.px': width$ | async,\n    'background-color': color\n  }\"\n>\n  <div class=\"ports\">\n    <ng-template #portsLayer></ng-template>\n  </div>\n</div>\n",
                    changeDetection: i0.ChangeDetectionStrategy.OnPush,
                    styles: [":host.selected .default-node{background-color:#fff!important;border-color:#00f}:host .default-node{border-radius:10px;cursor:auto;pointer-events:all;transition:all .3s ease-in-out;width:100%}:host .default-node .title{border-bottom:1px solid #00f;padding:10px;text-align:center}:host .ports{display:flex;flex-direction:column;flex-grow:1}"]
                },] }
    ];
    DefaultNodeComponent.ctorParameters = function () { return []; };
    DefaultNodeComponent.propDecorators = {
        portsLayer: [{ type: i0.ViewChild, args: ['portsLayer', { read: i0.ViewContainerRef, static: true },] }]
    };

    var AbstractNodeFactory = /** @class */ (function (_super) {
        __extends(AbstractNodeFactory, _super);
        function AbstractNodeFactory() {
            return _super !== null && _super.apply(this, arguments) || this;
        }
        return AbstractNodeFactory;
    }(AbstractFactory));

    var DefaultNodeFactory = /** @class */ (function (_super) {
        __extends(DefaultNodeFactory, _super);
        function DefaultNodeFactory(resolver, renderer) {
            var _this = _super.call(this, 'default') || this;
            _this.resolver = resolver;
            _this.renderer = renderer;
            return _this;
        }
        DefaultNodeFactory.prototype.generateWidget = function (diagramEngine, node, nodesHost) {
            var _this = this;
            var componentRef = nodesHost.createComponent(this.getRecipe());
            // attach coordinates and default positional behaviour to the generated component host
            var rootNode = componentRef.location.nativeElement;
            // default style for node
            this.renderer.setStyle(rootNode, 'position', 'absolute');
            this.renderer.setStyle(rootNode, 'display', 'block');
            // data attributes
            this.renderer.setAttribute(rootNode, 'data-nodeid', node.id);
            // subscribe to node coordinates
            node.selectCoords().subscribe(function (_a) {
                var x = _a.x, y = _a.y;
                _this.renderer.setStyle(rootNode, 'left', x + "px");
                _this.renderer.setStyle(rootNode, 'top', y + "px");
            });
            node.selectionChanges().subscribe(function (e) {
                e.isSelected
                    ? _this.renderer.addClass(rootNode, 'selected')
                    : _this.renderer.removeClass(rootNode, 'selected');
            });
            node.onEntityDestroy().subscribe(function () {
                componentRef.destroy();
            });
            // assign all passed properties to node initialization.
            Object.entries(node).forEach(function (_a) {
                var _b = __read(_a, 2), key = _b[0], value = _b[1];
                componentRef.instance[key] = value;
            });
            componentRef.instance.setDiagramEngine(diagramEngine);
            componentRef.instance.setPainted(true);
            return componentRef;
        };
        DefaultNodeFactory.prototype.getRecipe = function () {
            return this.resolver.resolveComponentFactory(DefaultNodeComponent);
        };
        DefaultNodeFactory.prototype.getNewInstance = function (initialConfig) {
            return new DefaultNodeModel(initialConfig);
        };
        return DefaultNodeFactory;
    }(AbstractNodeFactory));

    var AbstractPortFactory = /** @class */ (function (_super) {
        __extends(AbstractPortFactory, _super);
        function AbstractPortFactory() {
            return _super !== null && _super.apply(this, arguments) || this;
        }
        return AbstractPortFactory;
    }(AbstractFactory));

    var DefaultPortComponent = /** @class */ (function (_super) {
        __extends(DefaultPortComponent, _super);
        function DefaultPortComponent() {
            return _super.call(this) || this;
        }
        return DefaultPortComponent;
    }(DefaultPortModel));
    DefaultPortComponent.decorators = [
        { type: i0.Component, args: [{
                    selector: 'ngdx-default-port',
                    template: "<div [ngClass]=\"in ? 'in' : 'out'\" draggable=\"false\"></div>\n",
                    changeDetection: i0.ChangeDetectionStrategy.OnPush,
                    styles: [":host{background-color:#d3d3d3;border:1px solid grey;border-radius:60px;height:10px;margin:10px 0;position:relative;width:10px}:host:hover{background-color:green}:host.out{right:-195px}:host.in{left:-5px}"]
                },] }
    ];
    DefaultPortComponent.ctorParameters = function () { return []; };

    var DefaultPortFactory = /** @class */ (function (_super) {
        __extends(DefaultPortFactory, _super);
        function DefaultPortFactory(resolver, renderer) {
            var _this = _super.call(this, 'default') || this;
            _this.resolver = resolver;
            _this.renderer = renderer;
            return _this;
        }
        DefaultPortFactory.prototype.generateWidget = function (port, portsHost) {
            var componentRef = portsHost.createComponent(this.getRecipe());
            // attach coordinates and default positional behaviour to the generated component host
            var rootNode = componentRef.location.nativeElement;
            // data attributes
            this.renderer.setAttribute(rootNode, 'data-portid', port.id);
            this.renderer.setAttribute(rootNode, 'data-name', port.getName());
            port.in
                ? this.renderer.addClass(rootNode, 'in')
                : this.renderer.addClass(rootNode, 'out');
            // assign all passed properties to node initialization.
            Object.entries(port).forEach(function (_a) {
                var _b = __read(_a, 2), key = _b[0], value = _b[1];
                componentRef.instance[key] = value;
            });
            port.onEntityDestroy().subscribe(function () {
                componentRef.destroy();
            });
            componentRef.instance.setPainted(true);
            return componentRef;
        };
        DefaultPortFactory.prototype.getRecipe = function () {
            return this.resolver.resolveComponentFactory(DefaultPortComponent);
        };
        DefaultPortFactory.prototype.getNewInstance = function (initialConfig) {
            return new DefaultPortModel(Object.assign({ isInput: true, name: 'unknown' }, initialConfig));
        };
        return DefaultPortFactory;
    }(AbstractPortFactory));

    var DiagramModel = /** @class */ (function (_super) {
        __extends(DiagramModel, _super);
        function DiagramModel(diagramEngine, id, logPrefix) {
            if (logPrefix === void 0) { logPrefix = '[Diagram]'; }
            var _this = _super.call(this, id, logPrefix) || this;
            _this.diagramEngine = diagramEngine;
            _this.nodes$ = createEntityState([], _this.entityPipe('nodes'));
            _this.links$ = createEntityState([], _this.entityPipe('links'));
            _this.offsetX$ = createValueState(0, _this.entityPipe('offsetX'));
            _this.offsetY$ = createValueState(0, _this.entityPipe('offsetY'));
            _this.zoom$ = createValueState(100, _this.entityPipe('zoom'));
            _this.maxZoomOut$ = createValueState(null);
            _this.maxZoomIn$ = createValueState(null);
            _this.gridSize$ = createValueState(0);
            return _this;
        }
        // TODO: support the following events for links and nodes
        // removed, updated<positionChanged/dataChanged>, added
        DiagramModel.prototype.getNodes = function () {
            return this.nodes$.value;
        };
        DiagramModel.prototype.getNodesArray = function () {
            return this.nodes$.array();
        };
        DiagramModel.prototype.getNode = function (id) {
            return this.nodes$.get(id);
        };
        DiagramModel.prototype.getLink = function (id) {
            return this.links$.get(id);
        };
        DiagramModel.prototype.getLinks = function () {
            return this.links$.value;
        };
        DiagramModel.prototype.getLinksArray = function () {
            return this.links$.array();
        };
        DiagramModel.prototype.getAllPorts = function (options) {
            if (options === void 0) { options = {}; }
            var result = new Map();
            this.getNodes().forEach(function (node) {
                var ports = options.filter ? node.getPortsArray().filter(options.filter) : node.getPortsArray();
                ports.forEach(function (port) { return result.set(port.id, port); });
            });
            return result;
        };
        /**
         * Add a node to the diagram
         * @returns Inserted Node
         */
        DiagramModel.prototype.addNode = function (node) {
            this.nodes$.add(node).emit();
            return node;
        };
        /**
         * Delete a node from the diagram
         */
        DiagramModel.prototype.deleteNode = function (nodeOrId) {
            var e_1, _a, e_2, _b;
            var nodeId = typeof nodeOrId === 'string' ? nodeOrId : nodeOrId.id;
            var node = this.getNode(nodeId);
            try {
                for (var _c = __values(node.getPorts().values()), _d = _c.next(); !_d.done; _d = _c.next()) {
                    var port = _d.value;
                    try {
                        for (var _e = (e_2 = void 0, __values(port.getLinks().values())), _f = _e.next(); !_f.done; _f = _e.next()) {
                            var link = _f.value;
                            this.deleteLink(link);
                        }
                    }
                    catch (e_2_1) { e_2 = { error: e_2_1 }; }
                    finally {
                        try {
                            if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
                        }
                        finally { if (e_2) throw e_2.error; }
                    }
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
                }
                finally { if (e_1) throw e_1.error; }
            }
            this.nodes$.remove(nodeId).emit();
        };
        /**
         * Get nodes as observable, use `.getValue()` for snapshot
         */
        DiagramModel.prototype.selectNodes = function () {
            return this.nodes$.value$;
        };
        /**
         * Add link
         * @returns Newly created link
         */
        DiagramModel.prototype.addLink = function (link) {
            this.links$.add(link).emit();
            return link;
        };
        /**
         * Delete link
         */
        DiagramModel.prototype.deleteLink = function (linkOrId) {
            var linkId = typeof linkOrId === 'string' ? linkOrId : linkOrId.id;
            this.links$.remove(linkId).emit();
        };
        DiagramModel.prototype.reset = function () {
            this.nodes$.clear().emit();
            this.links$.clear().emit();
        };
        /**
         * Get links behaviour subject, use `.getValue()` for snapshot
         */
        DiagramModel.prototype.selectLinks = function () {
            return this.links$.value$;
        };
        // /**
        //  * Serialize the diagram model to JSON
        //  * @returns diagram model as a string
        //  */
        DiagramModel.prototype.serialize = function () {
            var serializedNodes = this.nodes$.map(function (node) { return node.serialize(); });
            var serializedLinks = this.links$.map(function (link) { return link.serialize(); });
            return Object.assign(Object.assign({}, _super.prototype.serialize.call(this)), { nodes: serializedNodes, links: serializedLinks });
        };
        DiagramModel.prototype.setMaxZoomOut = function (maxZoomOut) {
            this.maxZoomOut$.set(maxZoomOut).emit();
        };
        DiagramModel.prototype.setMaxZoomIn = function (maxZoomIn) {
            this.maxZoomIn$.set(maxZoomIn).emit();
        };
        DiagramModel.prototype.getMaxZoomOut = function () {
            return this.maxZoomOut$.value;
        };
        DiagramModel.prototype.getMaxZoomIn = function () {
            return this.maxZoomIn$.value;
        };
        DiagramModel.prototype.setOffset = function (x, y) {
            this.offsetX$.set(x).emit();
            this.offsetY$.set(y).emit();
        };
        DiagramModel.prototype.setOffsetX = function (x) {
            this.offsetX$.set(x).emit();
        };
        DiagramModel.prototype.getOffsetX = function () {
            return this.offsetX$.value;
        };
        DiagramModel.prototype.selectOffsetX = function () {
            return this.offsetX$.value$;
        };
        DiagramModel.prototype.setOffsetY = function (y) {
            this.offsetY$.set(y).emit();
        };
        DiagramModel.prototype.getOffsetY = function () {
            return this.offsetY$.value;
        };
        DiagramModel.prototype.selectOffsetY = function () {
            return this.offsetY$.value$;
        };
        DiagramModel.prototype.setZoomLevel = function (z) {
            var maxZoomIn = this.getMaxZoomIn();
            var maxZoomOut = this.getMaxZoomOut();
            // check if zoom levels exceeded defined boundaries
            if ((maxZoomIn && z > maxZoomIn) || (maxZoomOut && z < maxZoomOut)) {
                return;
            }
            this.zoom$.set(z).emit();
        };
        DiagramModel.prototype.getZoomLevel = function () {
            return this.zoom$.value;
        };
        DiagramModel.prototype.selectZoomLevel = function () {
            return this.zoom$.value$;
        };
        DiagramModel.prototype.getDiagramEngine = function () {
            return this.diagramEngine;
        };
        DiagramModel.prototype.clearSelection = function (ignore) {
            if (ignore === void 0) { ignore = null; }
            this.getSelectedItems().forEach(function (element) {
                if ((ignore === null || ignore === void 0 ? void 0 : ignore.id) === element.id) {
                    return;
                }
                element.setSelected(false);
            });
        };
        DiagramModel.prototype.getGridPosition = function (_a) {
            var x = _a.x, y = _a.y;
            var gridSize = this.gridSize$.value;
            if (gridSize === 0) {
                return { x: x, y: y };
            }
            return {
                x: gridSize * Math.floor((x + gridSize / 2) / gridSize),
                y: gridSize * Math.floor((y + gridSize / 2) / gridSize)
            };
        };
        DiagramModel.prototype.getSelectedItems = function () {
            var e_3, _a;
            var filters = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                filters[_i] = arguments[_i];
            }
            filters = coerceArray(filters);
            var items = [];
            var nodes = this.nodes$.array();
            var links = this.links$.array();
            var selectedNodes = function () { return nodes.flatMap(function (node) { return node.getSelectedEntities(); }); };
            var selectedPorts = function () { return nodes.flatMap(function (node) { return node.getPortsArray().flatMap(function (port) { return port.getSelectedEntities(); }); }); };
            var selectedLinks = function () { return links.flatMap(function (link) { return link.getSelectedEntities(); }); };
            var selectedPoints = function () { return links.flatMap(function (link) { return link.getPoints().flatMap(function (point) { return point.getSelectedEntities(); }); }); };
            if (isEmptyArray(filters)) {
                items.push.apply(items, __spread(selectedNodes(), selectedPorts(), selectedLinks(), selectedPoints()));
            }
            else {
                var byType = {
                    node: selectedNodes,
                    port: selectedPorts,
                    link: selectedLinks,
                    point: selectedPoints
                };
                try {
                    for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) {
                        var type = filters_1_1.value;
                        items.push.apply(items, __spread(byType[type]()));
                    }
                }
                catch (e_3_1) { e_3 = { error: e_3_1 }; }
                finally {
                    try {
                        if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1);
                    }
                    finally { if (e_3) throw e_3.error; }
                }
            }
            return unique(items);
        };
        DiagramModel.prototype.addAll = function () {
            var e_4, _a;
            var models = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                models[_i] = arguments[_i];
            }
            var links = [];
            var nodes = [];
            try {
                for (var models_1 = __values(models), models_1_1 = models_1.next(); !models_1_1.done; models_1_1 = models_1.next()) {
                    var model = models_1_1.value;
                    if (model instanceof LinkModel) {
                        links.push(model);
                    }
                    else if (model instanceof NodeModel) {
                        nodes.push(model);
                    }
                }
            }
            catch (e_4_1) { e_4 = { error: e_4_1 }; }
            finally {
                try {
                    if (models_1_1 && !models_1_1.done && (_a = models_1.return)) _a.call(models_1);
                }
                finally { if (e_4) throw e_4.error; }
            }
            this.addLinks(links);
            this.addNodes(nodes);
            return models;
        };
        DiagramModel.prototype.addLinks = function (links) {
            this.links$.addMany(links).emit();
        };
        DiagramModel.prototype.addNodes = function (nodes) {
            this.nodes$.addMany(nodes).emit();
        };
        DiagramModel.prototype.destroy = function () {
            _super.prototype.destroy.call(this);
            this.nodes$.destroy();
            this.links$.destroy();
        };
        return DiagramModel;
    }(BaseEntity));

    var TemplateVarContext = /** @class */ (function () {
        function TemplateVarContext() {
        }
        return TemplateVarContext;
    }());
    var TemplateVarDirective = /** @class */ (function () {
        function TemplateVarDirective(vcRef, templateRef) {
            this.vcRef = vcRef;
            this.templateRef = templateRef;
            this.context = new TemplateVarContext();
        }
        Object.defineProperty(TemplateVarDirective.prototype, "templateVar", {
            set: function (context) {
                this.context.$implicit = this.context.templateVar = context;
                this.updateView();
            },
            enumerable: false,
            configurable: true
        });
        TemplateVarDirective.prototype.updateView = function () {
            if (!this.view) {
                this.vcRef.clear();
                this.view = this.vcRef.createEmbeddedView(this.templateRef, this.context);
            }
            else {
                this.view.markForCheck();
            }
        };
        return TemplateVarDirective;
    }());
    TemplateVarDirective.decorators = [
        { type: i0.Directive, args: [{
                    selector: '[templateVar]',
                },] }
    ];
    TemplateVarDirective.ctorParameters = function () { return [
        { type: i0.ViewContainerRef },
        { type: i0.TemplateRef }
    ]; };
    TemplateVarDirective.propDecorators = {
        templateVar: [{ type: i0.Input }]
    };

    var NgxDiagramsModule = /** @class */ (function () {
        function NgxDiagramsModule() {
        }
        return NgxDiagramsModule;
    }());
    NgxDiagramsModule.decorators = [
        { type: i0.NgModule, args: [{
                    declarations: [NgxDiagramComponent, TemplateVarDirective],
                    imports: [common.CommonModule],
                    exports: [NgxDiagramComponent],
                },] }
    ];

    var DiagramEngine = /** @class */ (function () {
        function DiagramEngine(resolver, rendererFactory) {
            this.resolver = resolver;
            this.rendererFactory = rendererFactory;
            this.nodeFactories = new Map();
            this.labelFactories = new Map();
            this.linkFactories = new Map();
            this.portFactories = new Map();
            this.canvas$ = createValueState(null);
            // calculated only when smart routing is active
            this.canvasMatrix = [];
            this.routingMatrix = [];
            // used when at least one element has negative coordinates
            this.hAdjustmentFactor = 0;
            this.vAdjustmentFactor = 0;
            this.markMatrixPoint = function (matrix, x, y) {
                if (matrix[y] !== undefined && matrix[y][x] !== undefined) {
                    matrix[y][x] = 1;
                }
            };
            this._renderer = this.rendererFactory.createRenderer(null, null);
        }
        DiagramEngine.prototype.createDiagram = function () {
            this.diagramModel = new DiagramModel(this);
            return this.diagramModel;
        };
        DiagramEngine.prototype.registerDefaultFactories = function () {
            this.registerNodeFactory(new DefaultNodeFactory(this.resolver, this._renderer));
            this.registerPortFactory(new DefaultPortFactory(this.resolver, this._renderer));
            this.registerLinkFactory(new DefaultLinkFactory(this.resolver, this._renderer));
            this.registerLabelFactory(new DefaultLabelFactory(this.resolver, this._renderer));
        };
        //#region Factories
        // LABELS
        DiagramEngine.prototype.registerLabelFactory = function (labelFactory) {
            this.labelFactories.set(labelFactory.type, labelFactory);
        };
        DiagramEngine.prototype.getLabelFactories = function () {
            return this.labelFactories;
        };
        DiagramEngine.prototype.getLabelFactory = function (type) {
            if (this.labelFactories.has(type)) {
                return this.labelFactories.get(type);
            }
            throw new Error("cannot find factory for node of type: [" + type + "]");
        };
        DiagramEngine.prototype.getFactoryForLabel = function (label) {
            return this.getLabelFactory(label.getType());
        };
        DiagramEngine.prototype.generateWidgetForLabel = function (label, labelHost) {
            var labelFactory = this.getFactoryForLabel(label);
            if (!labelFactory) {
                throw new Error("Cannot find widget factory for node: " + label.getType());
            }
            return labelFactory.generateWidget(label, labelHost);
        };
        // NODES
        DiagramEngine.prototype.registerNodeFactory = function (nodeFactory) {
            this.nodeFactories.set(nodeFactory.type, nodeFactory);
        };
        DiagramEngine.prototype.getNodeFactories = function () {
            return this.nodeFactories;
        };
        DiagramEngine.prototype.getNodeFactory = function (type) {
            if (this.nodeFactories.has(type)) {
                return this.nodeFactories.get(type);
            }
            throw new Error("cannot find factory for node of type: [" + type + "]");
        };
        DiagramEngine.prototype.getFactoryForNode = function (node) {
            return this.getNodeFactory(node.getType());
        };
        DiagramEngine.prototype.generateWidgetForNode = function (node, nodesHost) {
            var nodeFactory = this.getFactoryForNode(node);
            if (!nodeFactory) {
                throw new Error("Cannot find widget factory for node: " + node.getType());
            }
            return nodeFactory.generateWidget(this, node, nodesHost);
        };
        // PORTS
        DiagramEngine.prototype.registerPortFactory = function (factory) {
            this.portFactories.set(factory.type, factory);
        };
        DiagramEngine.prototype.getPortFactories = function () {
            return this.portFactories;
        };
        DiagramEngine.prototype.getPortFactory = function (type) {
            if (this.portFactories.has(type)) {
                return this.portFactories.get(type);
            }
            throw new Error("cannot find factory for port of type: [" + type + "]");
        };
        DiagramEngine.prototype.getFactoryForPort = function (port) {
            return this.getPortFactory(port.getType());
        };
        DiagramEngine.prototype.generateWidgetForPort = function (port, portsHost) {
            var portFactory = this.getFactoryForPort(port);
            if (!portFactory) {
                throw new Error("Cannot find widget factory for port: " + port.getType());
            }
            return portFactory.generateWidget(port, portsHost);
        };
        // LINKS
        DiagramEngine.prototype.getLinkFactories = function () {
            return this.linkFactories;
        };
        DiagramEngine.prototype.registerLinkFactory = function (factory) {
            this.linkFactories.set(factory.type, factory);
        };
        DiagramEngine.prototype.getLinkFactory = function (type) {
            if (this.linkFactories.has(type)) {
                return this.linkFactories.get(type);
            }
            throw new Error("cannot find factory for link of type: [" + type + "]");
        };
        DiagramEngine.prototype.getFactoryForLink = function (link) {
            return this.getLinkFactory(link.getType());
        };
        DiagramEngine.prototype.generateWidgetForLink = function (link, linksHost) {
            var linkFactory = this.getFactoryForLink(link);
            if (!linkFactory) {
                throw new Error("Cannot find link factory for link: " + link.getType());
            }
            return linkFactory.generateWidget(this, link, linksHost);
        };
        //#endregion
        DiagramEngine.prototype.getNodeElement = function (node) {
            var selector = this.canvas$.value.querySelector("[data-nodeid=\"" + node.id + "\"]");
            if (selector === null) {
                throw new Error('Cannot find Node element with node id: [' + node.id + ']');
            }
            return selector;
        };
        DiagramEngine.prototype.getNodePortElement = function (port) {
            var selector = this.canvas$.value.querySelector("[data-nodeid=\"" + port.getParent().id + "\"] [data-portid=\"" + port.id + "\"]");
            if (selector === null) {
                throw new Error('Cannot find Node Port element with node id: [' + port.getParent().id + '] and port id: [' + port.id + ']');
            }
            return selector;
        };
        DiagramEngine.prototype.getPortCenter = function (port) {
            var sourceElement = this.getNodePortElement(port);
            var sourceRect = sourceElement.getBoundingClientRect();
            var rel = this.getRelativePoint(sourceRect.left, sourceRect.top);
            return {
                x: sourceElement.offsetWidth / 2 +
                    (rel.x - this.diagramModel.getOffsetX()) / (this.diagramModel.getZoomLevel() / 100.0),
                y: sourceElement.offsetHeight / 2 +
                    (rel.y - this.diagramModel.getOffsetY()) / (this.diagramModel.getZoomLevel() / 100.0)
            };
        };
        /**
         * Calculate rectangular coordinates of the port passed in.
         */
        DiagramEngine.prototype.getPortCoords = function (port) {
            var sourceElement = this.getNodePortElement(port);
            var sourceRect = sourceElement.getBoundingClientRect();
            var canvasRect = this.canvas$.value.getBoundingClientRect();
            return {
                x: (sourceRect.x - this.diagramModel.getOffsetX()) / (this.diagramModel.getZoomLevel() / 100.0) - canvasRect.left,
                y: (sourceRect.y - this.diagramModel.getOffsetY()) / (this.diagramModel.getZoomLevel() / 100.0) - canvasRect.top,
                width: sourceRect.width,
                height: sourceRect.height
            };
        };
        /**
         * Determine the width and height of the node passed in.
         * It currently assumes nodes have a rectangular shape, can be overriden for customised shapes.
         */
        DiagramEngine.prototype.getNodeDimensions = function (node) {
            if (!this.canvas$.value) {
                return {
                    width: 0,
                    height: 0
                };
            }
            var nodeElement = this.getNodeElement(node);
            var nodeRect = nodeElement.getBoundingClientRect();
            return {
                width: nodeRect.width,
                height: nodeRect.height
            };
        };
        DiagramEngine.prototype.setCanvas = function (canvas) {
            this.canvas$.set(canvas).emit();
        };
        DiagramEngine.prototype.getRelativeMousePoint = function (event) {
            var point = this.getRelativePoint(event.clientX, event.clientY);
            return {
                x: (point.x - this.diagramModel.getOffsetX()) / (this.diagramModel.getZoomLevel() / 100.0),
                y: (point.y - this.diagramModel.getOffsetY()) / (this.diagramModel.getZoomLevel() / 100.0)
            };
        };
        DiagramEngine.prototype.getRelativePoint = function (x, y) {
            var canvasRect = this.canvas$.value.getBoundingClientRect();
            return { x: x - canvasRect.left, y: y - canvasRect.top };
        };
        DiagramEngine.prototype.getDiagramModel = function () {
            return this.diagramModel;
        };
        DiagramEngine.prototype.isModelLocked = function (model) {
            if (this.diagramModel.getLocked()) {
                return true;
            }
            return model.getLocked();
        };
        /**
         * fit the canvas zoom levels to the elements contained.
         * @param additionalZoomFactor allow for further zooming out to make sure edges doesn't cut
         */
        DiagramEngine.prototype.zoomToFit = function (additionalZoomFactor) {
            var _this = this;
            if (additionalZoomFactor === void 0) { additionalZoomFactor = 0.005; }
            this.canvas$.value$.pipe(operators.filter(Boolean), operators.take(1), operators.delay(0)).subscribe(function (canvas) {
                var xFactor = canvas.clientWidth / canvas.scrollWidth;
                var yFactor = canvas.clientHeight / canvas.scrollHeight;
                var zoomFactor = xFactor < yFactor ? xFactor : yFactor;
                var newZoomLvl = _this.diagramModel.getZoomLevel() * (zoomFactor - additionalZoomFactor);
                var maxZoomOut = _this.diagramModel.getMaxZoomOut();
                if (maxZoomOut && newZoomLvl < maxZoomOut) {
                    newZoomLvl = maxZoomOut;
                }
                _this.diagramModel.setZoomLevel(newZoomLvl);
                // TODO: either block the canvas movement on 0,0 or detect the top left furthest element and set the offest to its edges
                _this.diagramModel.setOffset(0, 0);
            });
        };
        // SMART ROUTING
        DiagramEngine.prototype.setSmartRoutingStatus = function (status) {
            if (status && !this.pathFinding) {
                this.pathFinding = new PathFinding(this);
            }
            else {
                this.pathFinding = null;
            }
            this.smartRouting = status;
        };
        DiagramEngine.prototype.getPathfinding = function () {
            return this.pathFinding;
        };
        DiagramEngine.prototype.calculateCanvasMatrix = function () {
            var _a = this.calculateMatrixDimensions(), canvasWidth = _a.width, hAdjustmentFactor = _a.hAdjustmentFactor, canvasHeight = _a.height, vAdjustmentFactor = _a.vAdjustmentFactor;
            this.hAdjustmentFactor = hAdjustmentFactor;
            this.vAdjustmentFactor = vAdjustmentFactor;
            var matrixWidth = Math.ceil(canvasWidth / ROUTING_SCALING_FACTOR);
            var matrixHeight = Math.ceil(canvasHeight / ROUTING_SCALING_FACTOR);
            this.canvasMatrix = Array.from({ length: matrixHeight }, function (_, i) { return i + 1; }).map(function () {
                return new Array(matrixWidth).fill(0);
            });
        };
        /**
         * Despite being a long method, we simply iterate over all three collections (nodes, ports and points)
         * to find the highest X and Y dimensions, so we can build the matrix large enough to contain all elements.
         */
        DiagramEngine.prototype.calculateMatrixDimensions = function () {
            var allNodesCoords = this.diagramModel.getNodesArray().map(function (item) { return ({
                x: item.getCoords().x,
                width: item.getWidth(),
                y: item.getCoords().y,
                height: item.getHeight()
            }); });
            var allLinks = this.diagramModel.getLinksArray();
            var allPortsCoords = allLinks
                .flatMap(function (link) { return [link.getSourcePort(), link.getTargetPort()]; })
                .filter(function (port) { return port !== null; })
                .map(function (item) { return ({
                x: item.getX(),
                width: item.getWidth(),
                y: item.getY(),
                height: item.getHeight()
            }); });
            var allPointsCoords = allLinks
                .flatMap(function (link) { return link.getPoints(); })
                .map(function (item) { return ({
                // points don't have width/height, so let's just use 0
                x: item.getCoords().x,
                width: 0,
                y: item.getCoords().y,
                height: 0
            }); });
            var canvas = this.canvas$.value;
            var allElements = allNodesCoords.concat(allPortsCoords, allPointsCoords);
            var minX = Math.floor(Math.min(allElements.reduce(function (a, b) {
                return a.x <= b.x ? a : b;
            }).x, 0) / ROUTING_SCALING_FACTOR) * ROUTING_SCALING_FACTOR;
            var maxXElement = allElements.reduce(function (a, b) {
                return a.x + a.width >= b.x + b.width ? a : b;
            });
            var maxX = Math.max(maxXElement.x + maxXElement.width, canvas.offsetWidth);
            var minY = Math.floor(Math.min(allElements.reduce(function (a, b) {
                return a.y <= b.y ? a : b;
            }).y, 0) / ROUTING_SCALING_FACTOR) * ROUTING_SCALING_FACTOR;
            var maxYElement = allElements.reduce(function (a, b) {
                return a.y + a.height >= b.y + b.height ? a : b;
            });
            var maxY = Math.max(maxYElement.y + maxYElement.height, canvas.offsetWidth);
            var width = Math.ceil(Math.abs(minX) + maxX);
            var height = Math.ceil(Math.abs(minY) + maxY);
            return {
                width: width,
                hAdjustmentFactor: Math.abs(minX) / ROUTING_SCALING_FACTOR + 1,
                height: height,
                vAdjustmentFactor: Math.abs(minY) / ROUTING_SCALING_FACTOR + 1
            };
        };
        /**
         * A representation of the canvas in the following format:
         *
         * +-----------------+
         * | 0 0 0 0 0 0 0 0 |
         * | 0 0 0 0 0 0 0 0 |
         * | 0 0 0 0 0 0 0 0 |
         * | 0 0 0 0 0 0 0 0 |
         * | 0 0 0 0 0 0 0 0 |
         * +-----------------+
         *
         * In which all walkable points are marked by zeros.
         * It uses @link{#ROUTING_SCALING_FACTOR} to reduce the matrix dimensions and improve performance.
         */
        DiagramEngine.prototype.getCanvasMatrix = function () {
            if (this.canvasMatrix.length === 0) {
                this.calculateCanvasMatrix();
            }
            return this.canvasMatrix;
        };
        /**
         * The routing matrix does not have negative indexes, but elements could be negatively positioned.
         * We use the functions below to translate back and forth between these coordinates, relying on the
         * calculated values of hAdjustmentFactor and vAdjustmentFactor.
         */
        DiagramEngine.prototype.translateRoutingX = function (x, reverse) {
            if (reverse === void 0) { reverse = false; }
            return x + this.hAdjustmentFactor * (reverse ? -1 : 1);
        };
        DiagramEngine.prototype.translateRoutingY = function (y, reverse) {
            if (reverse === void 0) { reverse = false; }
            return y + this.vAdjustmentFactor * (reverse ? -1 : 1);
        };
        /**
         * A representation of the canvas in the following format:
         *
         * +-----------------+
         * | 0 0 1 1 0 0 0 0 |
         * | 0 0 1 1 0 0 1 1 |
         * | 0 0 0 0 0 0 1 1 |
         * | 1 1 0 0 0 0 0 0 |
         * | 1 1 0 0 0 0 0 0 |
         * +-----------------+
         *
         * In which all points blocked by a node (and its ports) are
         * marked as 1; points were there is nothing (ie, free) receive 0.
         */
        DiagramEngine.prototype.getRoutingMatrix = function () {
            if (this.routingMatrix.length === 0) {
                this.calculateRoutingMatrix();
            }
            return this.routingMatrix;
        };
        DiagramEngine.prototype.calculateRoutingMatrix = function () {
            var matrix = this.getCanvasMatrix().map(function (item) { return item.slice(0); });
            // nodes need to be marked as blocked points
            this.markNodes(matrix);
            // same thing for ports
            this.markPorts(matrix);
            this.routingMatrix = matrix;
        };
        DiagramEngine.prototype.getSmartRouting = function () {
            return !!this.smartRouting;
        };
        /**
         * Updates (by reference) where nodes will be drawn on the matrix passed in.
         */
        DiagramEngine.prototype.markNodes = function (matrix) {
            var _this = this;
            this.diagramModel.getNodes().forEach(function (node) {
                var startX = Math.floor(node.getCoords().x / ROUTING_SCALING_FACTOR);
                var endX = Math.ceil((node.getCoords().x + node.getWidth()) / ROUTING_SCALING_FACTOR);
                var startY = Math.floor(node.getCoords().y / ROUTING_SCALING_FACTOR);
                var endY = Math.ceil((node.getCoords().y + node.getHeight()) / ROUTING_SCALING_FACTOR);
                for (var x = startX - 1; x <= endX + 1; x++) {
                    for (var y = startY - 1; y < endY + 1; y++) {
                        _this.markMatrixPoint(matrix, _this.translateRoutingX(x), _this.translateRoutingY(y));
                    }
                }
            });
        };
        /**
         * Updates (by reference) where ports will be drawn on the matrix passed in.
         */
        DiagramEngine.prototype.markPorts = function (matrix) {
            var _this = this;
            var allElements = this.diagramModel
                .getLinksArray()
                .flatMap(function (link) { return [link.getSourcePort(), link.getTargetPort()]; });
            allElements
                .filter(function (port) { return port !== null; })
                .forEach(function (port) {
                var startX = Math.floor(port.getX() / ROUTING_SCALING_FACTOR);
                var endX = Math.ceil((port.getX() + port.getWidth()) / ROUTING_SCALING_FACTOR);
                var startY = Math.floor(port.getY() / ROUTING_SCALING_FACTOR);
                var endY = Math.ceil((port.getY() + port.getHeight()) / ROUTING_SCALING_FACTOR);
                for (var x = startX - 1; x <= endX + 1; x++) {
                    for (var y = startY - 1; y < endY + 1; y++) {
                        _this.markMatrixPoint(matrix, _this.translateRoutingX(x), _this.translateRoutingY(y));
                    }
                }
            });
        };
        return DiagramEngine;
    }());
    DiagramEngine.ɵprov = i0.ɵɵdefineInjectable({ factory: function DiagramEngine_Factory() { return new DiagramEngine(i0.ɵɵinject(i0.ComponentFactoryResolver), i0.ɵɵinject(i0.RendererFactory2)); }, token: DiagramEngine, providedIn: NgxDiagramsModule });
    DiagramEngine.decorators = [
        { type: i0.Injectable, args: [{ providedIn: NgxDiagramsModule },] }
    ];
    DiagramEngine.ctorParameters = function () { return [
        { type: i0.ComponentFactoryResolver },
        { type: i0.RendererFactory2 }
    ]; };

    var DefaultLinkModule = /** @class */ (function () {
        function DefaultLinkModule() {
        }
        return DefaultLinkModule;
    }());
    DefaultLinkModule.decorators = [
        { type: i0.NgModule, args: [{
                    declarations: [DefaultLinkComponent],
                    imports: [common.CommonModule],
                    exports: [DefaultLinkComponent],
                    entryComponents: [DefaultLinkComponent],
                },] }
    ];

    var DefaultNodeModule = /** @class */ (function () {
        function DefaultNodeModule() {
        }
        return DefaultNodeModule;
    }());
    DefaultNodeModule.decorators = [
        { type: i0.NgModule, args: [{
                    declarations: [DefaultNodeComponent],
                    imports: [common.CommonModule],
                    exports: [DefaultNodeComponent],
                    entryComponents: [DefaultNodeComponent],
                },] }
    ];

    var DefaultPortModule = /** @class */ (function () {
        function DefaultPortModule() {
        }
        return DefaultPortModule;
    }());
    DefaultPortModule.decorators = [
        { type: i0.NgModule, args: [{
                    declarations: [DefaultPortComponent],
                    imports: [common.CommonModule],
                    exports: [DefaultPortComponent],
                    entryComponents: [DefaultPortComponent]
                },] }
    ];

    var DefaultLabelModule = /** @class */ (function () {
        function DefaultLabelModule() {
        }
        return DefaultLabelModule;
    }());
    DefaultLabelModule.decorators = [
        { type: i0.NgModule, args: [{
                    declarations: [DefaultLabelComponent],
                    imports: [common.CommonModule],
                    exports: [DefaultLabelComponent],
                    entryComponents: [DefaultLabelComponent],
                },] }
    ];

    var DagreEngine = /** @class */ (function () {
        function DagreEngine() {
        }
        DagreEngine.prototype.instantiate = function () {
            try {
                this.g = new dagre.graphlib.Graph({ multigraph: true });
            }
            catch (error) {
                console.warn("`dagre` packages isn't installed, please install it before using the DagreEngine plugin");
            }
        };
        DagreEngine.prototype.redistribute = function (model, options) {
            var _this = this;
            if (options === void 0) { options = {}; }
            this.instantiate();
            this.g.setGraph(options.graph || {});
            this.g.setDefaultEdgeLabel(function () {
                return {};
            });
            var processedlinks = {};
            // set nodes
            model.getNodes().forEach(function (node) {
                _this.g.setNode(node.id, {
                    width: node.getWidth(),
                    height: node.getHeight()
                });
            });
            model.getLinks().forEach(function (link) {
                // set edges
                if (link.getSourcePort() && link.getTargetPort()) {
                    processedlinks[link.id] = true;
                    _this.g.setEdge({
                        v: link.getSourcePort().getNode().id,
                        w: link.getTargetPort().getNode().id,
                        name: link.id
                    });
                }
            });
            // layout the graph
            dagre.layout(this.g, options.layout);
            this.g.nodes().forEach(function (v) {
                var _a = _this.g.node(v), x = _a.x, y = _a.y;
                model.getNode(v).setCoords({ x: x, y: y });
            });
            // also include links?
            if (options.includeLinks) {
                this.g.edges().forEach(function (e) {
                    var edge = _this.g.edge(e);
                    var link = model.getLink(e.name);
                    var points = [link.getFirstPoint()];
                    for (var i = 1; i < edge.points.length - 2; i++) {
                        points.push(new PointModel(link, { x: edge.points[i].x, y: edge.points[i].y }));
                    }
                    link.setPoints(points.concat(link.getLastPoint()));
                });
            }
        };
        return DagreEngine;
    }());
    DagreEngine.decorators = [
        { type: i0.Injectable }
    ];

    /*
     * Public API Surface of ngx-diagrams library
     */

    /**
     * Generated bundle index. Do not edit.
     */

    exports.AbstractFactory = AbstractFactory;
    exports.AbstractLabelFactory = AbstractLabelFactory;
    exports.AbstractLinkFactory = AbstractLinkFactory;
    exports.AbstractNodeFactory = AbstractNodeFactory;
    exports.AbstractPortFactory = AbstractPortFactory;
    exports.BaseAction = BaseAction;
    exports.BaseEvent = BaseEvent;
    exports.BaseModel = BaseModel;
    exports.DagreEngine = DagreEngine;
    exports.DefaultLabelComponent = DefaultLabelComponent;
    exports.DefaultLabelFactory = DefaultLabelFactory;
    exports.DefaultLabelModel = DefaultLabelModel;
    exports.DefaultLabelModule = DefaultLabelModule;
    exports.DefaultLinkComponent = DefaultLinkComponent;
    exports.DefaultLinkFactory = DefaultLinkFactory;
    exports.DefaultLinkModel = DefaultLinkModel;
    exports.DefaultLinkModule = DefaultLinkModule;
    exports.DefaultNodeComponent = DefaultNodeComponent;
    exports.DefaultNodeFactory = DefaultNodeFactory;
    exports.DefaultNodeModel = DefaultNodeModel;
    exports.DefaultNodeModule = DefaultNodeModule;
    exports.DefaultPortComponent = DefaultPortComponent;
    exports.DefaultPortFactory = DefaultPortFactory;
    exports.DefaultPortModel = DefaultPortModel;
    exports.DefaultPortModule = DefaultPortModule;
    exports.DiagramEngine = DiagramEngine;
    exports.DiagramModel = DiagramModel;
    exports.InvalidLinkDestroyed = InvalidLinkDestroyed;
    exports.LabelModel = LabelModel;
    exports.LinkCreatedAction = LinkCreatedAction;
    exports.LinkModel = LinkModel;
    exports.LockEvent = LockEvent;
    exports.LooseLinkDestroyed = LooseLinkDestroyed;
    exports.MoveCanvasAction = MoveCanvasAction;
    exports.MoveItemsAction = MoveItemsAction;
    exports.NgxDiagramComponent = NgxDiagramComponent;
    exports.NgxDiagramsModule = NgxDiagramsModule;
    exports.NodeModel = NodeModel;
    exports.OutsideZone = OutsideZone;
    exports.PaintedEvent = PaintedEvent;
    exports.ParentChangeEvent = ParentChangeEvent;
    exports.PointModel = PointModel;
    exports.PortModel = PortModel;
    exports.SelectingAction = SelectingAction;
    exports.SelectionEvent = SelectionEvent;
    exports.UID = UID;
    exports.arrayToMap = arrayToMap;
    exports.coerceArray = coerceArray;
    exports.enableDiagramProdMode = enableDiagramProdMode;
    exports.entityProperty = entityProperty;
    exports.generateCurvePath = generateCurvePath;
    exports.generateDynamicPath = generateDynamicPath;
    exports.generateLinePath = generateLinePath;
    exports.isArray = isArray;
    exports.isDev = isDev;
    exports.isEmptyArray = isEmptyArray;
    exports.isFunction = isFunction;
    exports.isNil = isNil;
    exports.isString = isString;
    exports.log = log;
    exports.mapToArray = mapToArray;
    exports.mapToEntries = mapToEntries;
    exports.setLogLevel = setLogLevel;
    exports.unique = unique;
    exports.withLog = withLog;
    exports.ɵa = OutsideZone;
    exports.ɵb = TemplateVarDirective;
    exports.ɵc = DefaultLinkModel;
    exports.ɵd = LinkModel;
    exports.ɵe = BaseModel;
    exports.ɵf = BaseEntity;
    exports.ɵh = DefaultNodeModel;
    exports.ɵi = NodeModel;
    exports.ɵk = DefaultPortModel;
    exports.ɵl = PortModel;
    exports.ɵm = DefaultLabelModel;
    exports.ɵn = LabelModel;

    Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=ngx-diagrams.umd.js.map