var Jade;
(function (Jade) {
    class EventBus {
        constructor(options) {
            this.m_events = new Jade.EventBusEvents();
            this.m_id = UUID.genV4().toString();
            this.m_label = options ? options.label : this.id;
        }
        get id() {
            return this.m_id;
        }
        get label() {
            return this.m_label;
        }
        get events() {
            return this.m_events;
        }
        ;
        subscribe(eventType, callback) {
            return this.events.subscribe(eventType, callback);
        }
        publish(eventType, data) {
            this.events.publish(eventType, data);
        }
        clear() {
            this.m_events = new Jade.EventBusEvents();
        }
    }
    Jade.EventBus = EventBus;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class ContextEventBus extends Jade.EventBus {
    }
    Jade.ContextEventBus = ContextEventBus;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandContext {
        constructor(options) {
            this.m_id = null;
            this.m_groups = new Jade.CommandGroupCollection();
            this.m_id = options.contextId;
        }
        get ID() {
            return this.m_id;
        }
        get groups() {
            return this.m_groups;
        }
        setState(state, options = {}) {
            let group = null;
            let key = null;
            let keyCount = 0;
            for (key in state) {
                let groupId = Jade.CommandPath.buildGroup(key, options.discriminatorId);
                group = this.groups.getById(groupId);
                if (group == null) {
                    group = this.ensureGroup(groupId);
                }
                if (group != null) {
                    group.setState(state[key], options);
                }
                keyCount += 1;
            }
            if (keyCount === 0) {
                this.groups.each((groupId) => {
                    group = this.groups.getById(groupId);
                    if (group == null) {
                        group = this.ensureGroup(groupId);
                    }
                    if (group != null) {
                        group.setState({}, options);
                    }
                });
            }
        }
        ensureGroup(groupId) {
            var result = null;
            if (!this.groups.groupIsDefined(groupId)) {
                result = Jade.CommandGroups.create(groupId);
            }
            else {
                result = this.groups.getById(groupId);
            }
            if (result != null) {
                this.groups.add(result);
            }
            return result;
        }
        resolve(commandPath) {
            let info = Jade.CommandPath.parse(commandPath);
            let group = this.ensureGroup(info.groupId);
            let command = null;
            if (group) {
                command = group.commands.getById(info.commandId);
            }
            return {
                context: this,
                group: group,
                command: command,
                path: commandPath
            };
        }
        execute(commandPath, args) {
            args = args == null ? {} : args;
            var resolved = this.resolve(commandPath);
            if (resolved.group != null) {
                resolved.group.execute(commandPath, args);
            }
        }
        clear() {
            this.groups.clear();
        }
    }
    Jade.CommandContext = CommandContext;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Context {
        constructor(options) {
            this.m_node = options.contextNode;
            this.m_options = new Jade.StateBag(_.extend({
                id: UUID.genV4(),
                name: "",
                parent: null,
                tags: []
            }, options));
            this.m_commands = new Jade.CommandContext({
                contextId: this.ID
            });
            this.m_events = new Jade.ContextEventBus();
            this.m_store = new Jade.StateBag();
            this.m_hotkeys = new window.keypress.Listener(document.getElementsByTagName("BODY")[0]);
            this.m_depth = this.parent == null ? 0 : this.parent.depth + 1;
            this.m_tags = [];
        }
        get ID() {
            return this.options.get("id");
        }
        get name() {
            return this.options.get("name");
        }
        get options() {
            return this.m_options;
        }
        get parent() {
            return this.options.get("parent");
        }
        get commands() {
            return this.m_commands;
        }
        get events() {
            return this.m_events;
        }
        get tags() {
            return this.m_tags;
        }
        get hotkeys() {
            return this.m_hotkeys;
        }
        get store() {
            return this.m_store;
        }
        get depth() {
            return this.m_depth;
        }
        get node() {
            return this.m_node;
        }
        get isDialogContext() {
            return this.options.get("isDialogContext", false);
        }
        onCurrentChanged(current) {
            if (current) {
                this.hotkeys.listen();
            }
            else {
                this.hotkeys.stop_listening();
            }
        }
        destroy() {
            this.hotkeys.destroy();
            this.options.clear();
            this.store.clear();
            this.events.clear();
            this.commands.clear();
        }
        findClosestContext(tags, matchAny = false) {
            var result = null, target = this;
            matchAny = matchAny == null ? false : true;
            while (target != null) {
                if (target.isTaggedWith(tags, matchAny)) {
                    result = target;
                    break;
                }
                target = target.parent;
            }
            return result;
        }
        isCurrent(value) {
            this.onCurrentChanged(value);
            this.events.publish(Context.CONTEXT_FOCUS_EVENT, { isCurrent: value });
        }
        isTaggedWith(tags, matchAny = false) {
            var result = false, unmatched = _.difference(tags, this.tags);
            result = (matchAny === false && unmatched.length === 0)
                || (matchAny === true && unmatched.length < tags.length);
            return result;
        }
    }
    Context.CONTEXT_FOCUS_EVENT = "CONTEXT_FOCUS_EVENT";
    Jade.Context = Context;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class ContextCollection {
        constructor(options) {
            this.m_collection = {};
            this.m_childContextMaps = {};
            this.m_options = new Jade.StateBag(_.extend({
                add: function () { },
                remove: function () { }
            }));
        }
        get options() {
            return this.m_options;
        }
        add(context) {
            var addFn = this.options.get("add");
            if (!this.exists(context)) {
                this.m_collection[context.ID] = context;
                this.createChildContextMap(context);
                addFn({
                    contextId: context.ID
                });
            }
        }
        remove(context, destroyChildContexts = true) {
            var targetContextId = this.getContextId(context), targetContext = this.getById(targetContextId), removeFn = this.options.get("remove");
            if (targetContext != null) {
                delete this.m_collection[targetContextId];
                if (destroyChildContexts !== false) {
                    this.destroyChildContexts(targetContext);
                }
                removeFn({
                    contextId: targetContextId
                });
            }
        }
        each(fn) {
            fn = fn || function () { };
            _.each(this.m_collection, function (value, key, list) {
                fn(key, value);
            });
        }
        exists(context) {
            var contextId = this.getContextId(context);
            return this.m_collection[contextId] != null;
        }
        getById(contextId) {
            var result = null;
            contextId = this.getContextId(contextId);
            if (this.exists(contextId)) {
                result = this.m_collection[contextId];
            }
            return result;
        }
        getContextId(context) {
            var result = null;
            if (context != null) {
                if (_.isString(context)) {
                    result = context.toLowerCase();
                }
                else {
                    result = context.ID;
                }
            }
            return result;
        }
        createChildContextMap(context) {
            var mapArray = null, focusedContext = context, focusedContextId = null;
            do {
                focusedContext = focusedContext.parent;
                if (focusedContext != null) {
                    focusedContextId = focusedContext.ID;
                    if (this.m_childContextMaps[focusedContextId] == null) {
                        this.m_childContextMaps[focusedContextId] = [];
                    }
                    mapArray = this.m_childContextMaps[focusedContextId];
                    mapArray[mapArray.length] = context.ID;
                }
            } while (focusedContext != null);
        }
        destroyChildContexts(context) {
            var focusedContext = context, focusedContextId = context.ID, mapArray = this.m_childContextMaps[focusedContextId] || [], oldArray = null, newArray = null;
            if (mapArray != null) {
                mapArray.unshift(focusedContextId);
                _.each(mapArray, (contextId) => {
                    this.remove(contextId, false);
                    delete this.m_childContextMaps[contextId];
                });
            }
            do {
                focusedContext = focusedContext.parent;
                if (focusedContext != null) {
                    oldArray = this.m_childContextMaps[focusedContext.ID];
                    this.m_childContextMaps[focusedContext.ID] = _.difference(oldArray, mapArray);
                }
            } while (focusedContext != null);
        }
    }
    Jade.ContextCollection = ContextCollection;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class ContextManager {
        constructor() {
            this.GLOBAL_CONTEXT_ID = "GLOBAL";
            this.m_globalContext = null;
            this.m_currentContext = null;
            this.m_contexts = null;
            this.m_contexts = new Jade.ContextCollection({
                remove: function (e) {
                }
            });
            this.m_globalContext = this.createContext({
                id: this.GLOBAL_CONTEXT_ID
            });
        }
        get contexts() {
            return this.m_contexts;
        }
        get current() {
            return this.m_currentContext != null ? this.m_currentContext : this.m_globalContext;
        }
        get global() {
            return this.m_globalContext;
        }
        setCurrent(contextId) {
            var newContext = this.contexts.getById(contextId);
            var oldContext = this.current;
            var hasContextChanged = oldContext != newContext;
            if (oldContext != null) {
                this.m_currentContext = null;
                oldContext.isCurrent(false);
            }
            if (newContext != null) {
                this.m_currentContext = newContext;
                newContext.isCurrent(true);
            }
            if (hasContextChanged) {
                _.each(ContextManager.m_onContextChangeCallbacks, (callback) => {
                    callback({
                        oldContext: oldContext,
                        newContext: newContext,
                        oldContextId: oldContext != null ? oldContext.ID : null,
                        newContextId: newContext != null ? newContext.ID : null,
                    });
                });
            }
        }
        createContext(createOptions) {
            var options = _.extend({
                id: UUID.genV4(),
                name: "",
                parent: this.current,
                makeDefault: true
            }, createOptions);
            var context = new Jade.Context(options);
            this.contexts.add(context);
            if (options.makeDefault) {
                this.setCurrent(context.ID);
            }
            return context;
        }
        destroyContext(options) {
            var context = this.getContextById(options.id);
            if (options.id === this.GLOBAL_CONTEXT_ID) {
                return;
            }
            if (context != null) {
                let isDefault = this.isCurrent(context);
                this.contexts.remove(context);
                if (isDefault && context.parent != null) {
                    this.setCurrent(context.parent.ID);
                }
            }
        }
        revolveContext(args) {
            var originalContext = this.current;
            var targetedContext = args.context;
            var hasContext = targetedContext != null && targetedContext instanceof Jade.Context;
            var hasFunction = _.isFunction(args.fn);
            var hasTags = _.isArray(args.tags);
            if (!hasContext && hasTags) {
                targetedContext = this.findClosestContext(args.tags);
            }
            if (targetedContext == null) {
                return;
            }
            this.m_currentContext = targetedContext;
            if (hasFunction) {
                args.fn();
            }
            this.m_currentContext = originalContext;
        }
        getContextById(contextId) {
            return this.contexts.getById(contextId);
        }
        findClosestContext(tagsArr) {
            return this.current.findClosestContext(tagsArr);
        }
        isCurrent(context) {
            return context != null && this.current.ID === context.ID;
        }
        static onContextChange(callback) {
            ContextManager.m_onContextChangeCallbacks[ContextManager.m_onContextChangeCallbacks.length] = callback;
        }
        static setTimeout(callback, timeout) {
            let previousContext = Jade.Application.instance.contexts.current;
            setTimeout(() => {
                let currentContext = Jade.Application.instance.contexts.current;
                if (Jade.Application.instance.contexts.contexts.exists(previousContext)) {
                    Jade.Application.instance.contexts.setCurrent(previousContext.ID);
                }
                ;
                callback();
                if (Jade.Application.instance.contexts.contexts.exists(currentContext)) {
                    Jade.Application.instance.contexts.setCurrent(currentContext.ID);
                }
                ;
            }, timeout);
        }
    }
    ContextManager.m_onContextChangeCallbacks = [];
    Jade.ContextManager = ContextManager;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    Jade.nameof = (name) => name;
    class Application {
        constructor(options) {
            if (Application.m_instance != null) {
                return;
            }
            this.m_options = new Jade.StateBag(options);
            this.m_contexts = new Jade.ContextManager();
            Application.m_instance = this;
            window[this.name] = this;
            this.onInit(options);
        }
        get id() {
            return this.m_id || this.name;
        }
        set id(value) {
            this.m_id = value;
        }
        static get instance() {
            return this.internalInstance;
            ;
        }
        static get internalInstance() {
            return Application.m_instance;
        }
        static set internalInstance(value) {
            Application.m_instance = value;
        }
        static get hasStarted() {
            return Application.m_hasStarted;
        }
        get name() {
            return this.options.get("name");
        }
        get virtualDirectory() {
            return this.options.get("virtualDirectory");
        }
        get imagePaths() {
            return this.options.get("imagePaths");
        }
        get contexts() {
            return this.m_contexts;
        }
        get options() {
            return this.m_options;
        }
        static onStart(callback) {
            if (Application.hasStarted) {
                callback();
                return;
            }
            Application.m_startFunctions[Application.m_startFunctions.length] = callback;
        }
        onInit(options) {
            this.executeStartUpFunctions();
            this.captureBrowserResizeEvent();
        }
        executeStartUpFunctions() {
            _.each(Application.m_startFunctions, (callback) => {
                callback(this);
            });
            Application.m_hasStarted = true;
        }
        captureBrowserResizeEvent() {
            window.onresize = _.debounce(() => {
                Jade.Application
                    .instance
                    .contexts
                    .global
                    .events
                    .publish("Window:Resized");
            }, 100);
        }
    }
    Application.m_instance = null;
    Application.m_hasStarted = false;
    Application.m_startFunctions = [];
    Jade.Application = Application;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Arithmetic {
        static round(number, roundToNearest) {
            return Math.floor(Math.round(number / roundToNearest)) * roundToNearest;
        }
        static roundUp(number, roundToNearest) {
            return Math.ceil(number / roundToNearest) * roundToNearest;
        }
        static roundDown(number, roundToNearest) {
            return Math.floor(number / roundToNearest) * roundToNearest;
        }
        static randomInteger(min, max) {
            return Math.floor(Math.random() * (max - min + 1) + min);
        }
    }
    Jade.Arithmetic = Arithmetic;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Attribute {
        static get root() {
            return "___attributes___";
        }
        get name() {
            return;
        }
        static define(callback) {
            return function (constr) {
                return class extends constr {
                    constructor(...args) {
                        super(...args);
                        if (this[Attribute.root] == undefined) {
                            this[Attribute.root] = {};
                        }
                        callback(this);
                    }
                };
            };
        }
    }
    Jade.Attribute = Attribute;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    let DiskUnits;
    (function (DiskUnits) {
        DiskUnits[DiskUnits["Bytes"] = 0] = "Bytes";
        DiskUnits[DiskUnits["KB"] = 1] = "KB";
        DiskUnits[DiskUnits["MB"] = 2] = "MB";
        DiskUnits[DiskUnits["GB"] = 3] = "GB";
        DiskUnits[DiskUnits["TB"] = 4] = "TB";
    })(DiskUnits = Jade.DiskUnits || (Jade.DiskUnits = {}));
    class Convert {
        static BytesToUnits(bytes, roundUnitsUnder = DiskUnits.MB) {
            let unit = DiskUnits.Bytes;
            let value = 0;
            let formattedValue = "";
            if (bytes == null) {
                return "";
            }
            _.each(Jade.Enum.GetValues(DiskUnits), (value, index) => {
                if (bytes / Math.pow(1024, index) < 1024) {
                    unit = index;
                    return false;
                }
            });
            value = bytes / Math.pow(1024, unit);
            if (unit < roundUnitsUnder) {
                formattedValue = unit == DiskUnits.Bytes ?
                    `${Math.round(value)}` :
                    `${Math.round(value).toFixed(2)}`;
            }
            else {
                formattedValue = unit == DiskUnits.Bytes ?
                    `${value}` :
                    `${value.toFixed(2)}`;
            }
            return `${formattedValue} ${Jade.Enum.GetName(DiskUnits, unit)}`;
        }
    }
    Jade.Convert = Convert;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Enum {
        static GetNames(enumeration) {
            let keys = Object.keys(enumeration);
            return keys.slice(keys.length / 2, keys.length - 1);
        }
        static GetValues(enumeration) {
            let keys = Object.keys(enumeration);
            let result = [];
            keys = keys.slice(0, (keys.length / 2) - 1);
            _.each(keys, (key) => {
                result[result.length] = enumeration[enumeration[key]];
            });
            return result;
        }
        static GetName(enumeration, value) {
            let names = Enum.GetNames(enumeration);
            let values = Enum.GetValues(enumeration);
            let result = names[values.indexOf(value)];
            return result;
        }
    }
    Jade.Enum = Enum;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    let HttpVerb;
    (function (HttpVerb) {
        HttpVerb["GET"] = "GET";
        HttpVerb["DELETE"] = "DELETE";
        HttpVerb["POST"] = "POST";
        HttpVerb["PUT"] = "PUT";
    })(HttpVerb = Jade.HttpVerb || (Jade.HttpVerb = {}));
    let BrowserEvent;
    (function (BrowserEvent) {
        BrowserEvent["CLICK"] = "click";
        BrowserEvent["CHANGE"] = "change";
        BrowserEvent["DOUBLECLICK"] = "dblclick";
        BrowserEvent["MOUSEDOWN"] = "mousedown";
        BrowserEvent["MOUSEUP"] = "mouseup";
        BrowserEvent["MOUSEOVER"] = "mouseover";
        BrowserEvent["MOUSEENTER"] = "mouseenter";
        BrowserEvent["MOUSELEAVE"] = "mouseleave";
        BrowserEvent["KEYDOWN"] = "keydown";
        BrowserEvent["KEYUP"] = "keyup";
        BrowserEvent["KEYPRESS"] = "keypress";
        BrowserEvent["RESIZE"] = "resize";
        BrowserEvent["FOCUS"] = "focus";
        BrowserEvent["BLUR"] = "blur";
        BrowserEvent["SCROLL"] = "scroll";
    })(BrowserEvent = Jade.BrowserEvent || (Jade.BrowserEvent = {}));
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class EventMap {
        constructor() {
            this.m_map = {};
        }
        fire(eventName, ...args) {
            if (this.hasKey(eventName)) {
                this.handlers(eventName).forEach((handler) => {
                    handler.apply(this, args);
                });
            }
        }
        add(eventName, fn) {
            this.ensureKey(eventName);
            this.addHandler(eventName, fn);
        }
        hasKey(key) {
            return this.m_map[key] != null;
        }
        ensureKey(key) {
            if (!this.hasKey(key)) {
                this.m_map[key] = [];
            }
        }
        addHandler(eventName, fn) {
            this.m_map[eventName][this.m_map[eventName].length] = fn;
        }
        handlers(eventName) {
            let result = [];
            if (this.hasKey(eventName)) {
                result = this.m_map[eventName];
            }
            return result;
        }
    }
    Jade.EventMap = EventMap;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Events {
        static get BrowserResizedEvent() {
            return "Browser:Resized";
        }
        static get BrowserFocusedEvent() {
            return "Browser:Focused";
        }
    }
    Jade.Events = Events;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Guid {
        static new() {
            return UUID.genV4().toString();
        }
        static empty() {
            return "00000000-0000-0000-0000-000000000000".slice(0);
        }
        static isGuid(value) {
            let regex = new RegExp(/^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$/, "gi");
            return regex.test(value);
        }
        static isNullOrEmpty(value) {
            return value == null || value == Guid.empty();
        }
    }
    Jade.Guid = Guid;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    let IconSetSizeSpecificDirectory;
    (function (IconSetSizeSpecificDirectory) {
        IconSetSizeSpecificDirectory["x16"] = "16x16";
        IconSetSizeSpecificDirectory["x24"] = "24x24";
        IconSetSizeSpecificDirectory["x32"] = "32x32";
        IconSetSizeSpecificDirectory["x48"] = "48x48";
        IconSetSizeSpecificDirectory["x64"] = "64x64";
        IconSetSizeSpecificDirectory["x128"] = "128x128";
        IconSetSizeSpecificDirectory["x256"] = "256x256";
        IconSetSizeSpecificDirectory["x512"] = "512x512";
        IconSetSizeSpecificDirectory["misc"] = "misc";
        IconSetSizeSpecificDirectory["svg"] = "svg";
    })(IconSetSizeSpecificDirectory = Jade.IconSetSizeSpecificDirectory || (Jade.IconSetSizeSpecificDirectory = {}));
    class IconSet {
        constructor(options) {
            this.m_options = null;
            this.m_options = options;
        }
        get ID() {
            return this.options.id;
        }
        get rootPath() {
            return this.options.rootPath;
        }
        get options() {
            return this.m_options;
        }
        resolve(icon, sizeSpecificDirectory) {
            if (icon == null) {
                return null;
            }
            return Jade.Path.resolve(`${this.rootPath}/${sizeSpecificDirectory}/${icon}`);
        }
        static add(data) {
            this.m_list[data.id] = new IconSet(data);
        }
        static get(id) {
            return this.m_list[id];
        }
        static remove(id) {
            delete this.m_list[id];
        }
    }
    IconSet.m_list = {};
    Jade.IconSet = IconSet;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Instance {
        static isEmptyObject(item) {
            for (let key in item) {
                if (item.hasOwnProperty(key)) {
                    return false;
                }
            }
            return true;
        }
    }
    Jade.Instance = Instance;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class InterfaceGuard {
        static get root() {
            return "___interfaces___";
        }
        static is(target, ...guards) {
            let result = false;
            if (target != null) {
                this.iterateGuards(guards, (guard) => {
                    let is = this.hasInterface(target, guard.signature.id);
                    if (!is) {
                        result = false;
                        return false;
                    }
                    else {
                        result = true;
                    }
                });
            }
            return result;
        }
        static isAny(target, ...guards) {
            let result = false;
            if (target != null) {
                this.iterateGuards(guards, (guard) => {
                    let is = this.hasInterface(target, guard.signature.id);
                    if (!is) {
                        result = true;
                        return false;
                    }
                });
            }
            return result;
        }
        static anyImplement(targets, type) {
            let result = false;
            let guard = new type();
            _.each(targets, (target) => {
                let implemented = target != null && this.hasInterface(target, guard.signature.id);
                if (implemented) {
                    result = true;
                    return false;
                }
            });
            return result;
        }
        static allImplement(targets, type) {
            let result = targets.length > 0;
            let guard = new type();
            _.each(targets, (target) => {
                let implemented = target != null && this.hasInterface(target, guard.signature.id);
                if (!implemented) {
                    result = false;
                    return false;
                }
            });
            return result;
        }
        static register(registration) {
            if (registration.instance[InterfaceGuard.root] == null) {
                registration.instance[InterfaceGuard.root] = {};
            }
            if (registration.instance[InterfaceGuard.root][registration.guard.signature.id] == null) {
                registration.instance[InterfaceGuard.root][registration.guard.signature.id] = registration.guard;
            }
        }
        static implement(...guards) {
            return this.define((context) => {
                this.iterateGuards(guards, (guard) => {
                    InterfaceGuard.register({
                        instance: context,
                        guard: guard
                    });
                });
            });
        }
        static define(callback) {
            return function (constr) {
                return class extends constr {
                    constructor(...args) {
                        super(...args);
                        callback(this);
                    }
                };
            };
        }
        static iterateGuards(guards, callback) {
            _.each(guards, (guard) => {
                callback(new guard());
            });
        }
        static hasInterfaces(target) {
            return target[InterfaceGuard.root] != null;
        }
        static hasInterface(target, interfaceId) {
            return this.hasInterfaces(target) && target[InterfaceGuard.root][interfaceId] != null;
        }
    }
    Jade.InterfaceGuard = InterfaceGuard;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Json {
        static tryParse(target) {
            let result = null;
            try {
                result = this.parse(target);
            }
            catch {
                result = null;
            }
            return result;
        }
        static parse(target) {
            return JSON.parse(target);
        }
        static stringify(target) {
            JSON.stringify(target);
        }
    }
    Jade.Json = Json;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    let KeyCode;
    (function (KeyCode) {
        KeyCode[KeyCode["Backspace"] = 8] = "Backspace";
        KeyCode[KeyCode["Tab"] = 9] = "Tab";
        KeyCode[KeyCode["Enter"] = 13] = "Enter";
        KeyCode[KeyCode["Shift"] = 16] = "Shift";
        KeyCode[KeyCode["Ctrl"] = 17] = "Ctrl";
        KeyCode[KeyCode["Alt"] = 18] = "Alt";
        KeyCode[KeyCode["Pause"] = 19] = "Pause";
        KeyCode[KeyCode["CapsLock"] = 20] = "CapsLock";
        KeyCode[KeyCode["Escape"] = 27] = "Escape";
        KeyCode[KeyCode["Space"] = 32] = "Space";
        KeyCode[KeyCode["End"] = 35] = "End";
        KeyCode[KeyCode["Home"] = 36] = "Home";
        KeyCode[KeyCode["ArrowLeft"] = 37] = "ArrowLeft";
        KeyCode[KeyCode["ArrowUp"] = 38] = "ArrowUp";
        KeyCode[KeyCode["ArrowRight"] = 39] = "ArrowRight";
        KeyCode[KeyCode["ArrowDown"] = 40] = "ArrowDown";
        KeyCode[KeyCode["Insert"] = 45] = "Insert";
        KeyCode[KeyCode["Delete"] = 46] = "Delete";
        KeyCode[KeyCode["Key0"] = 48] = "Key0";
        KeyCode[KeyCode["Key1"] = 49] = "Key1";
        KeyCode[KeyCode["Key2"] = 50] = "Key2";
        KeyCode[KeyCode["Key3"] = 51] = "Key3";
        KeyCode[KeyCode["Key4"] = 52] = "Key4";
        KeyCode[KeyCode["Key5"] = 53] = "Key5";
        KeyCode[KeyCode["Key6"] = 54] = "Key6";
        KeyCode[KeyCode["Key7"] = 55] = "Key7";
        KeyCode[KeyCode["Key8"] = 56] = "Key8";
        KeyCode[KeyCode["Key9"] = 57] = "Key9";
        KeyCode[KeyCode["KeyA"] = 65] = "KeyA";
        KeyCode[KeyCode["KeyB"] = 66] = "KeyB";
        KeyCode[KeyCode["KeyC"] = 67] = "KeyC";
        KeyCode[KeyCode["KeyD"] = 68] = "KeyD";
        KeyCode[KeyCode["KeyE"] = 69] = "KeyE";
        KeyCode[KeyCode["KeyF"] = 70] = "KeyF";
        KeyCode[KeyCode["KeyG"] = 71] = "KeyG";
        KeyCode[KeyCode["KeyH"] = 72] = "KeyH";
        KeyCode[KeyCode["KeyI"] = 73] = "KeyI";
        KeyCode[KeyCode["KeyJ"] = 74] = "KeyJ";
        KeyCode[KeyCode["KeyK"] = 75] = "KeyK";
        KeyCode[KeyCode["KeyL"] = 76] = "KeyL";
        KeyCode[KeyCode["KeyM"] = 77] = "KeyM";
        KeyCode[KeyCode["KeyN"] = 78] = "KeyN";
        KeyCode[KeyCode["KeyO"] = 79] = "KeyO";
        KeyCode[KeyCode["KeyP"] = 80] = "KeyP";
        KeyCode[KeyCode["KeyQ"] = 81] = "KeyQ";
        KeyCode[KeyCode["KeyR"] = 82] = "KeyR";
        KeyCode[KeyCode["KeyS"] = 83] = "KeyS";
        KeyCode[KeyCode["KeyT"] = 84] = "KeyT";
        KeyCode[KeyCode["KeyU"] = 85] = "KeyU";
        KeyCode[KeyCode["KeyV"] = 86] = "KeyV";
        KeyCode[KeyCode["KeyW"] = 87] = "KeyW";
        KeyCode[KeyCode["KeyX"] = 88] = "KeyX";
        KeyCode[KeyCode["KeyY"] = 89] = "KeyY";
        KeyCode[KeyCode["KeyZ"] = 90] = "KeyZ";
        KeyCode[KeyCode["Pad0"] = 96] = "Pad0";
        KeyCode[KeyCode["Pad1"] = 97] = "Pad1";
        KeyCode[KeyCode["Pad2"] = 98] = "Pad2";
        KeyCode[KeyCode["Pad3"] = 99] = "Pad3";
        KeyCode[KeyCode["Pad4"] = 100] = "Pad4";
        KeyCode[KeyCode["Pad5"] = 101] = "Pad5";
        KeyCode[KeyCode["Pad6"] = 102] = "Pad6";
        KeyCode[KeyCode["Pad7"] = 103] = "Pad7";
        KeyCode[KeyCode["Pad8"] = 104] = "Pad8";
        KeyCode[KeyCode["Pad9"] = 105] = "Pad9";
        KeyCode[KeyCode["PadMultiply"] = 106] = "PadMultiply";
        KeyCode[KeyCode["PadAdd"] = 107] = "PadAdd";
        KeyCode[KeyCode["PadSubtract"] = 109] = "PadSubtract";
        KeyCode[KeyCode["PadPoint"] = 110] = "PadPoint";
        KeyCode[KeyCode["PadDivide"] = 111] = "PadDivide";
        KeyCode[KeyCode["F01"] = 112] = "F01";
        KeyCode[KeyCode["F02"] = 113] = "F02";
        KeyCode[KeyCode["F03"] = 114] = "F03";
        KeyCode[KeyCode["F04"] = 115] = "F04";
        KeyCode[KeyCode["F05"] = 116] = "F05";
        KeyCode[KeyCode["F06"] = 117] = "F06";
        KeyCode[KeyCode["F07"] = 118] = "F07";
        KeyCode[KeyCode["F08"] = 119] = "F08";
        KeyCode[KeyCode["F09"] = 120] = "F09";
        KeyCode[KeyCode["F10"] = 121] = "F10";
        KeyCode[KeyCode["F11"] = 122] = "F11";
        KeyCode[KeyCode["F12"] = 123] = "F12";
        KeyCode[KeyCode["NumLock"] = 144] = "NumLock";
        KeyCode[KeyCode["ScrollLock"] = 145] = "ScrollLock";
        KeyCode[KeyCode["SemiColon"] = 186] = "SemiColon";
        KeyCode[KeyCode["EqualSign"] = 187] = "EqualSign";
        KeyCode[KeyCode["Comma"] = 188] = "Comma";
        KeyCode[KeyCode["Dash"] = 189] = "Dash";
        KeyCode[KeyCode["Period"] = 190] = "Period";
        KeyCode[KeyCode["ForwardSlash"] = 191] = "ForwardSlash";
        KeyCode[KeyCode["GraveAccent"] = 192] = "GraveAccent";
        KeyCode[KeyCode["OpenBracket"] = 219] = "OpenBracket";
        KeyCode[KeyCode["BackSlash"] = 220] = "BackSlash";
        KeyCode[KeyCode["CloseBracket"] = 221] = "CloseBracket";
        KeyCode[KeyCode["SingleQuote"] = 222] = "SingleQuote";
    })(KeyCode = Jade.KeyCode || (Jade.KeyCode = {}));
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class LocalStorage {
        static get(key) {
            return localStorage.getItem(key);
        }
        static getOf(key) {
            let value = this.get(key);
            let result = null;
            try {
                result = JSON.parse(value);
            }
            catch {
            }
            return result;
        }
        static set(key, value) {
            let storedValue = value;
            if (storedValue == null) {
                return;
            }
            localStorage.setItem(key, JSON.stringify(value));
        }
        static exists(key) {
            let result = false;
            for (let i = 0; i < localStorage.length; i++) {
                if (localStorage.key(i) === key) {
                    result = true;
                    break;
                }
            }
            return result;
        }
        static subscribe(eventName, fn) {
            this.ensureListener();
            this.m_events.add(eventName, fn);
        }
        static ensureListener() {
            if (this.m_eventListener != null) {
                return;
            }
            this.m_eventListener = (e) => {
                this.m_events.fire(e.key, e);
            };
            window.addEventListener("storage", this.m_eventListener);
        }
    }
    Jade.LocalStorage = LocalStorage;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Path {
        static resolve(path, qs) {
            let vd = Jade.Application.instance.virtualDirectory || "";
            let result = null;
            if (path == null) {
                return;
            }
            result = path;
            if (this.isVirtual(path) && vd !== null) {
                result = vd + path.substr(1);
            }
            if (qs != null) {
                result += "?" + Object.keys(qs).map(key => key + '=' + qs[key]).join('&');
            }
            return result;
        }
        static resolveImage(name, imagePathId) {
            let result = "";
            let imagePath = Jade.Application.instance.imagePaths[imagePathId];
            if (imagePath != null) {
                result = Path.resolve(`${imagePath}/${name}`);
            }
            return result;
        }
        static addIconSet(path, imagePathId) {
            if (!Jade.Application.instance.imagePaths.hasOwnProperty(imagePathId)) {
                Jade.Application.instance.imagePaths[imagePathId] = Path.resolve(path);
            }
        }
        static getFileNameWithoutExtension(path) {
            return path.substr(0, path.lastIndexOf("."));
        }
        static getFileExtension(path) {
            var index = path.lastIndexOf(".");
            return index > -1 ? path.substr(index, path.length - index) : "";
        }
        static isVirtual(path) {
            return path.indexOf("~/") === 0;
        }
    }
    Jade.Path = Path;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class StateBag extends Backbone.DeepModel {
        constructor(options) {
            super(options);
            this.m_doNotSerialize = new Jade.Collection();
        }
        get doNotSerialize() {
            return this.m_doNotSerialize;
        }
        get(id, defaultValue = undefined) {
            let result = super.get(id);
            if (result == null && defaultValue !== undefined) {
                result = defaultValue;
            }
            return result;
        }
        set(arg1, arg2, arg3) {
            if (_.isString(arg1) && _.isArray(arg2)) {
                super.unset(arg1);
                super.set(arg1, arg2.slice(0), arg3);
            }
            else {
                super.set(arg1, arg2, arg3);
            }
        }
        replace(obj, options) {
            this.attributes = {};
            this.set(obj, options);
        }
        update(arg1, arg2, arg3) {
            let isProperty = _.isString(arg1);
            if (isProperty) {
                this.set(arg1, arg2, arg3);
                return;
            }
            let copy = {};
            _.keys(this.attributes).forEach((key) => {
                if (arg1[key] == undefined) {
                    copy[key] = this.attributes[key];
                }
            });
            copy = _.mergeWith(copy, arg1, (targetValue, sourceValue, propertyName, targetObject, sourceObject) => {
                if (_.isArray(sourceValue) && arg3?.mergeArrays !== true) {
                    return sourceValue;
                }
            });
            this.attributes = {};
            this.set(copy, arg2);
        }
        add(id, value) {
            var setValue = {};
            setValue[id] = value;
            super.set(setValue);
        }
        extend(...items) {
            let result = this.toJSON();
            _.each(items, (item) => {
                result = _.extend(result, item);
            });
            this.set(result);
            this.toJSON();
            return result;
        }
        toJSON(options) {
            let clone = _.clone(this.attributes);
            let result = {};
            _.each(_.keys(clone), (key) => {
                if (!this.doNotSerialize.contains(key) && !Jade.String.startsWith(key, "$")) {
                    result[key] = clone[key];
                }
                else {
                    result[key] = null;
                }
            });
            result = JSON.stringify(result);
            result = JSON.parse(result);
            this.doNotSerialize.each((key) => {
                result[key] = this.get(key);
            });
            return result;
        }
    }
    Jade.StateBag = StateBag;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Store {
        constructor() {
            this.m_dictionary = {};
        }
        set(item) {
            let uid = this.getUid(item);
            this.m_dictionary[uid] = item;
        }
        get(key) {
            let result = undefined;
            if (this.hasKey(key)) {
                result = this.m_dictionary[this.getUid(key)];
            }
            return result;
        }
        remove(key) {
            if (this.hasKey(key)) {
                delete this.m_dictionary[this.getUid(key)];
            }
        }
        hasKey(key) {
            let result = true;
            let uid = this.getUid(key);
            if (this.m_dictionary.hasOwnProperty(uid)) {
                result = true;
            }
            return result;
        }
        getUid(key) {
            if (key.hasOwnProperty("____objectID____")) {
                return key.__objectID__;
            }
            else {
                let uid = UUID.genV4().toString();
                Object.defineProperty(key, "____objectID____", {
                    "configurable": false,
                    "enumerable": false,
                    "get": (function (__objectID__) {
                        return function () { return __objectID__; };
                    })(uid),
                    "set": function () {
                        throw new Error("Property ____objectID____ is read-only.");
                    }
                });
                return key.____objectID____;
            }
        }
    }
    Jade.Store = Store;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class String {
        static template(value) {
            return value.join("");
        }
        static format(text, ...params) {
            var result = text;
            _.each(params, (value, index) => {
                result = result.replace(new RegExp("\\{" + index + "\\}", "gm"), value);
            });
            return result;
        }
        static isNullOrWhiteSpace(value) {
            return value == null || (_.isString(value) && value.match(/^\s*$/) !== null);
        }
        static startsWith(value, searchText, caseInsensitive = true) {
            let result = false;
            if (caseInsensitive) {
                result = value.toLowerCase().indexOf(searchText.toLowerCase()) === 0;
            }
            else {
                result = value.indexOf(searchText) === 0;
            }
            return result;
        }
        static toTitleCase(text, options) {
            let result = text || "";
            options = options || {
                lowers: [],
                uppers: []
            };
            result = result.replace(/([^\W_]+[^\s-]*) */g, (value) => {
                return value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
            });
            options.lowers.forEach(lower => {
                result = result.replace(new RegExp(`\\s${lower}\\s`, 'g'), match => {
                    return match.toLowerCase();
                });
            });
            options.uppers.forEach(upper => {
                result = result.replace(new RegExp(`\\b${upper}\\b`, 'g'), match => {
                    return match.toUpperCase();
                });
            });
            return result;
        }
        static padLeft(value, padWith, padCount = 0) {
            let result = value.toString();
            while (result.length < padCount) {
                result = padWith + result;
            }
            return result;
        }
        static humanize(value) {
            let regex = new RegExp(/([A-Z])(?<=[a-z]\1|[A-Za-z]\1(?=[a-z]))/g);
            if (!this.isNullOrWhiteSpace(value)) {
                return value.replace(regex, " $1");
            }
            return value;
        }
    }
    Jade.String = String;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Type {
        static getType(name) {
            if (name == null) {
                return null;
            }
            let pieces = name.split(".");
            let source = window;
            _.each(pieces, (piece) => {
                source = source[piece];
            });
            return source ? source : null;
        }
    }
    Jade.Type = Type;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Collection {
        constructor(data = null) {
            this.m_innerList = [];
            if (data != null) {
                if (!_.isArray(data)) {
                    data = [data];
                }
                this.m_innerList = data;
            }
        }
        get innerList() {
            return this.m_innerList;
        }
        get count() {
            return this.innerList.length;
        }
        onChange() {
        }
        add(item) {
            if (this.contains(item)) {
                return;
            }
            this.innerList[this.m_innerList.length] = item;
            this.onChange();
            return true;
        }
        addRange(items) {
            if (_.isArray(items) && items.length > 0) {
                _.each(items, (item, index) => {
                    this.add(item);
                });
            }
        }
        contains(item) {
            var result = false;
            this.each((itemToCompare, index) => {
                if (item === itemToCompare) {
                    result = true;
                    return false;
                }
            });
            return result;
        }
        clear() {
            this.m_innerList = [];
            this.onChange();
        }
        each(callback) {
            var abort = false;
            _.each(this.innerList, (item, index) => {
                if (abort === false) {
                    abort = callback(item, index) === false;
                }
                return !abort;
            });
        }
        first() {
            return _.first(this.innerList);
        }
        item(index) {
            var result = null;
            if (index < this.count) {
                result = this.innerList[index];
            }
            return result;
        }
        indexOf(item) {
            return _.indexOf(this.innerList, item);
        }
        insert(item, index) {
            var result = false;
            if (this.count >= index && index > -1) {
                this.innerList.splice(index, 0, item);
                result = true;
                this.onChange();
            }
            return result;
        }
        insertRange(items, insertIndex) {
            if (insertIndex == undefined) {
                insertIndex = 0;
            }
            if (_.isArray(items) && items.length > 0) {
                items.reverse();
                _.each(items, (item, index) => {
                    this.insert(items[index], insertIndex);
                });
            }
            else {
                throw "The parameter 'items' is typed as Array<T> but the value provided is not derived of this type.";
            }
        }
        move(item, index) {
            var removed = null;
            if (this.count <= index) {
                return;
            }
            removed = this.remove(item);
            if (index != null) {
                this.insert(removed, index);
            }
            else {
                this.add(removed);
            }
        }
        last() {
            return _.last(this.innerList);
        }
        remove(item) {
            let index = this.indexOf(item);
            let result = null;
            if (index > -1) {
                result = this.removeAt(index);
            }
            return result;
        }
        removeAt(index) {
            var result = null;
            if (this.count > index) {
                result = this.innerList.splice(index, 1)[0];
                this.onChange();
            }
            else {
                throw "Index " + index + " exceeds the bounds of the array!";
            }
            return result;
        }
        removeAtIndices(indices) {
            var result = [];
            var sortNumeric = (array) => {
                array.sort((item1, item2) => {
                    return item2 - item1;
                });
            };
            if (_.isArray(indices) && indices.length > 0) {
                sortNumeric(indices);
                _.each(indices, (index) => {
                    result[result.length] = this.removeAt(indices[index]);
                });
            }
            return result;
        }
        reverse() {
            this.innerList.reverse();
            this.onChange();
        }
        reverseCopy() {
            return new Collection(this.innerList.slice(0).reverse());
        }
        sort(fn) {
            this.innerList.sort(fn);
            this.onChange();
        }
        toArray() {
            return this.m_innerList.slice(0);
        }
        transform(callback) {
            var result = [];
            this.each(function (item) {
                let transform = callback(item);
                if (transform != null) {
                    result[result.length] = transform;
                }
                ;
            });
            return result;
        }
        getByType(type) {
            let result = new Collection();
            this.each((item) => {
                if (item instanceof type) {
                    result.add(item);
                }
            });
            return result;
        }
    }
    Jade.Collection = Collection;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class MappedCollection extends Jade.Collection {
        constructor(options) {
            super(options.data);
            this.m_map = {};
            this.m_key = null;
            this.m_key = options.key;
            _.each(this.innerList, (item) => {
                this.map[item[this.key]] = item;
            });
        }
        get map() {
            return this.m_map;
        }
        get key() {
            return this.m_key;
        }
        add(item) {
            if (this.contains(item)) {
                return;
            }
            this.innerList[this.innerList.length] = item;
            this.map[item[this.key]] = item;
            this.onChange();
            return true;
        }
        contains(item) {
            return this.map.hasOwnProperty(item[this.key]);
        }
        clear() {
            this.m_map = {};
            super.clear();
        }
        get(id) {
            return this.map[id];
        }
        insert(item, index) {
            var result = false;
            if (this.count >= index && index > -1) {
                this.innerList.splice(index, 0, item);
                this.m_map[item[this.key]] = item;
                result = true;
                this.onChange();
            }
            return result;
        }
        removeAt(index) {
            var result = null;
            if (this.count > index) {
                result = this.innerList.splice(index, 1)[0];
                delete this.m_map[result[this.key]];
                this.onChange();
            }
            else {
                throw "Index " + index + " exceeds the bounds of the array!";
            }
            return result;
        }
        reverseCopy() {
            return new Jade.MappedCollection({
                key: this.key,
                data: this.innerList.slice(0).reverse()
            });
        }
    }
    Jade.MappedCollection = MappedCollection;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class ObservableCollection extends Jade.Collection {
        constructor(data = null, callbacks = {}) {
            super(data);
            this.m_callbacks = {};
            this.m_callbacks = callbacks;
        }
        add(item) {
            var result = false;
            this.executeChange("Add", item, this.count - 1, () => {
                result = super.add(item);
            });
            return result;
        }
        insert(item, index) {
            var result = false;
            if (this.count < index || index < 0) {
                return result;
            }
            this.executeChange("Insert", item, index, () => {
                result = super.insert(item, index);
            });
            return result;
        }
        removeAt(index) {
            var result = null;
            if (this.count < index || index < 0) {
                return result;
            }
            this.executeChange("Remove", this.item(index), index, () => {
                result = super.removeAt(index);
            });
            return result;
        }
        clear() {
            _.forEachRight(this.toArray(), (item, index) => {
                this.removeAt(index);
            });
        }
        executeChange(event, item, index, operation) {
            let argsBefore = null;
            let argsAfter = null;
            argsBefore = new Jade.ObservableCollectionBeforeChangeEventArgs("onBeforeChange", this.item(index), index);
            this.trigger(argsBefore);
            if (argsBefore.canceled) {
                return;
            }
            argsBefore = new Jade.ObservableCollectionBeforeChangeEventArgs(`onBefore${event}`, this.item(index), index);
            this.trigger(argsBefore);
            if (argsBefore.canceled) {
                return;
            }
            if (_.isFunction(operation)) {
                operation();
            }
            argsAfter = new Jade.ObservableCollectionChangeEventArgs(`onAfter${event}`, item, index);
            this.trigger(argsAfter);
            argsAfter = new Jade.ObservableCollectionChangeEventArgs("onAfterChange", item, index);
            this.trigger(argsAfter);
        }
        trigger(args) {
            var fn = this.m_callbacks[args.type];
            if (_.isFunction(fn)) {
                fn(args);
            }
        }
    }
    Jade.ObservableCollection = ObservableCollection;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class ObservableCollectionChangeEventArgs {
        constructor(type, item, index) {
            this.m_type = null;
            this.m_item = null;
            this.m_index = null;
            this.m_type = type;
            this.m_item = item;
            this.m_index = index;
        }
        get type() {
            return this.m_type;
        }
        get item() {
            return this.m_item;
        }
        get index() {
            return this.m_index;
        }
    }
    Jade.ObservableCollectionChangeEventArgs = ObservableCollectionChangeEventArgs;
    class ObservableCollectionBeforeChangeEventArgs extends ObservableCollectionChangeEventArgs {
        constructor(type, item, index) {
            super(type, item, index);
            this.m_canceled = false;
        }
        get canceled() {
            return this.m_canceled;
        }
        cancel() {
            this.m_canceled = true;
        }
    }
    Jade.ObservableCollectionBeforeChangeEventArgs = ObservableCollectionBeforeChangeEventArgs;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class ObservableMappedCollection extends Jade.MappedCollection {
        constructor(options) {
            super({ key: options.key, data: options.data });
            this.m_callbacks = {};
            this.m_callbacks = options.callbacks || {};
        }
        add(item) {
            var result = false;
            this.executeChange("Add", item, this.count - 1, () => {
                result = super.add(item);
            });
            return result;
        }
        insert(item, index) {
            var result = false;
            if (this.count < index || index < 0) {
                return result;
            }
            this.executeChange("Insert", item, index, () => {
                result = super.insert(item, index);
            });
            return result;
        }
        removeAt(index) {
            var result = null;
            if (this.count < index || index < 0) {
                return result;
            }
            this.executeChange("Remove", this.item(index), index, () => {
                result = super.removeAt(index);
            });
            return result;
        }
        clear() {
            this.reverseCopy().each((item, index) => {
                let targetIndex = this.indexOf(item);
                this.executeChange("Remove", item, targetIndex, () => {
                    super.removeAt(targetIndex);
                });
            });
        }
        executeChange(event, item, index, operation) {
            let argsBefore = null;
            let argsAfter = null;
            argsBefore = new Jade.ObservableCollectionBeforeChangeEventArgs("onBeforeChange", this.item(index), index);
            this.trigger(argsBefore);
            if (argsBefore.canceled) {
                return;
            }
            argsBefore = new Jade.ObservableCollectionBeforeChangeEventArgs(`onBefore${event}`, this.item(index), index);
            this.trigger(argsBefore);
            if (argsBefore.canceled) {
                return;
            }
            if (_.isFunction(operation)) {
                operation();
            }
            argsAfter = new Jade.ObservableCollectionChangeEventArgs(`onAfter${event}`, item, index);
            this.trigger(argsAfter);
            argsAfter = new Jade.ObservableCollectionChangeEventArgs("onAfterChange", item, index);
            this.trigger(argsAfter);
        }
        trigger(args) {
            var fn = this.m_callbacks[args.type];
            if (_.isFunction(fn)) {
                fn(args);
            }
        }
    }
    Jade.ObservableMappedCollection = ObservableMappedCollection;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class Command {
        constructor(props = {}) {
            this.m_props = new Jade.StateBag();
            this.m_state = new Jade.StateBag();
            this.m_uid = null;
            this.m_uid = UUID.genV4().toString();
            this.m_state = new Jade.StateBag();
            this.setDefaultValuesForProps(props);
            this.onInit();
        }
        get UID() {
            return this.m_uid;
        }
        get ID() {
            return this.props.get("id");
        }
        get state() {
            return this.m_state;
        }
        get props() {
            return this.m_props;
        }
        get checked() {
            return this.props.get("checked");
        }
        set checked(value) {
            this.props.set("checked", value);
        }
        get label() {
            return this.props.get("label");
        }
        set label(value) {
            this.props.set("label", value);
        }
        get enabled() {
            return this.props.get("enabled");
        }
        set enabled(value) {
            this.props.set("enabled", value);
        }
        get visible() {
            return this.props.get("visible");
        }
        set visible(value) {
            this.props.set("visible", value);
        }
        get icon() {
            return this.props.get("icon");
        }
        set icon(value) {
            this.props.set("icon", value);
        }
        get tooltip() {
            return this.props.get("tooltip", this.label);
        }
        set tooltip(value) {
            this.props.set("tooltip", value);
        }
        get display() {
            return this.props.get("display", "icon label");
        }
        set display(value) {
            this.props.set("display", value);
        }
        get value() {
            return this.props.get("value");
        }
        set value(value) {
            this.props.set("value", value);
        }
        get iconSet() {
            this.ensureIconSet(this.props.get("iconSetId"));
            return this.m_iconSet;
        }
        onInit() {
        }
        onStateChanged(state) {
        }
        onExecute(e) {
        }
        onExecuteSuccess() {
        }
        onExecuteError() {
        }
        onExecuteComplete() {
        }
        execute(e) {
            this.mergeCallbacks(e);
            this.onExecute(e);
        }
        bind(callback) {
            if (!_.isFunction(callback)) {
                return;
            }
            callback(this.props.toJSON());
            this.props.on("change", (e) => {
                callback(this.props.toJSON());
            });
        }
        setState(state, options = {}) {
            let src = state;
            let dst = this.state.toJSON();
            if (state == null) {
                return;
            }
            for (let key in dst) {
                if (key in src) {
                    if (_.isArray(src[key])) {
                        this.state.set(key, null);
                    }
                    this.state.set(key, src[key]);
                }
                else if (options.overwrite) {
                    this.state.set(key, null);
                }
            }
            for (let key in src) {
                if (!(key in dst) && this.state.doNotSerialize.contains(key)) {
                    dst[key] = src[key];
                }
            }
            if (Jade.Instance.isEmptyObject(state) || this.state.hasChanged() || options.force) {
                this.onStateChanged(state);
            }
        }
        getIcon(size, icon) {
            let result;
            if (this.iconSet != null) {
                result = this.iconSet.resolve(icon || this.icon, size);
            }
            else {
                result = icon || this.icon;
            }
            return result;
        }
        getDefaultValuesForProps() {
            return {};
        }
        setDefaultValuesForProps(props) {
            this.props.set(props);
            this.props.set(_.extend({
                id: null,
                enabled: false,
                icon: null,
                label: null,
                tooltip: null,
                visible: true,
                checked: false,
                value: null
            }, this.getDefaultValuesForProps(), props || {}));
        }
        mergeCallbacks(args) {
            this.mergeCallback("onSuccess", args);
            this.mergeCallback("onError", args);
            this.mergeCallback("onComplete", args);
        }
        mergeCallback(callbackName, args = {}) {
            var eventHandler = this[callbackName];
            if (args != null && _.isFunction(args[callbackName])) {
                this[callbackName] = (e) => {
                    eventHandler(e);
                    args[callbackName](e);
                };
            }
        }
        ensureIconSet(iconSetId) {
            if (this.m_iconSet == null) {
                this.m_iconSet = Jade.IconSet.get(iconSetId);
            }
        }
    }
    Jade.Command = Command;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandCollection {
        constructor(options = {}) {
            this.m_collection = {};
        }
        add(command) {
            if (this.commandIsValid(command)
                && !this.commandIsRegistered(command)) {
                this.m_collection[command.ID] = command;
            }
        }
        remove(commandId) {
            delete this.m_collection[commandId];
        }
        each(callback) {
            for (var key in this.m_collection) {
                if (callback != null) {
                    callback(key, this.m_collection[key]);
                }
            }
        }
        getByPath(commandPath) {
            let result = null;
            let info = Jade.CommandPath.parse(commandPath);
            if (info.commandId != null) {
                result = this.m_collection[info.commandId];
            }
            return result;
        }
        getById(commandId) {
            return this.m_collection[commandId];
        }
        commandIsValid(command) {
            return command.ID != null
                && command.setState != null
                && command.execute != null;
        }
        commandIsRegistered(command) {
            if (this.commandIsValid(command)) {
                return (command.ID in this.m_collection);
            }
            return false;
        }
    }
    Jade.CommandCollection = CommandCollection;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandExecutor {
        execute(command, args = {}) {
            command.execute(args);
        }
    }
    Jade.CommandExecutor = CommandExecutor;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandGroup {
        constructor(id) {
            this.m_id = null;
            this.m_discriminatorId = null;
            this.m_label = null;
            this.m_visible = true;
            this.m_enabled = true;
            this.m_groupConditionCheck = false;
            this.m_commands = new Jade.CommandCollection();
            this.m_executor = null;
            this.m_id = id;
        }
        get ID() {
            return this.m_id;
        }
        set ID(value) {
            this.m_id = value;
        }
        get label() {
            return this.m_label;
        }
        get commands() {
            return this.m_commands;
        }
        get baseId() {
            return Jade.CommandPath.getGroupBaseId(this.ID);
        }
        get discriminatorId() {
            return this.m_discriminatorId;
        }
        set discriminatorId(value) {
            this.m_discriminatorId = value;
        }
        get executor() {
            this.ensureCommandExecutor();
            return this.m_executor;
        }
        set executor(value) {
            this.m_executor = value;
        }
        setState(state, options = {}) {
            if (state != null) {
                this.onStateChange(state, options);
                this.commands.each((key, command) => {
                    command.setState(state, options);
                });
            }
        }
        execute(commandPath, args) {
            var command = this.commands.getByPath(commandPath);
            if (command != null) {
                this.executor.execute(command, args);
            }
        }
        register(discriminatorId = undefined) {
            if (!Jade.String.isNullOrWhiteSpace(discriminatorId)) {
                this.ID = Jade.CommandPath.buildGroup(this.ID, discriminatorId);
            }
            this.registerCommands(new Jade.OnRegisterCommandsEventArgs({
                commands: this.commands
            }));
        }
        onStateChange(state, options = {}) {
        }
        registerCommands(e) {
        }
        getCommandExecutor() {
            return new Jade.CommandExecutor();
        }
        ensureCommandExecutor() {
            if (this.m_executor == null) {
                this.m_executor = this.getCommandExecutor();
            }
        }
    }
    Jade.CommandGroup = CommandGroup;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandGroupCollection {
        constructor() {
            this.m_collection = {};
        }
        each(callback) {
            if (!_.isFunction(callback)) {
                return;
            }
            for (var key in this.m_collection) {
                callback(key, this.m_collection[key]);
            }
        }
        add(group) {
            if (this.groupIsValid(group)
                && !this.groupIsDefined(group.ID)) {
                this.m_collection[group.ID] = group;
            }
        }
        remove(id) {
            if (this.groupIsDefined(id)) {
                delete this.m_collection[id];
            }
        }
        groupIsValid(group) {
            return group.ID != null;
        }
        groupIsDefined(id) {
            return this.m_collection[id] != null;
        }
        getByPath(commandPath) {
            let result = null;
            let info = Jade.CommandPath.parse(commandPath);
            if (info.groupId != null) {
                result = this.m_collection[info.groupId];
            }
            return result;
        }
        getById(groupId) {
            return this.m_collection[groupId];
        }
        clear() {
            this.m_collection = {};
        }
    }
    Jade.CommandGroupCollection = CommandGroupCollection;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandGroups {
        static get registrations() {
            return this.m_resgistrations;
        }
        static add(type) {
            let instance = new type();
            if (instance != null) {
                this.registrations[instance.ID] = type;
            }
        }
        static remove(id) {
            if (this.isGroupRegistered(id)) {
                delete this.registrations[id];
            }
        }
        static create(groupId) {
            let result = null;
            let baseId = Jade.CommandPath.getGroupBaseId(groupId);
            let discriminatorId = Jade.CommandPath.getGroupDiscriminatorId(groupId);
            if (this.isGroupRegistered(baseId)) {
                result = new this.registrations[baseId](groupId);
            }
            if (result != null) {
                result.register(discriminatorId);
            }
            return result;
        }
        static isGroupRegistered(groupId) {
            let baseId = Jade.CommandPath.getGroupBaseId(groupId);
            return baseId != null && baseId in this.registrations;
        }
    }
    CommandGroups.m_resgistrations = {};
    Jade.CommandGroups = CommandGroups;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class ICommandInfo {
    }
    Jade.ICommandInfo = ICommandInfo;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandPath {
        static build(parts) {
            let group = this.buildGroup(parts.groupBaseId, parts.groupDiscriminatorId);
            let path = [];
            if (parts.contextId != null) {
                path.push(parts.contextId);
            }
            if (group != null) {
                path.push(group);
            }
            if (parts.contextId != null) {
                path.push(parts.contextId);
            }
            return path.join(this.pathSeparator);
        }
        static parse(path) {
            let result = {};
            let parts = path.toString().split(this.pathSeparator);
            switch (parts.length) {
                case 1:
                    result.commandId = parts[0];
                    break;
                case 2:
                    result.groupId = parts[0];
                    result.groupBaseId = this.getGroupBaseId(parts[0]);
                    result.groupDiscriminatorId = this.getGroupDiscriminatorId(parts[0]);
                    result.commandId = parts[1];
                    break;
                case 3:
                    result.contextId = parts[0];
                    result.groupId = parts[1];
                    result.groupBaseId = this.getGroupBaseId(parts[1]);
                    result.groupDiscriminatorId = this.getGroupDiscriminatorId(parts[1]);
                    result.commandId = parts[2];
                    break;
            }
            return result;
        }
        static getGroupBaseId(id) {
            return CommandPath.hasGroupDiscriminatorId(id) ?
                id.split(this.discriminatorSeparator)[0] : id;
        }
        static getGroupDiscriminatorId(id) {
            return CommandPath.hasGroupDiscriminatorId(id) ?
                id.split(this.discriminatorSeparator)[1] : null;
        }
        static parseGroup(path) {
            let result = {};
            let parts = path.toString().split(this.discriminatorSeparator);
            switch (parts.length) {
                case 1:
                    result.groupId = parts[0];
                    result.groupBaseId = parts[0];
                    break;
                case 2:
                    result.groupId = parts[0];
                    result.groupBaseId = parts[0];
                    result.groupDiscriminatorId = parts[1];
                    break;
            }
            return result;
        }
        static buildGroup(groupBaseId, groupDiscriminatorId = null) {
            let result = null;
            let separator = this.discriminatorSeparator;
            if (groupBaseId != null) {
                result = groupBaseId;
                if (groupDiscriminatorId != null) {
                    result = `${result}${separator}${groupDiscriminatorId}`;
                }
            }
            return result;
        }
        static hasGroupDiscriminatorId(id) {
            return id?.indexOf(this.discriminatorSeparator) > -1;
        }
    }
    CommandPath.discriminatorSeparator = "#";
    CommandPath.pathSeparator = ":";
    Jade.CommandPath = CommandPath;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandPathInfo {
        constructor(path) {
            this.m_contextId = null;
            this.m_groupId = null;
            this.m_commandId = null;
            this.m_path = null;
            this.parse(path);
        }
        get contextId() {
            return this.m_contextId;
        }
        get groupId() {
            return this.m_groupId;
        }
        get commandId() {
            return this.m_commandId;
        }
        get path() {
            return this.m_path;
        }
        parse(path) {
            let pieces = path.toString().split(":");
            switch (pieces.length) {
                case 1:
                    this.m_commandId = pieces[0];
                    break;
                case 2:
                    this.m_groupId = pieces[0];
                    this.m_commandId = pieces[1];
                    break;
                case 3:
                    this.m_contextId = pieces[0];
                    this.m_groupId = pieces[1];
                    this.m_commandId = pieces[2];
                    break;
            }
        }
    }
    Jade.CommandPathInfo = CommandPathInfo;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class CommandSubscriber {
        constructor() {
            this.m_commandId = null;
            this.m_callback = null;
            this.m_subscription = null;
        }
        get checked() {
            return this.getValue("checked", false);
        }
        get label() {
            return this.getValue("label", "Label");
        }
        get icon() {
            return this.getValue("icon", null);
        }
        get enabled() {
            return this.getValue("enabled", true);
        }
        get tooltip() {
            return this.getValue("tooltip", "");
        }
        get visible() {
            return this.getValue("visible", true);
        }
        get display() {
            return this.command != null ? this.command.display : "label";
        }
        get value() {
            return this.getValue("value", null);
        }
        get command() {
            return this.info != null ? this.info.command : null;
        }
        get info() {
            this.ensureSubscription();
            return this.m_subscription;
        }
        execute(args) {
            this.command.execute(args);
        }
        subscribe(commandId, callback) {
            this.m_commandId = commandId;
            this.m_callback = callback;
            if (this.command) {
                this.command.props.on("change", () => {
                    this.m_callback();
                });
            }
        }
        ensureSubscription() {
            if (this.m_subscription == null && this.m_commandId != null) {
                this.m_subscription = Jade.Application.instance.contexts.current.commands.resolve(this.m_commandId);
            }
        }
        getValue(id, defaultValue) {
            let result = null;
            if (this.command != null) {
                result = this.command.props.get(id, defaultValue);
            }
            else {
                result = defaultValue;
            }
            return result;
        }
    }
    Jade.CommandSubscriber = CommandSubscriber;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class OnRegisterCommandsEventArgs {
        constructor(options) {
            this.m_commands = options.commands;
        }
        get commands() {
            return this.m_commands;
        }
    }
    Jade.OnRegisterCommandsEventArgs = OnRegisterCommandsEventArgs;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class EventBusCollection {
        constructor() {
            this.m_collection = {};
        }
        add(id) {
            if (!(id in this.m_collection)) {
                this.m_collection[id] = new Jade.EventBus(id);
            }
        }
        contains(id) {
            return id in this.m_collection;
        }
        each(callback) {
            if (_.isFunction(callback)) {
                callback = () => { };
            }
            _.each(this.m_collection, (item, key) => {
                callback(key, item);
            });
        }
        getById(id) {
            var result = null;
            if (id in this.m_collection) {
                result = this.m_collection[id];
            }
            return result;
        }
        remove(id) {
            if (id in this.m_collection) {
                delete this.m_collection[id];
            }
        }
    }
    Jade.EventBusCollection = EventBusCollection;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class EventBusManager {
        constructor() {
            this.m_global = null;
            this.m_locals = null;
            this.m_currentLocalId = null;
            this.m_global = new Jade.EventBus({ label: "global" });
            this.m_locals = new Jade.EventBusCollection();
        }
        get currentLocalId() {
            return this.m_currentLocalId;
        }
        local(id = null) {
            var result = null;
            if (id != null) {
                result = this.m_locals.getById(id);
            }
            else {
                result = this.m_locals.getById(this.m_currentLocalId);
            }
            return result;
        }
        get global() {
            return this.m_global;
        }
        setLocal(id) {
            this.m_currentLocalId = id;
            this.m_locals.add(id);
        }
        destroy(id) {
            this.m_locals.remove(id);
            if (this.m_currentLocalId === id) {
                this.m_currentLocalId = null;
            }
        }
    }
    Jade.EventBusManager = EventBusManager;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    var Data;
    (function (Data) {
        class Database {
            constructor(id) {
                this.m_id = id;
            }
            get id() {
                return this.m_id;
            }
            static get instance() {
                return this.m_instance;
            }
            get connection() {
                return indexedDB.open(this.id);
            }
            static create(id) {
                let dbRequest = indexedDB.open(id);
                dbRequest.onupgradeneeded = ((e) => {
                    _.each(Database.m_onCreateFunctions, (callback) => {
                        callback({
                            db: e.target.result,
                            e: e
                        });
                    });
                });
                this.m_instance = new Database(id);
            }
            open(callback) {
                let request = indexedDB.open(this.id);
                request.onsuccess = ((sender, e) => {
                    callback(request.result);
                });
            }
            static onCreate(callback) {
                Database.m_onCreateFunctions[Database.m_onCreateFunctions.length] = callback;
            }
        }
        Database.m_onCreateFunctions = [];
        Data.Database = Database;
    })(Data = Jade.Data || (Jade.Data = {}));
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    var Data;
    (function (Data) {
        class Table {
            constructor(options) {
                this.m_name = options.name;
            }
            get name() {
                return this.m_name;
            }
        }
        Data.Table = Table;
        class TableOf extends Table {
            getOf(id, success, error) {
                Data.Database.instance.open((database) => {
                    let transaction = database.transaction(this.name);
                    let request = transaction.objectStore(this.name).get(id);
                    request.onsuccess = ((e) => {
                        if (success != null) {
                            success(e.target.result);
                        }
                    });
                    request.onerror = ((e) => {
                        if (error != null) {
                            error();
                        }
                    });
                });
            }
            setOf(id, value, success, error) {
                Data.Database.instance.open((database) => {
                    let transaction = database.transaction(this.name, "readwrite");
                    let request = null;
                    if (id !== null) {
                        request = transaction.objectStore(this.name).put(value, id);
                    }
                    else {
                        request = transaction.objectStore(this.name).put(value);
                    }
                    request.onsuccess = ((e) => {
                        if (success != null) {
                            success();
                        }
                    });
                    request.onerror = ((e) => {
                        if (error != null) {
                            error();
                        }
                    });
                });
            }
            delete(id, success, error) {
                Data.Database.instance.open((database) => {
                    let transaction = database.transaction(this.name, "readwrite");
                    let request = transaction.objectStore(this.name).delete(id);
                    request.onsuccess = ((e) => {
                        if (success != null) {
                            success();
                        }
                    });
                    request.onerror = ((e) => {
                        if (error != null) {
                            error();
                        }
                    });
                });
            }
        }
        Data.TableOf = TableOf;
    })(Data = Jade.Data || (Jade.Data = {}));
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class EventBusEvents {
        constructor() {
            this.m_events = {};
        }
        get events() {
            return this.m_events;
        }
        publish(eventType, data) {
            let register = this.get(eventType);
            if (register != null) {
                register.publish(data);
            }
        }
        subscribe(eventType, callback) {
            let register = null;
            if (Jade.String.isNullOrWhiteSpace(eventType)) {
                return;
            }
            if (!this.exists(eventType)) {
                this.add(eventType);
            }
            register = this.get(eventType);
            return register.subscribe(callback);
        }
        unsubscribe(eventType = null) {
            _.each(this.events, (e, key) => {
                if (eventType == null || eventType == key) {
                    e.unsubscribe();
                }
            });
        }
        exists(eventType) {
            return this.events[eventType] != undefined;
        }
        add(eventType) {
            this.events[eventType] = new Jade.EventBusSubscriptions({
                empty: () => {
                    this.remove(eventType);
                }
            });
        }
        remove(eventType) {
            delete this.events[eventType];
        }
        get(eventType) {
            return this.events[eventType];
        }
    }
    Jade.EventBusEvents = EventBusEvents;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class EventBusSubscription {
        constructor(options) {
            this.m_callback = options.callback;
            this.m_subscriptionId = options.subscriptionId;
            this.m_unsubscribeCallback = options.unsubscribe;
        }
        get subscriptionId() {
            return this.m_subscriptionId;
        }
        get callback() {
            return this.m_callback || (() => { });
        }
        get unsubscribeCallback() {
            return this.m_unsubscribeCallback || (() => { });
        }
        invoke(data) {
            this.callback(data);
        }
        unsubscribe() {
            this.unsubscribeCallback(this.subscriptionId);
        }
    }
    Jade.EventBusSubscription = EventBusSubscription;
})(Jade || (Jade = {}));
var Jade;
(function (Jade) {
    class EventBusSubscriptions {
        constructor(options) {
            this.m_subscriptions = {};
            this.m_emptyCallback = options.empty;
        }
        get emptyCallback() {
            return this.m_emptyCallback || (() => { });
        }
        get subscriptions() {
            return this.m_subscriptions;
        }
        publish(data) {
            Object.keys(this.subscriptions).forEach((subscriptionId) => {
                this.get(subscriptionId).invoke(data);
            });
        }
        subscribe(callback) {
            let subscriptionId = UUID.genV4().toString();
            let subscription;
            this.add(subscriptionId, callback);
            subscription = this.get(subscriptionId);
            return subscription;
        }
        unsubscribe() {
            _.each(this.subscriptions, (e) => {
                e.unsubscribe();
            });
        }
        exists(subscriptionId) {
            return this.subscriptions[subscriptionId] != undefined;
        }
        add(subscriptionId, callback) {
            this.subscriptions[subscriptionId] = new Jade.EventBusSubscription({
                subscriptionId: subscriptionId,
                callback: callback,
                unsubscribe: (id) => {
                    this.remove(id);
                }
            });
        }
        remove(subscriptionId) {
            delete this.subscriptions[subscriptionId];
            if (Object.keys(this.subscriptions).length === 0) {
                this.emptyCallback();
            }
        }
        get(subscriptionId) {
            return this.subscriptions[subscriptionId];
        }
    }
    Jade.EventBusSubscriptions = EventBusSubscriptions;
})(Jade || (Jade = {}));
