var __extends = (this && this.__extends) || (function () {
    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 (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (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 _this = this;
import './ripple-e6ddbc19.js';
var ModuleFlow;
(function (ModuleFlow) {
    // prettier-ignore
    ModuleFlow.DEFAULT_CONDITION = function condition(context) { return true; }.toString();
    ModuleFlow.empty = function () { return ({
        start: { id: '', version: '' },
        blocks: {},
        orphans: []
    }); };
})(ModuleFlow || (ModuleFlow = {}));
var Module;
(function (Module) {
    Module.empty = function (metadata) { return ({
        id: '',
        version: '',
        name: '',
        description: '',
        category: '',
        image: '',
        flow: ModuleFlow.empty(),
        settings: {
            active: false,
            locked: false,
            createdAt: new Date().toISOString(),
            createdBy: ''
        },
        metadata: metadata || {}
    }); };
})(Module || (Module = {}));
function debounce(f, wait) {
    if (wait === void 0) { wait = 200; }
    var timeout = undefined;
    return function () {
        var context = this;
        var args = arguments;
        window.clearTimeout(timeout);
        timeout = window.setTimeout(function () { return f.apply(context, args); }, wait);
    };
}
var File = /** @class */ (function () {
    function File(id, name, data) {
        this.id = id;
        this.name = name;
        this.type = data.type;
        this.blob = data;
    }
    return File;
}());
var UUID = /** @class */ (function () {
    function UUID() {
    }
    UUID.v4 = function () {
        return ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, function (c) {
            var r = crypto.getRandomValues(new Uint8Array(1))[0];
            var n = parseInt(c, 10);
            return (n ^ (r & (15 >> (n / 4)))).toString(16);
        });
    };
    return UUID;
}());
var Validator = /** @class */ (function () {
    function Validator() {
        this.rules = new Map();
    }
    Validator.prototype.validate = function (rule, value, el, meta) {
        var rules = rule.split('|').map(function (r) {
            var _a = r.split(':'), rule = _a[0], arg = _a[1];
            return { rule: rule, arg: arg };
        });
        return this.exec(value, rules, el, meta);
    };
    Validator.prototype.exec = function (value, rules, el, meta) {
        return __awaiter(this, void 0, void 0, function () {
            var errors, _i, rules_1, _a, rule, arg, error;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        errors = [];
                        _i = 0, rules_1 = rules;
                        _b.label = 1;
                    case 1:
                        if (!(_i < rules_1.length)) return [3 /*break*/, 4];
                        _a = rules_1[_i], rule = _a.rule, arg = _a.arg;
                        return [4 /*yield*/, this.rule(value, rule, el, arg, meta)];
                    case 2:
                        error = _b.sent();
                        if (error)
                            errors.push(error);
                        _b.label = 3;
                    case 3:
                        _i++;
                        return [3 /*break*/, 1];
                    case 4: return [2 /*return*/, errors];
                }
            });
        });
    };
    Validator.prototype.rule = function (value, rule, el, arg, meta) {
        return __awaiter(this, void 0, void 0, function () {
            var validator, validation, message, _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        validator = this.rules.get(rule);
                        if (!validator)
                            throw new Error('Unknown validation rule: ' + rule);
                        return [4 /*yield*/, validator(value, arg, el)];
                    case 1:
                        validation = _b.sent();
                        if (validation.valid)
                            return [2 /*return*/, null];
                        if (!this.translator) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.translator(rule, {
                                arg: arg,
                                default: validation.message,
                                meta: meta,
                                value: value
                            })];
                    case 2:
                        _a = _b.sent();
                        return [3 /*break*/, 4];
                    case 3:
                        _a = validation.message;
                        _b.label = 4;
                    case 4:
                        message = _a;
                        return [2 /*return*/, { rule: rule, message: message }];
                }
            });
        });
    };
    return Validator;
}());
var validator = new Validator();
validator.rules.set('alpha', function (value) { return __awaiter(_this, void 0, void 0, function () {
    var valid, message;
    return __generator(this, function (_a) {
        valid = /^[A-Z]*$/i.test(value);
        message = 'This field should only contain alphabetic characters [A-Z].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('alpha-dash', function (value) { return __awaiter(_this, void 0, void 0, function () {
    var valid, message;
    return __generator(this, function (_a) {
        valid = /^[0-9A-Z_-]*$/i.test(value);
        message = 'This field should only contain alphanumeric characters or dashes [0-9A-Z_-].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('alpha-num', function (value) { return __awaiter(_this, void 0, void 0, function () {
    var valid, message;
    return __generator(this, function (_a) {
        valid = /^[0-9A-Z]*$/i.test(value);
        message = 'This field should only contain alphanumeric characters [0-9A-Z].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('alpha-spaces', function (value) { return __awaiter(_this, void 0, void 0, function () {
    var valid, message;
    return __generator(this, function (_a) {
        valid = /^[A-Z\s]*$/i.test(value);
        message = 'This field should only contain alphabetic characters or whitespace [A-Zs].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('between', function (value, arg) { return __awaiter(_this, void 0, void 0, function () {
    var _a, min, max, valid, message;
    return __generator(this, function (_b) {
        _a = arg ? arg.split(',') : [0, 1], min = _a[0], max = _a[1];
        valid = Number(min) <= Number(value) && Number(max) >= Number(value);
        message = "This field should contain a value between " + min + " and " + max + ".";
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('digits', function (value, arg) { return __awaiter(_this, void 0, void 0, function () {
    var length, str, valid, message;
    return __generator(this, function (_a) {
        length = arg && Number(arg);
        str = '' + value;
        valid = /^[0-9]*$/.test(str) && (length ? str.length === length : true);
        message = length
            ? "This field requires " + length + " digits."
            : 'This field should only contain numbers.';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('email', function (value) { return __awaiter(_this, void 0, void 0, function () {
    var re, valid, message;
    return __generator(this, function (_a) {
        re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
        valid = re.test(value);
        message = 'This is not a valid email.';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('in', function (value, arg) { return __awaiter(_this, void 0, void 0, function () {
    var options, valid, message;
    return __generator(this, function (_a) {
        options = arg ? arg.split(',') : [];
        valid = options.indexOf('' + value) >= 0;
        message = 'This value is not allowed.';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('is', function (value, arg) { return __awaiter(_this, void 0, void 0, function () {
    var valid, message;
    return __generator(this, function (_a) {
        valid = arg === value;
        message = "The value should be: " + arg + ".";
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('max', function (value, arg) { return __awaiter(_this, void 0, void 0, function () {
    var max, size, valid, message;
    return __generator(this, function (_a) {
        max = Number(arg);
        size = typeof value === 'number' ? value : ('' + value).length;
        valid = size <= max;
        message = typeof value === 'number'
            ? "This field may not exceed " + max + "."
            : "This field may not exceed " + max + " characters.";
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('min', function (value, arg) { return __awaiter(_this, void 0, void 0, function () {
    var min, size, valid, message;
    return __generator(this, function (_a) {
        min = Number(arg);
        size = typeof value === 'number' ? value : ('' + value).length;
        valid = size >= min;
        message = typeof value === 'number'
            ? "This field should be " + min + " or higher."
            : "This field requires at least " + min + " characters.";
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('numeric', function (value) { return __awaiter(_this, void 0, void 0, function () {
    var str, valid, message;
    return __generator(this, function (_a) {
        str = '' + value;
        valid = /^[0-9]+$/.test(str);
        message = 'This field should only contain numeric characters [0-9].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('required', function (value) { return __awaiter(_this, void 0, void 0, function () {
    var valid, message;
    return __generator(this, function (_a) {
        valid = Array.isArray(value) ? !!value.length : value === 0 || !!value;
        message = 'This field is required.';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
var File$1 = /** @class */ (function (_super) {
    __extends(File$1, _super);
    function File$1(id, name, path, blob) {
        var _this = _super.call(this, id, name, blob) || this;
        _this.path = path;
        return _this;
    }
    return File$1;
}(File));
var FlowTree = /** @class */ (function () {
    function FlowTree(flow) {
        this.orphans = [];
        this._nodes = new Map();
        this._indexes = new Map();
        this.flow = flow;
        this.root = this.parse(flow.start, flow);
        for (var _i = 0, _a = flow.orphans || []; _i < _a.length; _i++) {
            var orphan = _a[_i];
            this.orphans.push(this.parse(orphan, flow));
        }
        // Add indexes
        var i = 1;
        for (var _b = 0, _c = this.nodes; _b < _c.length; _b++) {
            var node = _c[_b];
            if (!node.id)
                continue;
            var id = FlowTree.encode(node.id, node.version);
            if (!this._indexes.has(id))
                this._indexes.set(id, i++);
        }
    }
    Object.defineProperty(FlowTree.prototype, "nodes", {
        get: function () {
            var nodes = [];
            var iterated = new Set();
            for (var _i = 0, _a = this.root.nodes; _i < _a.length; _i++) {
                var node = _a[_i];
                if (iterated.has(node))
                    continue;
                iterated.add(node);
                nodes.push(node);
            }
            for (var _b = 0, _c = this.orphans; _b < _c.length; _b++) {
                var orphan = _c[_b];
                for (var _d = 0, _e = orphan.nodes; _d < _e.length; _d++) {
                    var node = _e[_d];
                    if (iterated.has(node))
                        continue;
                    iterated.add(node);
                    nodes.push(node);
                }
            }
            return nodes;
        },
        enumerable: true,
        configurable: true
    });
    FlowTree.encode = function (arg1, arg2) {
        var _a = typeof arg1 === 'string' ? { id: arg1, version: arg2 } : arg1, id = _a.id, version = _a.version;
        return id + "@" + version;
    };
    FlowTree.decode = function (encoded) {
        var _a = encoded.split('@'), id = _a[0], version = _a[1];
        return { id: id, version: version };
    };
    FlowTree.prototype.canLink = function (source, target, once) {
        if (once === void 0) { once = false; }
        var encoded = FlowTree.encode(source);
        var src = this._nodes.get(encoded);
        if (!src)
            return false;
        // Can not link nodes if the target node is already a child of the source
        // node or the other way around
        for (var _i = 0, _a = src.nodes; _i < _a.length; _i++) {
            var node = _a[_i];
            if (node.id === target.id)
                return false;
        }
        return once ? true : this.canLink(target, source, true);
    };
    FlowTree.prototype.getIndex = function (arg1, arg2) {
        var encoded = arg2 != undefined ? FlowTree.encode(arg1, arg2) : arg1;
        var index = this._indexes.get(encoded);
        return index == undefined ? -1 : index;
    };
    FlowTree.prototype.getLabel = function (arg1, arg2) {
        var index = this.getIndex(arg1, arg2);
        return index >= 0 ? ('000' + index).slice(-3) : '';
    };
    FlowTree.prototype.add = function (target, id) {
        var _a;
        var prev = this.flow.blocks[target.id] || [];
        var blocks = Object.assign({}, this.flow.blocks, (_a = {}, _a[target.id] = prev.concat([{ id: id, version: 'v1' }]), _a));
        return Object.assign({}, this.flow, { blocks: blocks });
    };
    FlowTree.prototype.delete = function (target) {
        var _this = this;
        if (this.flow.start.id === target.id)
            return this.flow;
        // Remove every reference of the node from the blocks chain
        var blocks = Object.keys(this.flow.blocks).reduce(function (blocks, id) {
            if (id === target.id)
                return blocks;
            var next = _this.flow.blocks[id];
            blocks[id] =
                next && next.findIndex(function (n) { return n.id === target.id; }) >= 0
                    ? next.filter(function (n) { return n.id !== target.id; }).concat((_this.flow.blocks[target.id] || [])) : next;
            return blocks;
        }, {});
        // Filter block from orphans
        var filtered = this.flow.orphans.filter(function (o) { return o.id !== target.id; });
        if (this.flow.orphans.length === filtered.length)
            return Object.assign({}, this.flow, { blocks: blocks });
        // An orphaned block was deleted, gather the new orphans
        var newOrphans = (this.flow.blocks[target.id] || []).filter(function (n) {
            return Object.keys(blocks).every(function (id) { return blocks[id].every(function (c) { return c.id !== n.id; }); });
        });
        var orphans = filtered.concat(newOrphans);
        return Object.assign({}, this.flow, { blocks: blocks, orphans: orphans });
    };
    FlowTree.prototype.unlink = function (source, target) {
        var _a;
        // Unlink blocks
        var next = this.flow.blocks[source];
        var detached = next.find(function (n) { return n.id === target; });
        var blocks = Object.assign({}, this.flow.blocks, (_a = {}, _a[source] = next.filter(function (n) { return n.id !== target; }), _a));
        // Add target to orphaned blocks in case there is no way to reach the block
        var orphaned = Object.keys(blocks).every(function (id) { return blocks[id].every(function (n) { return n.id !== target; }); });
        var orphans = orphaned
            ? this.flow.orphans.concat([detached]) : this.flow.orphans;
        return Object.assign({}, this.flow, { blocks: blocks, orphans: orphans });
    };
    FlowTree.prototype.parse = function (current, flow) {
        var encoded = FlowTree.encode(current);
        var existing = this._nodes.get(encoded);
        if (existing)
            return existing;
        var id = current.id, version = current.version;
        var children = this.parseChildren(current, flow);
        var node = new FlowTreeNode(id, version, children);
        this._nodes.set(encoded, node);
        return node;
    };
    FlowTree.prototype.parseChildren = function (current, flow) {
        var _this = this;
        var next = (flow.blocks || {})[current.id];
        return next ? next.map(function (n) { return _this.parse(n, flow); }).filter(function (n) { return !!n; }) : [];
    };
    return FlowTree;
}());
var FlowTreeNode = /** @class */ (function () {
    function FlowTreeNode(id, version, children) {
        this.id = id;
        this.version = version;
        this.children = children;
    }
    Object.defineProperty(FlowTreeNode.prototype, "nodes", {
        get: function () {
            var nestedNodes = this.children.map(function (n) { return n.nodes; });
            var nodes = [].concat.apply([], nestedNodes);
            return [this].concat(nodes);
        },
        enumerable: true,
        configurable: true
    });
    return FlowTreeNode;
}());
var Polyline = /** @class */ (function () {
    function Polyline(points) {
        this.sections = [];
        this.points = points.map(function (_a) {
            var x = _a.x, y = _a.y;
            return [x, y];
        });
        for (var i = 0; i < this.points.length - 1; i++) {
            var p1 = this.points[i];
            var p2 = this.points[i + 1];
            var size = this.distance(p1, p2);
            this.sections.push({ p1: p1, p2: p2, size: size });
        }
    }
    Object.defineProperty(Polyline.prototype, "size", {
        /**
         * Returns the size of the polyline.
         */
        get: function () {
            return this.sections.reduce(function (total, _a) {
                var size = _a.size;
                return total + size;
            }, 0);
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Polyline.prototype, "middle", {
        /**
         * Returns the middle of the polyline.
         */
        get: function () {
            // Middle of the line in terms of size
            var target = this.size / 2;
            // Move along sections until we find the section that exceeds our target,
            // from this section we can calculate the middle point's coordinated.
            var cur = 0;
            for (var _i = 0, _a = this.sections; _i < _a.length; _i++) {
                var section = _a[_i];
                var next = cur + section.size;
                if (next > target)
                    return this.along(section, target - cur);
                cur = next;
            }
            // Polyline has no sections
            return [0, 0];
        },
        enumerable: true,
        configurable: true
    });
    /**
     * Moves along the section by the specified distance.
     * @param section
     * @param distance
     */
    Polyline.prototype.along = function (section, distance) {
        var _a = section.p1, x1 = _a[0], y1 = _a[1];
        var _b = section.p2, x2 = _b[0], y2 = _b[1];
        var k = distance / section.size;
        return [k * x2 + (1 - k) * x1, k * y2 + (1 - k) * y1];
    };
    /**
     * Calculate the distance between two points.
     * @param p1 Start point
     * @param p2 End point
     */
    Polyline.prototype.distance = function (_a, _b) {
        var x1 = _a[0], y1 = _a[1];
        var x2 = _b[0], y2 = _b[1];
        var horDistance = Math.pow(x2 - x1, 2);
        var verDistance = Math.pow(y2 - y1, 2);
        return Math.sqrt(horDistance + verDistance);
    };
    return Polyline;
}());
var storedFiles = function (arg, id) {
    if (id === void 0) { id = true; }
    // prettier-ignore
    var files = 'flow' in arg
        ? storedModule(arg)
        : 'type' in arg ? storedElement(arg) : storedBlock(arg);
    return id ? files.map(function (file) { return file.match(/BCE:\/\/([^/]+)\//)[1]; }) : files;
};
var storedModule = function (module) {
    return stored(module.image) ? [module.image] : [];
};
var storedBlock = function (block) {
    return [].concat.apply([], block.elements.map(storedElement));
};
var storedElement = function (element) {
    switch (element.type) {
        case 'audio':
        case 'download':
        case 'image':
        case 'image-map':
        case 'video':
            return stored(element.location) ? [element.location] : [];
        case 'checkbox':
        case 'radio':
        case 'sort':
            return storedOptions(element.options);
        case 'matrix':
            var rows = storedOptions(element.rows);
            var columns = storedOptions(element.columns);
            return rows.concat(columns);
        case 'table':
            return storedOptions(element.columns);
        default:
            return [];
    }
};
var storedOptions = function (options) {
    return options
        .filter(function (option) { return 'image' in option && option.image && stored(option.image); })
        .map(function (option) { return option.image; });
};
var stored = function (location) {
    return !!location && location.startsWith('BCE://');
};
export { File$1 as F, ModuleFlow as M, Polyline as P, UUID as U, Module as a, FlowTree as b, FlowTreeNode as c, debounce as d, File as e, storedFiles as s, validator as v };
