UNPKG

@stratusjs/angularjs

Version:

This is the AngularJS package for StratusJS.

2,141 lines 108 kB
System.register(['angular', 'lodash', '@stratusjs/runtime/stratus', '@stratusjs/core/environment', 'angular-sanitize', '@stratusjs/core/errors/errorBase', '@stratusjs/core/datastore/modelBase', '@stratusjs/core/events/eventManager', '@stratusjs/core/misc', '@stratusjs/core/datastore/xhr', 'toastify-js', 'angular-material', '@stratusjs/core/conversion'], (function (exports) {
    'use strict';
    var element, throttle, extend, isObject, isEmpty, cloneDeep, once, forEach, get, isUndefined, set, isArray, isString, unset, isNumber, clone, head, has, filter, map, isEqual, find, reduce, isFunction, _, isDate, isElement, union, kebabCase, size, Stratus, cookie, ErrorBase, ModelBase, EventManager, isJSON, ucfirst, setUrlParams, getAnchorParams, getUrlParams, serializeUrlParams, patch, strcmp, safeUniqueId, poll, flatten, XHR, Toastify, sanitize;
    return {
        setters: [function (module) {
            element = module.element;
        }, function (module) {
            throttle = module.throttle;
            extend = module.extend;
            isObject = module.isObject;
            isEmpty = module.isEmpty;
            cloneDeep = module.cloneDeep;
            once = module.once;
            forEach = module.forEach;
            get = module.get;
            isUndefined = module.isUndefined;
            set = module.set;
            isArray = module.isArray;
            isString = module.isString;
            unset = module.unset;
            isNumber = module.isNumber;
            clone = module.clone;
            head = module.head;
            has = module.has;
            filter = module.filter;
            map = module.map;
            isEqual = module.isEqual;
            find = module.find;
            reduce = module.reduce;
            isFunction = module.isFunction;
            _ = module.default;
            isDate = module.isDate;
            isElement = module.isElement;
            union = module.union;
            kebabCase = module.kebabCase;
            size = module.size;
        }, function (module) {
            Stratus = module.Stratus;
        }, function (module) {
            cookie = module.cookie;
        }, null, function (module) {
            ErrorBase = module.ErrorBase;
        }, function (module) {
            ModelBase = module.ModelBase;
        }, function (module) {
            EventManager = module.EventManager;
        }, function (module) {
            isJSON = module.isJSON;
            ucfirst = module.ucfirst;
            setUrlParams = module.setUrlParams;
            getAnchorParams = module.getAnchorParams;
            getUrlParams = module.getUrlParams;
            serializeUrlParams = module.serializeUrlParams;
            patch = module.patch;
            strcmp = module.strcmp;
            safeUniqueId = module.safeUniqueId;
            poll = module.poll;
            flatten = module.flatten;
        }, function (module) {
            XHR = module.XHR;
        }, function (module) {
            Toastify = module.default;
        }, null, function (module) {
            sanitize = module.sanitize;
        }],
        execute: (function () {

            const getInjector = exports('getInjector', () => {
                const $root = element(document.documentElement);
                return (!$root || !$root.injector) ? null : $root.injector();
            });

            var __awaiter$3 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
                function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
                return new (P || (P = Promise))(function (resolve, reject) {
                    function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
                    function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
                    function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
                    step((generator = generator.apply(thisArg, _arguments || [])).next());
                });
            };
            let injector$1 = getInjector();
            let $rootScope;
            const genericValidationMessages = [
                'There were validation errors.'
            ];
            const transientWriteFailureCode = 'API_TRANSIENT_WRITE_FAILURE';
            const retryableWriteMethods = ['POST', 'PUT', 'PATCH', 'DELETE'];
            const transientWriteRetryBackoff = [250, 750, 1500];
            const transientWriteRetryMaxAttempts = 3;
            function normalizeStatusList(source) {
                const status = get(source, 'meta.status') ||
                    get(source, 'payload.meta.status') ||
                    get(source, 'payload.status') ||
                    get(source, 'status') ||
                    source;
                if (isArray(status)) {
                    return status.filter((entry) => isObject(entry));
                }
                if (isObject(status) &&
                    (isString(get(status, 'code')) ||
                        isString(get(status, 'message')) ||
                        isString(get(status, 'type')))) {
                    return [status];
                }
                return [];
            }
            function parseXHRPayload(error) {
                if (error instanceof ErrorBase) {
                    return get(error, 'payload') || error;
                }
                if (isObject(error) && !isUndefined(get(error, 'response'))) {
                    return get(error, 'response');
                }
                const responseText = get(error, 'responseText');
                return isString(responseText) && isJSON(responseText) ? JSON.parse(responseText) : null;
            }
            function isGenericValidationMessage(message) {
                return genericValidationMessages.indexOf(message.trim()) !== -1;
            }
            function hasStatusCode(source, code) {
                return normalizeStatusList(source).some((status) => status.code === code);
            }
            function isRetryableTransientWritePayload(action, payload) {
                const method = (action || 'GET').toUpperCase();
                if (retryableWriteMethods.indexOf(method) === -1) {
                    return false;
                }
                if (!payload) {
                    return false;
                }
                return !!(get(payload, 'code') === transientWriteFailureCode ||
                    hasStatusCode(payload, transientWriteFailureCode));
            }
            function isRetryableTransientWrite(action, error) {
                const payload = parseXHRPayload(error);
                if (!payload) {
                    return false;
                }
                return isRetryableTransientWritePayload(action, payload);
            }
            function delay(ms) {
                return new Promise((resolve) => setTimeout(resolve, ms));
            }
            function getPreferredStatusMessage(source) {
                const statuses = normalizeStatusList(source);
                if (!statuses.length) {
                    return null;
                }
                const messages = statuses
                    .map((status) => isString(status.message) ? status.message.trim() : '')
                    .filter((message) => !!message);
                if (!messages.length) {
                    return null;
                }
                const specificMessage = messages.find((message) => !isGenericValidationMessage(message));
                return specificMessage || messages[0];
            }
            function hasNotableStatus(source) {
                return normalizeStatusList(source).some((status) => {
                    const type = isString(status.type) ? status.type.toLowerCase() : '';
                    return type === 'warning' || type === 'error' || status.code === 'VALIDATION';
                });
            }
            function hasUsablePayload(payload) {
                return (isArray(payload) && !!payload.length) || (isObject(payload) && !isArray(payload));
            }
            function isSoftStatusFailure(meta, payload) {
                return !!(get(meta, 'success') === false &&
                    hasUsablePayload(payload) &&
                    getPreferredStatusMessage(meta) &&
                    hasNotableStatus(meta));
            }
            const serviceVerify$1 = () => __awaiter$3(void 0, void 0, void 0, function* () {
                return new Promise((resolve, _reject) => __awaiter$3(void 0, void 0, void 0, function* () {
                    if ($rootScope) {
                        resolve(true);
                        return;
                    }
                    if (!injector$1) {
                        injector$1 = getInjector();
                    }
                    if (injector$1) {
                        $rootScope = injector$1.get('$rootScope');
                    }
                    if ($rootScope) {
                        resolve(true);
                        return;
                    }
                    setTimeout(() => {
                        if (cookie('env')) {
                            console.log('wait for $rootScope service:', {
                                $rootScope
                            });
                        }
                        serviceVerify$1().then(resolve);
                    }, 250);
                }));
            });
            const ModelOptionKeys = exports('ModelOptionKeys', ["autoSave", "autoSaveInterval", "autoSaveHalt", "collection", "completed", "manifest", "serviceId", "stagger", "target", "targetSuffix", "toast", "type", "urlRoot", "urlSync", "watch", "withCredentials", "payload", "convoy", "headers", "ignoreKeys", "received"]);
            class Model extends ModelBase {
                constructor(options = {}, attributes) {
                    super(attributes);
                    this.name = 'Model';
                    this.target = null;
                    this.type = null;
                    this.manifest = false;
                    this.stagger = false;
                    this.toast = true;
                    this.identifier = null;
                    this.urlRoot = '/Api';
                    this.targetSuffix = null;
                    this.serviceId = null;
                    this.header = new ModelBase();
                    this.meta = new ModelBase();
                    this.route = new ModelBase();
                    this.collection = null;
                    this.withCredentials = false;
                    this.headers = {};
                    this.pending = false;
                    this.error = false;
                    this.completed = false;
                    this.saving = false;
                    this.changedExternal = false;
                    this.watch = true;
                    this.status = null;
                    this.autoSave = false;
                    this.autoSaveInterval = 4000;
                    this.autoSaveHalt = true;
                    this.autoSaveTimeout = null;
                    this.urlSync = false;
                    this.bracket = {
                        match: /\[[\d+]]/,
                        search: /\[([\d+])]/g,
                        attr: /(^[^[]+)/
                    };
                    this.throttle = throttle(this.fetch, 1000);
                    this.initialize = null;
                    options = typeof options !== 'object' ? {} : options;
                    options.received = options.received || false;
                    extend(this, this.sanitizeOptions(options));
                    if (options.convoy) {
                        const convoy = isJSON(options.convoy) ? JSON.parse(options.convoy) : options.convoy;
                        if (isObject(convoy)) {
                            this.meta.set(convoy.meta || {});
                            const payload = convoy.payload;
                            if (isObject(payload)) {
                                extend(this.data, payload);
                                this.completed = true;
                                options.received = true;
                            }
                            else {
                                console.error('malformed payload:', payload);
                            }
                        }
                        else {
                            console.error('malformed convoy:', convoy);
                        }
                    }
                    if (options.payload) {
                        const payload = isJSON(options.payload) ? JSON.parse(options.payload) : options.payload;
                        if (isObject(payload)) {
                            extend(this.data, payload);
                            this.completed = true;
                            options.received = true;
                        }
                        else {
                            console.error('malformed payload:', payload);
                        }
                    }
                    this.header = new ModelBase();
                    this.meta = new ModelBase();
                    this.route = new ModelBase();
                    if (!isEmpty(this.collection)) {
                        if (this.collection.target) {
                            this.target = this.collection.target;
                        }
                        if (this.collection.meta.has('api')) {
                            this.meta.set('api', this.collection.meta.get('api'));
                        }
                    }
                    this.recv = options.received ? cloneDeep(this.data) : {};
                    this.sent = {};
                    this.ignoreKeys = options.ignoreKeys || ['$$hashKey'];
                    if (this.target) {
                        this.urlRoot += '/' + ucfirst(this.target);
                    }
                    const that = this;
                    this.initialize = once(this.initialize || function defaultInitializer() {
                        if (that.completed && (that.watch || that.autoSave)) {
                            that.watcher().then();
                        }
                        if (that.manifest && !that.getIdentifier()) {
                            that.sync('POST', that.meta.has('api') ? {
                                meta: that.meta.get('api'),
                                payload: {}
                            } : {}).catch((error) => __awaiter$3(this, void 0, void 0, function* () {
                                console.error('MANIFEST:', error);
                                if (!that.toast) {
                                    return;
                                }
                                const errorMessage = that.errorMessage(error);
                                const formatMessage = errorMessage ? `: ${errorMessage}` : '.';
                                Toastify({
                                    text: `Unable to Manifest ${that.target}${formatMessage}`,
                                    duration: 12000,
                                    close: true,
                                    stopOnFocus: true,
                                    style: {
                                        background: '#E14D45',
                                    }
                                }).showToast();
                                that.errorMessage(error);
                            }));
                        }
                    });
                    if (!this.stagger) {
                        this.initialize();
                    }
                }
                resetXHRFlags() {
                    this.pending = false;
                    this.saving = false;
                }
                sanitizeOptions(options) {
                    const sanitizedOptions = {};
                    forEach(ModelOptionKeys, (key) => {
                        const data = get(options, key);
                        if (isUndefined(data)) {
                            return;
                        }
                        set(sanitizedOptions, key, data);
                    });
                    return sanitizedOptions;
                }
                getReadOnlyFields() {
                    const fields = this.meta.get('readOnlyFields');
                    return isArray(fields) ? fields.filter((field) => isString(field) && !!field) : [];
                }
                deletePath(obj, path) {
                    if (!obj || !isString(path)) {
                        return;
                    }
                    unset(obj, path);
                }
                pruneEmptyBranches(obj) {
                    if (!isObject(obj) || isArray(obj)) {
                        return isEmpty(obj);
                    }
                    const target = obj;
                    for (const key of Object.keys(target)) {
                        const value = target[key];
                        if (isObject(value) && !isArray(value)) {
                            const emptyChild = this.pruneEmptyBranches(value);
                            if (emptyChild) {
                                delete target[key];
                            }
                            continue;
                        }
                        if (isUndefined(value)) {
                            delete target[key];
                        }
                    }
                    return isEmpty(target);
                }
                sanitizeReadOnlyPatchPayload(payload) {
                    if (!isObject(payload)) {
                        return payload;
                    }
                    const sanitized = cloneDeep(payload);
                    const readOnlyFields = this.getReadOnlyFields();
                    if (!readOnlyFields.length) {
                        return sanitized;
                    }
                    forEach(readOnlyFields, (path) => {
                        this.deletePath(sanitized, path);
                    });
                    this.pruneEmptyBranches(sanitized);
                    return sanitized;
                }
                getSavablePatch() {
                    return this.sanitizeReadOnlyPatchPayload(cloneDeep(this.toPatch()));
                }
                watcher() {
                    return __awaiter$3(this, void 0, void 0, function* () {
                        if (this.watching) {
                            return true;
                        }
                        this.watching = true;
                        if (!$rootScope) {
                            yield serviceVerify$1();
                        }
                        $rootScope.$watch(() => this.data, (_newData, _priorData) => this.handleChanges(), true);
                    });
                }
                handleChanges(changeSet) {
                    const isUserChangeSet = isUndefined(changeSet);
                    if (isUserChangeSet) {
                        changeSet = super.handleChanges();
                        changeSet = this.sanitizeReadOnlyPatchPayload(changeSet);
                        if (!isEmpty(this.patch)) {
                            this.patch = this.sanitizeReadOnlyPatchPayload(this.patch);
                        }
                        this.changed = !isEmpty(this.patch);
                    }
                    if (!changeSet || isEmpty(changeSet)) {
                        return changeSet;
                    }
                    if (this.error && !this.completed && this.getIdentifier()) {
                        const action = isUserChangeSet ? 'save' : 'sync url for';
                        console.warn(`Blocked attempt to ${action} a persisted model that has not been fetched successfully.`);
                        return;
                    }
                    if (!isUserChangeSet) {
                        if (cookie('env')) {
                            console.info('Attempting URL Sync for non-User ChangeSet:', changeSet);
                        }
                    }
                    if (this.urlSync) {
                        if (get(changeSet, 'id')) {
                            const newUrl = setUrlParams({
                                id: get(changeSet, 'id') || this.getIdentifier()
                            });
                            if (newUrl !== document.location.href) {
                                window.location.replace(newUrl);
                            }
                        }
                        const version = getAnchorParams('version');
                        const versionId = !isEmpty(version) ? parseInt(version, 10) : 0;
                        if (versionId && versionId !== get(changeSet, 'version.id')) {
                            if (cookie('env')) {
                                console.warn('replacing version:', versionId);
                            }
                        }
                    }
                    if (!isUserChangeSet) {
                        return;
                    }
                    this.saveIdle();
                    this.throttleTrigger('change', this);
                    if (this.collection) {
                        this.collection.throttleTrigger('change', this);
                    }
                    return changeSet;
                }
                getIdentifier() {
                    return (this.identifier = this.get('id') || this.route.get('identifier') || this.identifier);
                }
                getType() {
                    return (this.type = this.type || this.target || 'orphan');
                }
                getHash() {
                    return this.getType() + (isNumber(this.getIdentifier()) ? this.getIdentifier().toString() : this.getIdentifier());
                }
                isNew() {
                    return !this.getIdentifier();
                }
                url() {
                    let url = this.getIdentifier() ? `${this.urlRoot}/${this.getIdentifier()}` : `${this.urlRoot}${this.targetSuffix || ''}`;
                    if (getUrlParams('version')) {
                        url += url.includes('?') ? '&' : '?';
                        url += 'options[version]=' + getUrlParams('version');
                    }
                    return url;
                }
                serialize(obj, chain) { return serializeUrlParams(obj, chain); }
                sync(action, data, options) {
                    this.pending = true;
                    this.trigger('change', this);
                    if (this.collection) {
                        this.collection.pending = true;
                        this.collection.throttleTrigger('change');
                    }
                    this.sent = cloneDeep(this.data);
                    return new Promise((resolve, reject) => __awaiter$3(this, void 0, void 0, function* () {
                        let successfulResponseMeta = null;
                        action = action || 'GET';
                        options = options || {};
                        const request = {
                            method: action,
                            url: this.url(),
                            headers: clone(this.headers),
                            withCredentials: this.withCredentials,
                        };
                        if (!isUndefined(data)) {
                            if (['GET', 'DELETE'].includes(action)) {
                                if (isObject(data) && Object.keys(data).length) {
                                    request.url += request.url.includes('?') ? '&' : '?';
                                    request.url += this.serialize(data);
                                }
                            }
                            else {
                                request.headers['Content-Type'] = 'application/json';
                                request.data = data;
                            }
                        }
                        if (cookie('env')) {
                            console.log('Prototype:', request);
                        }
                        if (Object.prototype.hasOwnProperty.call(options, 'headers') && typeof options.headers === 'object') {
                            Object.keys(options.headers).forEach((headerKey) => {
                                request.headers[headerKey] = options.headers[headerKey];
                            });
                        }
                        const sendRequest = (attempt = 1) => __awaiter$3(this, void 0, void 0, function* () {
                            this.xhr = new XHR(request);
                            try {
                                const response = yield this.xhr.send();
                                if (attempt < transientWriteRetryMaxAttempts &&
                                    isRetryableTransientWritePayload(request.method, response)) {
                                    const nextAttempt = attempt + 1;
                                    console.warn(`XHR: ${request.method} ${request.url} returned ${transientWriteFailureCode}; retrying ${nextAttempt} of ${transientWriteRetryMaxAttempts}.`, response);
                                    if (this.toast) {
                                        Toastify({
                                            text: `The server was busy saving. Retrying ${nextAttempt} of ${transientWriteRetryMaxAttempts}...`,
                                            duration: 4000,
                                            close: true,
                                            stopOnFocus: true,
                                            style: {
                                                background: '#D9902F',
                                            }
                                        }).showToast();
                                    }
                                    yield delay(transientWriteRetryBackoff[attempt - 1] || transientWriteRetryBackoff[transientWriteRetryBackoff.length - 1]);
                                    return sendRequest(nextAttempt);
                                }
                                return response;
                            }
                            catch (error) {
                                if (attempt < transientWriteRetryMaxAttempts &&
                                    isRetryableTransientWrite(request.method, error)) {
                                    const nextAttempt = attempt + 1;
                                    console.warn(`XHR: ${request.method} ${request.url} returned ${transientWriteFailureCode}; retrying ${nextAttempt} of ${transientWriteRetryMaxAttempts}.`, error);
                                    if (this.toast) {
                                        Toastify({
                                            text: `The server was busy saving. Retrying ${nextAttempt} of ${transientWriteRetryMaxAttempts}...`,
                                            duration: 4000,
                                            close: true,
                                            stopOnFocus: true,
                                            style: {
                                                background: '#D9902F',
                                            }
                                        }).showToast();
                                    }
                                    yield delay(transientWriteRetryBackoff[attempt - 1] || transientWriteRetryBackoff[transientWriteRetryBackoff.length - 1]);
                                    return sendRequest(nextAttempt);
                                }
                                throw error;
                            }
                        });
                        sendRequest().then((response) => {
                            this.status = this.xhr.status;
                            if (this.watch || this.autoSave) {
                                this.watcher();
                            }
                            const propagateError = () => {
                                this.error = true;
                                this.resetXHRFlags();
                                if (this.collection) {
                                    this.collection.pending = false;
                                }
                                this.trigger('error', this);
                                this.trigger('complete', this);
                                if (this.collection instanceof Collection) {
                                    this.collection.throttleTrigger('change');
                                }
                            };
                            if (!isObject(response) && !isArray(response)) {
                                const error = new ErrorBase({
                                    payload: response,
                                    message: `Invalid Payload: ${request.method} ${request.url}`
                                }, {});
                                propagateError();
                                reject(error);
                                return;
                            }
                            this.header.set(this.xhr.getAllResponseHeaders() || {});
                            successfulResponseMeta = response.meta || null;
                            const responseMeta = successfulResponseMeta || {};
                            this.meta.set(responseMeta);
                            this.route.set(response.route || {});
                            const payload = response.payload || response;
                            this.error = false;
                            if ((this.meta.has('success') && !this.meta.get('success') && !isSoftStatusFailure(responseMeta, payload))) {
                                this.error = true;
                            }
                            else if (isArray(payload) && payload.length) {
                                this.recv = head(payload);
                            }
                            else if (isObject(payload) && !isArray(payload)) {
                                this.recv = payload;
                            }
                            else {
                                if (!this.meta.has('status') && !this.meta.has('success')) {
                                    this.error = true;
                                }
                                console.warn(`Invalid Payload: ${request.method} ${request.url}`);
                            }
                            if (this.error) {
                                const error = new ErrorBase({
                                    payload: response,
                                    message: `Invalid Payload: ${request.method} ${request.url}`
                                }, {});
                                propagateError();
                                reject(error);
                                return;
                            }
                            const incomingChangeSet = this.completed ? cloneDeep(patch(this.recv, this.sent)) : {};
                            if (!isEmpty(incomingChangeSet)) {
                                if (cookie('env')) {
                                    console.log('Incoming ChangeSet detected:', cookie('debug_change_set')
                                        ? JSON.stringify(incomingChangeSet)
                                        : incomingChangeSet);
                                }
                                this.handleChanges(incomingChangeSet);
                            }
                            const intermediateData = cloneDeep(this.recv);
                            const intermediateChangeSet = cloneDeep(patch(this.data, this.sent));
                            if (!isEmpty(intermediateChangeSet)) {
                                if (cookie('env')) {
                                    console.log('Intermediate ChangeSet detected:', cookie('debug_change_set')
                                        ? JSON.stringify(intermediateChangeSet)
                                        : intermediateChangeSet);
                                }
                                forEach(intermediateChangeSet, (element, key) => {
                                    set(intermediateData, key, element);
                                });
                            }
                            this.data = cloneDeep(intermediateData);
                            this.changed = false;
                            this.changedExternal = false;
                            this.saving = false;
                            this.handleChanges();
                            this.patch = {};
                            this.resetXHRFlags();
                            this.completed = true;
                            if (this.collection) {
                                this.collection.pending = false;
                            }
                            this.meta.clearTemp();
                            const statusMessage = getPreferredStatusMessage(response.meta || {});
                            if (this.toast && statusMessage && hasNotableStatus(response.meta || {})) {
                                Toastify({
                                    text: statusMessage,
                                    duration: 12000,
                                    close: true,
                                    stopOnFocus: true,
                                    style: {
                                        background: '#D9902F',
                                    }
                                }).showToast();
                            }
                            this.trigger('success', this);
                            this.trigger('change', this);
                            this.trigger('complete', this);
                            if (this.collection instanceof Collection) {
                                this.collection.throttleTrigger('change');
                            }
                            resolve(this.data);
                            return;
                        })
                            .catch((error) => {
                            if (successfulResponseMeta && get(successfulResponseMeta, 'success') === true) {
                                console.warn(`XHR: ${request.method} ${request.url} completed with API success but post-save handling failed.`, error);
                                this.error = false;
                                this.resetXHRFlags();
                                this.completed = true;
                                if (this.collection) {
                                    this.collection.pending = false;
                                }
                                const statusMessage = getPreferredStatusMessage(successfulResponseMeta);
                                if (this.toast && statusMessage && hasNotableStatus(successfulResponseMeta)) {
                                    Toastify({
                                        text: statusMessage,
                                        duration: 12000,
                                        close: true,
                                        stopOnFocus: true,
                                        style: {
                                            background: '#D9902F',
                                        }
                                    }).showToast();
                                }
                                this.trigger('success', this);
                                this.trigger('change', this);
                                this.trigger('complete', this);
                                if (this.collection instanceof Collection) {
                                    this.collection.throttleTrigger('change');
                                }
                                resolve(this.data);
                                return;
                            }
                            this.status = 500;
                            this.error = true;
                            this.resetXHRFlags();
                            console.error(`XHR: ${request.method} ${request.url}`, error);
                            reject(error);
                            return;
                        });
                    }));
                }
                fetch(action, data, options) {
                    return new Promise((resolve, reject) => __awaiter$3(this, void 0, void 0, function* () {
                        this.sync(action, data || this.meta.get('api'), options)
                            .then(resolve)
                            .catch((error) => __awaiter$3(this, void 0, void 0, function* () {
                            this.status = 500;
                            this.error = true;
                            this.resetXHRFlags();
                            console.error('FETCH:', error);
                            if (!this.toast) {
                                reject(error);
                                return;
                            }
                            const errorMessage = this.errorMessage(error);
                            const formatMessage = errorMessage ? `: ${errorMessage}` : '.';
                            Toastify({
                                text: `Unable to Fetch ${this.target}${formatMessage}`,
                                duration: 12000,
                                close: true,
                                stopOnFocus: true,
                                style: {
                                    background: '#E14D45',
                                }
                            }).showToast();
                            reject(error);
                            return;
                        }));
                    }));
                }
                save(options) {
                    this.saving = true;
                    options = options || {};
                    if (!isObject(options)) {
                        console.warn('invalid options supplied:', options);
                        options = {};
                    }
                    if (has(options, 'force') && options.force) {
                        options.patch = has(options, 'patch') ? options.patch : false;
                        return this.doSave(options);
                    }
                    if (!this.isNew() && (this.pending || !this.completed || isEmpty(this.getSavablePatch()))) {
                        console.warn(`Blocked attempt to save ${isEmpty(this.toPatch()) ? 'an empty payload' : 'a duplicate XHR'} to a persisted model.`);
                        return new Promise((resolve, _reject) => {
                            this.saving = false;
                            resolve(this.data);
                        });
                    }
                    return this.doSave(options);
                }
                doSave(options) {
                    options = options || {};
                    if (!isObject(options)) {
                        console.warn('invalid options supplied:', options);
                        options = {};
                    }
                    options.patch = has(options, 'patch') ? options.patch : true;
                    return new Promise((resolve, reject) => __awaiter$3(this, void 0, void 0, function* () {
                        this.sync(this.getIdentifier() ? 'PUT' : 'POST', this.toJSON({
                            patch: options.patch
                        }))
                            .then(resolve)
                            .catch((error) => __awaiter$3(this, void 0, void 0, function* () {
                            this.error = true;
                            this.resetXHRFlags();
                            console.error('SAVE:', error);
                            if (!this.toast) {
                                reject(error);
                                return;
                            }
                            const errorMessage = this.errorMessage(error) || getPreferredStatusMessage(this.meta.data);
                            Toastify({
                                text: errorMessage || `Unable to Save ${this.target}.`,
                                duration: 12000,
                                close: true,
                                stopOnFocus: true,
                                style: {
                                    background: '#E14D45',
                                }
                            }).showToast();
                            reject(error);
                            return;
                        }));
                    }));
                }
                saveIdle() {
                    if (this.autoSaveTimeout) {
                        clearTimeout(this.autoSaveTimeout);
                    }
                    if (this.pending || !this.completed || this.isNew() || isEmpty(this.getSavablePatch())) {
                        return;
                    }
                    if (this.autoSaveHalt && !this.autoSave) {
                        return;
                    }
                    this.autoSaveTimeout = setTimeout(() => {
                        if (!this.autoSaveHalt && !this.autoSave) {
                            this.saveIdle();
                            return;
                        }
                        this.save().then();
                    }, this.autoSaveInterval);
                }
                throttleSave() {
                    return new Promise((resolve, reject) => {
                        const request = this.throttle();
                        console.log('throttle request:', request);
                        request.then((data) => {
                            console.log('throttle received:', data);
                            resolve(data);
                        }).catch(reject);
                    });
                }
                toJSON(options) {
                    options = options || {};
                    if (!isObject(options)) {
                        options = {};
                    }
                    options.patch = (options.patch && !this.isNew());
                    let data = super.toJSON(options);
                    if (options.patch) {
                        data = this.sanitizeReadOnlyPatchPayload(data);
                    }
                    const metaData = this.meta.get('api');
                    if (metaData) {
                        data = {
                            meta: metaData,
                            payload: data
                        };
                    }
                    return data;
                }
                buildPath(path) {
                    const acc = [];
                    if (!isString(path)) {
                        return acc;
                    }
                    let cur;
                    let search;
                    forEach(path.split('.'), (link) => {
                        if (link.match(this.bracket.match)) {
                            cur = this.bracket.attr.exec(link);
                            if (cur !== null) {
                                acc.push(cur[1]);
                                cur = null;
                            }
                            else {
                                cur = false;
                            }
                            search = this.bracket.search.exec(link);
                            while (search !== null) {
                                if (cur !== false) {
                                    cur = parseInt(search[1], 10);
                                    if (!isNaN(cur)) {
                                        acc.push(cur);
                                    }
                                    else {
                                        cur = false;
                                    }
                                }
                                search = this.bracket.search.exec(link);
                            }
                        }
                        else {
                            acc.push(link);
                        }
                    });
                    return acc;
                }
                get(attr) {
                    if (typeof attr !== 'string' || !this.data || typeof this.data !== 'object') {
                        return undefined;
                    }
                    return get(this.data, attr);
                }
                find(attr, key, value) {
                    if (typeof attr === 'string') {
                        attr = this.get(attr);
                    }
                    return !isArray(attr) ? attr : attr.find((obj) => obj[key] === value);
                }
                set(attr, value) {
                    if (!attr) {
                        console.warn('No attr for model.set()!');
                        return this;
                    }
                    if (typeof attr === 'object') {
                        forEach(attr, (v, k) => this.setAttribute(k, v));
                        return this;
                    }
                    this.setAttribute(attr, value);
                    return this;
                }
                setAttribute(attr, value) {
                    if (typeof attr !== 'string') {
                        console.warn('Malformed attr for model.setAttribute()!');
                        return false;
                    }
                    set(this.data, attr, value);
                    this.throttleTrigger('change', this);
                    this.throttleTrigger(`change:${attr}`, value);
                }
                toggle(attribute, item, options) {
                    if (typeof options === 'object' &&
                        !isUndefined(options.multiple) &&
                        isUndefined(options.strict)) {
                        options.strict = true;
                    }
                    options = extend({
                        multiple: true
                    }, isObject(options) ? options : {});
                    const request = attribute.split('[].');
                    let target = this.get(request.length > 1 ? request[0] : attribute);
                    if (isUndefined(target) ||
                        (options.strict && isArray(target) !==
                            options.multiple)) {
                        target = options.multiple ? [] : null;
                        this.set(request.length > 1 ? request[0] : attribute, target);
                    }
                    if (isArray(target)) {
                        if (isUndefined(item)) {
                            this.set(attribute, null);
                        }
                        else if (!this.exists(attribute, item)) {
                            target.push(item);
                        }
                        else {
                            forEach(target, (element, key) => {
                                const child = (request.length > 1 &&
                                    typeof element === 'object' && request[1] in element)
                                    ? element[request[1]]
                                    : element;
                                const childId = (typeof child === 'object' && child.id)
                                    ? child.id
                                    : child;
                                const itemId = (typeof item === 'object' && item.id)
                                    ? item.id
                                    : item;
                                if (childId === itemId || (isString(childId) && isString(itemId) && strcmp(childId, itemId) === 0)) {
                                    target.splice(key, 1);
                                }
                            });
                        }
                    }
                    else if (typeof target === 'object' || typeof target === 'number') {
                        this.set(attribute, !this.exists(attribute, item) ? item : null);
                    }
                    else if (isUndefined(item)) {
                        this.set(attribute, !target);
                    }
                    return this.get(attribute);
                }
                pluck(attr) {
                    if (typeof attr !== 'string' || attr.indexOf('[].') === -1) {
                        return this.get(attr);
                    }
                    const request = attr.split('[].');
                    if (request.length <= 1) {
                        return undefined;
                    }
                    attr = this.get(request[0]);
                    if (!attr || !isArray(attr)) {
                        return undefined;
                    }
                    const list = filter(map(attr, (element) => get(element, request[1])));
                    return list.length ? list : undefined;
                }
                exists(attribute, item) {
                    if (!item) {
                        attribute = this.get(attribute);
                        return typeof attribute !== 'undefined' && attribute;
                    }
                    if (typeof attribute === 'string' && item) {
                        attribute = this.pluck(attribute);
                        if (isArray(attribute)) {
                            return typeof attribute.find((element) => element === item || ((typeof element === 'object' && element.id && element.id === item) || isEqual(element, item))) !== 'undefined';
                        }
                        return attribute === item || (typeof attribute === 'object' && attribute.id && (isEqual(attribute, item) || attribute.id === item));
                    }
                    return false;
                }
                destroy() {
                    if (this.isNew()) {
                        return new Promise((resolve, _reject) => {
                            this.throttleTrigger('change');
                            if (this.collection) {
                                this.collection.remove(this);
                            }
                            resolve(this.data);
                        });
                    }
                    return new Promise((_resolve, reject) => __awaiter$3(this, void 0, void 0, function* () {
                        let deleteData = {};
                        if (!isEmpty(this.meta.get('api'))) {
                            deleteData = this.meta.get('api');
                        }
                        this.sync('DELETE', deleteData)
                            .then((_data) => {
                            if (this.error) {
                                reject(this.error);
                                return;
                            }
                            this.throttleTrigger('change');
                            if (this.collection) {
                                this.collection.remove(this);
                            }
                            _resolve(this.data);
                        })
                            .catch((error) => __awaiter$3(this, void 0, void 0, function* () {
                            this.error = true;
                            this.resetXHRFlags();
                            console.error('DESTROY:', error);
                            if (!this.toast) {
                                reject(error);
                                return;
                            }
                            const errorMessage = this.errorMessage(error);
                            const formatMessage = errorMessage ? `: ${errorMessage}` : '.';
                            Toastify({
                                text: `Unable to Delete ${this.target}${formatMessage}`,
                                duration: 12000,
                                close: true,
                                stopOnFocus: true,
                                style: {
                                    background: '#E14D45',
                                }
                            }).showToast();
                            reject(error);
                            return;
                        }));
                    }));
                }
                errorMessage(error) {
                    if (error instanceof ErrorBase) {
                        console.error(`[${error.code}] ${error.message}`, error);
                        const message = getPreferredStatusMessage(error);
                        if (message) {
                            return message;
                        }
                        return error.code !== 'Internal' ? error.message : null;
                    }
                    const digest = (error.responseText && isJSON(error.responseText)) ? JSON.parse(error.responseText) : null;
                    if (!digest) {
                        return null;
                    }
                    const message = getPreferredStatusMessage(digest) || get(digest, 'error.exception[0].message') || null;
                    if (!message) {
                        return null;
                    }
                    if (!cookie('env') && has(digest, 'error.exception[0].message')) {
                        console.error('[xhr] server:', message);
                        return null;
                    }
                    return message;
                }
            } exports('Model', Model);
            Stratus.Services.Model = [
                '$provide', ($provide) => {
                    $provide.factory('Model', [
                        () => {
                            return Model;
                        }
                    ]);
                }
            ];
            Stratus.Data.Model = Model;

            var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
                function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
                return new (P || (P = Promise))(function (resolve, reject) {
                    function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
                    function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
                    function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
                    step((generator = generator.apply(thisArg, _arguments || [])).next());
                });
            };
            const CollectionOptionKeys = exports('CollectionOptionKeys', ["autoSave", "autoSaveInterval", "cache", "direct", "target", "targetSuffix", "urlRoot", "watch", "payload", "convoy", "headers"]);
            class Collection extends EventManager {
                constructor(options = {}) {
                    super();
                    this.name = 'Collection';
                    this.direct = false;
                    this.target = null;
                    this.targetSuffix = null;
                    this.urlRoot = '/Api';
                    this.toast = true;
                    this.qualifier = '';
                    this.serviceId = null;
                    this.infinite = false;
                    this.threshold = 0.5;
                    this.decay = 0;
                    this.header = new ModelBase();
                    this.meta = new ModelBase();
                    this.model = Model;
                    this.models = [];
                    this.types = [];
                    this.withCredentials = false;
                    this.headers = {};
                    this.cacheResponse = {};
                    this.cacheHeaders = {};
                    this.cache = false;
                    this.pending = false;
                    this.error = false;
                    this.completed = false;
                    this.filtering = false;
                    this.paginate = false;
                    this.collectionApiHydratedFromUrl = false;
                    this.watch = false;
                    this.autoSave = false;
                    this.autoSaveInterval = 2500;
                    this.throttle = throttle(this.fetch, 1000);
                    options = (!options || typeof options !== 'object') ? {} : options;
                    extend(this, options);
                    if (this.target) {
                        this.urlRoot += '/' + ucfirst(this.target);
                    }
                    if (options.convoy) {
                        const convoy = isJSON(options.convoy) ? JSON.parse(options.convoy) : options.convoy;
                        if (isObject(convoy)) {
                            this.meta.set(convoy.meta || {});
                            const models = convoy.payload;
                            if (isArray(models)) {
                                this.inject(models);
                                this.completed = true;
                            }
                            else {
                                console.error('malformed payload:', models);
                            }
                        }
                        else {
                            console.error('malformed convoy:', convoy);
                        }
                    }
                    if (options.payload) {
                        const models = isJSON(options.payload) ? JSON.parse(options.payload) : options.payload;
                        if (isArray(models)) {
                            this.inject(models);
                            this.completed = true;
                        }
                        else {
                            console.error('malformed payload:', models);
                        }
                    }
                }
                sanitizeOptions(options) {
                    const sanitizedOptions = {};
                    forEach(CollectionOptionKeys, (key) => {
                        const data = get(options, key);
                        if (isUndefined(data)) {
                            return;
                        }
                        set(sanitizedOptions, key, data);
                    });
                    return sanitizedOptions;
                }
                serialize(obj, chain) {
                    const str = [];
                    obj = obj || {};
                    forEach(obj, (value, key) => {
                        if (isObject(value)) {
                            if (chain) {
                                key = chain + '[' + key + ']';
                            }
                            const serialized = this.serialize(value, key);
                            if (serialized) {
                                str.push(serialized);
                            }
                        }
                        else {
                            let encoded = '';
                            if (chain) {
                                encoded += chain + '[';
                            }
                            encoded += key;
                            if (chain) {
                                encoded += ']';
                            }
                            str.push(encoded + '=' + value);
                        }
                    });
                    return str.join('&');
                }
                url() {
                    return this.urlRoot + (this.targetSuffix || '');
                }
                inject(data, type) {
                    if (!isArray(data)) {
                        return;
                    }
                    if (this.types && this.types.indexOf(type) === -1) {
                        this.types.push(type);
                    }
                    if (!this.direct) {
                        data.forEach((target) => {
                            this.models.push(new Model({
                                autoSave: this.autoSave,
                                autoSaveInterval: this.autoSaveInterval,
                                collection: this,
                                completed: true,
                                received: true,
                                toast: this.toast,
                                type: type || null,
                                watch: this.watch
                            }, target));
                        });
                    }
                }
                sync(action, data, options) {
                    this.pending = true;
                    return new Promise((resolve, reject) => __awaiter$2(this, void 0, void 0, function* () {
                        action = action || 'GET';
                        options = options || {};
                        const request = {
                            method: action,
                            url: this.url(),
                            headers: clone(this.headers),
                            withCredentials: this.withCredentials,
                        };
                        if (!isUndefined(data)) {
                            if (isObject(data) && Object.prototype.hasOwnProperty.call(data, 'p')) {
                                const validPage = this.normalizePositiveWholeNumber(data.p);
                                if (isUndefined(validPage)) {
                                    delete data.p;
                                }
                                else {
                                    data.p = validPage;
                                }
                            }
                            if (action === 'GET') {
                                if (isObject(data) && Object.keys(data).length) {
                                    request.url += request.url.includes('?') ? '&' : '?';
                                    request.url += this.serialize(data);
                                }
                            }
                            else {
                                request.headers['Content-Type'] = 'application/json';
                                request.data = JSON.stringify(data);
                            }
                        }
                        if (Object.prototype.hasOwnProperty.call(options, 'headers') && typeof options.headers === 'object') {
                            Object.keys(options.headers).forEach((headerKey) => {
                                request.headers[headerKey] = options.headers[headerKey];
                            });
                        }
                        const queryHash = `${request.method}:${request.url}`;
                        if (options.nocache) {
                            if (queryHash in this.cacheResponse) {
                                delete this.cacheResponse[queryHash];
                            }
                            if (queryHash in this.cacheHeaders) {
                                delete this.cacheHeaders[queryHash];
                            }
                        }
                        const xhr = new XHR(request);
                        this.xhr = xhr;
                        const handler = (response, responseXhr = xhr) => {
                            if (!isObject(response) && !isArray(response)) {
                                const error = new ErrorBase({
                                    payload: response,
                                    message: `Invalid Payload: ${request.method} ${request.url}`
                                }, {});
                                this.error = true;
                                this.pending = false;
                                this.throttleTrigger('change');
                                this.trigger('error', error);
                                reject(error);
                                return;
                            }
                            let responseHeaders = null;
                            if (this.cache && request.method === 'GET') {
                                if (!(queryHash in this.cacheResponse)) {
                                    this.cacheResponse[queryHash] = cloneDeep(response);
                                }
                                if (!(queryHash in this.cacheHeaders)) {
                                    this.cacheHeaders[queryHash] = responseXhr.getAllResponseHeaders();
                                }
                                else {
                                    responseHeaders = this.cacheHeaders[queryHash];
                                }
                            }
                            this.header.set(responseHeaders || responseXhr.getAllResponseHeaders());
                            this.meta.set(response.meta || {});
                            this.models = [];
                            const payload = response.payload || response;
                            this.error = false;
                            if ((this.meta.has('success') && !this.meta.get('success'))) {
                                this.error = true;
                            }
                            else if (this.direct) {
                                this.models = payload;
                            }
                            else if (isArray(payload)) {
                                this.inject(payload);
                            }
                            else if (isObject(payload)) {
                                forEach(payload, (value, key) => {
                                    this.inject(value, key);
                                });
                            }
                            else {
                                if (!this.meta.has('status') && !this.meta.has('success')) {
                                    this.error = true;
                                }
                                console.warn(`Invalid Payload: ${request.method} ${request.url}`);
                            }
                            this.pending = false;
                            this.completed = true;
                            this.filtering = !isEmpty(this.meta.get('api.q'));
                            this.paginate = !isEmpty(this.meta.get('api.p'));
                            this.meta.clearTemp();
                            this.throttleTrigger('change');
                            this.trigger('complete');
                            resolve(this.models);
                        };
                        if (this.cache && request.method === 'GET' && queryHash in this.cacheResponse) {
                            handler(this.cacheResponse[queryHash]);
                            return;
                        }
                        const prewarmRequest = typeof window !== 'undefined' && window.StratusApiPrewarm
                            ? window.StratusApiPrewarm[request.url]
                            : null;
                        const prewarm = request.method === 'GET'
                            && prewarmRequest
                            && typeof prewarmRequest.then === 'function'
                            ? prewarmRequest
                            : null;
                        if (prewarm) {
                            prewarm
                                .then((response) => {
                                if (response && typeof response.clone === 'function') {
                                    return response.clone().json();
                                }
                                return response;
                            })
                                .then((response) => {
                                const prewarmXhr = {
                                    getAllResponseHeaders: () => ({})
                                };
                                this.xhr = prewarmXhr;
                                handler(response, prewarmXhr);
                            })
                                .catch(() => {
                                const retryXhr = new XHR(request);
                                this.xhr = retryXhr;
                                const xhrPromise = retryXhr.send();
                                if (request.method === 'GET' && typeof window !== 'undefined') {
                                    window.StratusApiPrewarm = window.StratusApiPrewarm || {};
                                    window.StratusApiPrewarm[request.url] = xhrPromise;
                                }
                                xhrPromise.then((response) => handler(response, retryXhr))
                                    .catch((error) => {
                                    console.error(`XHR: ${request.method} ${request.url}`);
                                    this.throttleTrigger('change');
                                    this.trigger('error', error);
                                    reject(error);
                                });
                            });
                            return;
                        }
                        const xhrPromise = this.xhr.send();
                        if (request.method === 'GET' && typeof window !== 'undefined') {
                            window.StratusApiPrewarm = window.StratusApiPrewarm || {};
                            window.StratusApiPrewarm[request.url] = xhrPromise;
                        }
                        xhrPromise.then((response) => handler(response, xhr))
                            .catch((error) => {
                            console.error(`XHR: ${request.method} ${request.url}`);
                            this.throttleTrigger('change');
                            this.trigger('error', error);
                            reject(error);
                            return;
                        });
                    }));
                }
                fetch(action, data, options) {
                    return new Promise((resolve, reject) => __awaiter$2(this, void 0, void 0, function* () {
                        this.hydrateCollectionApiFromUrl();
                        this.sync(action, data || this.meta.get('api'), options)
                            .then(resolve)
                            .catch((error) => __awaiter$2(this, void 0, void 0, function* () {
                            console.error('FETCH:', error);
                            if (!this.toast) {
                                reject(error);
                                return;
                            }
                            const errorMessage = this.errorMessage(error);
                            const formatMessage = errorMessage ? `: ${errorMessage}` : '.';
                            Toastify({
                                text: `Unable to Fetch ${this.target}${formatMessage}`,
                                duration: 12000,
                                close: true,
                                stopOnFocus: true,
                                style: {
                                    background: '#E14D45',
                                }
                            }).showToast();
                            reject(error);
                            return;
                        }));
                    }));
                }
                hydrateCollectionApiFromUrl(force = false) {
                    if ((!force && this.collectionApiHydratedFromUrl) || typeof window === 'undefined') {
                        return;
                    }
                    const url = new URL(window.location.href);
                    const urlApi = this.getUrlSearchObject(url);
                    let filterApplied = false;
                    let rawPage = null;
                    if (typeof this.target === 'string' && this.target.length) {
                        rawPage = this.getUrlSearchValue(url, [`page[${this.target}]`, `p[${this.target}]`]);
                    }
                    if (!rawPage) {
                        rawPage = this.getUrlSearchValue(url, ['p', 'page']);
                    }
                    const page = this.normalizePositiveWholeNumber(rawPage);
                    if (!isUndefined(page)) {
                        this.meta.set('api.p', page);
                        this.meta.set('pagination.pageCurrent', page);
                        this.paginate = true;
                    }
                    const query = this.getUrlSearchValue(url, ['q', 'query', 'keyword', 'search']);
                    if (!isUndefined(query) && query !== null && query !== '') {
                        this.meta.set('api.q', query);
                        this.filtering = true;
                        filterApplied = true;
                    }
                    const tags = this.getUrlSearchArray(url, ['tags', 'tag']);
                    if (!isEmpty(tags)) {
                        this.meta.set('api.tags', map(tags, this.parseNumberLikeValue));
                        filterApplied = true;
                    }
                    const contentTypes = this.getUrlSearchArray(url, ['contentType', 'contentTypes']);
                    if (!isEmpty(contentTypes)) {
                        this.meta.set('api.contentType', map(contentTypes, this.parseNumberLikeValue));
                        filterApplied = true;
                    }
                    forEach([
                        'authorId',
                        'status',
                        'filterPublished',
                        'filterTimeField',
                        'filterTimeMin',
                        'filterTimeMax',
                        'filterTimeRange',
                        'sort',
                        'sortOrder',
                        'filter'
                    ], (apiKey) => {
                        if (!has(urlApi, apiKey)) {
                            return;
                        }
                        const value = get(urlApi, apiKey);
                        if (isUndefined(value) || value === null || value === '') {
                            return;
                        }
                        this.meta.set(`api.${apiKey}`, this.parseUrlValue(value));
                        filterApplied = true;
                    });
                    if (filterApplied) {
                        this.meta.set('filterApplied', true);
                    }
                    this.collectionApiHydratedFromUrl = true;
                }
                getUrlSearchValue(url, keys) {
                    for (const key of keys) {
                        const value = url.searchParams.get(key);
                        if (!isUndefined(value) && value !== null && value !== '') {
                            return this.parseUrlValue(value);
                        }
                    }
                    return undefined;
                }
                getUrlSearchArray(url, keys) {
                    const values = [];
                    url.searchParams.forEach((value, key) => {
                        if (!find(keys, (name) => key === name || key === `${name}[]` || new RegExp(`^${name}\\[\\d+\\]$`).test(key))) {
                            return;
                        }
                        const parsedValue = this.parseUrlValue(value);
                        if (isArray(parsedValue)) {
                            values.push(...parsedValue);
                            return;
                        }
                        if (typeof parsedValue === 'string' && parsedValue.indexOf(',') !== -1) {
                            values.push(...parsedValue.split(',').map((item) => item.trim()).filter((item) => item !== ''));
                            return;
                        }
                        values.push(parsedValue);
                    });
                    return values;
                }
                getUrlSearchObject(url) {
                    const output = {};
                    url.searchParams.forEach((value, key) => {
                        this.assignUrlSearchValue(output, this.getUrlSearchParts(key), value);
                    });
                    return output;
                }
                getUrlSearchParts(key) {
                    const parts = [];
                    const pattern = /([^\[\]]+)|\[(.*?)\]/g;
                    let match;
                    while ((match = pattern.exec(key)) !== null) {
                        parts.push(match[1] || match[2] || '');
                    }
                    return parts;
                }
                assignUrlSearchValue(output, parts, rawValue) {
                    if (!parts.length) {
                        return;
                    }
                    let cursor = output;
                    forEach(parts, (part, index) => {
                        const last = index === parts.length - 1;
                        const key = part === '' ? this.nextArrayKey(cursor) : part;
                        if (last) {
                            if (isUndefined(cursor[key])) {
                                cursor[key] = rawValue;
                            }
                            else if (isArray(cursor[key])) {
                                cursor[key].push(rawValue);
                            }
                            else {
                                cursor[key] = [cursor[key], rawValue];
                            }
                            return;
                        }
                        if (!isObject(cursor[key])) {
                            cursor[key] = parts[index + 1] === '' || /^\d+$/.test(parts[index + 1]) ? [] : {};
                        }
                        cursor = cursor[key];
                    });
                }
                nextArrayKey(value) {
                    return isArray(value) ? value.length : Object.keys(value).length;
                }
                parseUrlValue(value) {
                    if (isArray(value)) {
                        return map(value, (item) => this.parseUrlValue(item));
                    }
                    if (isObject(value)) {
                        return reduce(value, (result, item, key) => {
                            result[key] = this.parseUrlValue(item);
                            return result;
                        }, {});
                    }
                    if (typeof value !== 'string') {
                        return value;
                    }
                    if (isJSON(value)) {
                        return JSON.parse(value);
                    }
                    return this.parseNumberLikeValue(value);
                }
                parseNumberLikeValue(value) {
                    if (typeof value !== 'string') {
                        return value;
                    }
                    if (/^-?\d+(\.\d+)?$/.test(value)) {
                        return Number(value);
                    }
                    return value;
                }
                filter(query) {
                    this.filtering = !isEmpty(query);
                    this.meta.set('api.q', !isUndefined(query) ? query : '');
                    this.meta.set('api.p', 1);
                    return this.fetch();
                }
                throttleFilter(query) {
                    this.meta.set('api.q', !isUndefined(query) ? query : '');
                    return new Promise((resolve, reject) => {
                        const request = this.throttle();
                        if (cookie('env')) {
                            console.log('request:', request);
                        }
                        request.then((models) => {
                            if (cookie('env')) ;
                            resolve(models);
                        }).catch(reject);
                    });
                }
                page(page) {
                    const validPage = this.normalizePositiveWholeNumber(page);
                    this.paginate = !isUndefined(validPage);
                    if (isUndefined(validPage)) {
                        this.clearApiPage();
                        return;
                    }
                    this.meta.set('api.p', validPage);
                    this.fetch().then();
                    this.clearApiPage();
                }
                clearApiPage() {
                    const collectionApi = this.meta.get('api');
                    if (isObject(collectionApi) && Object.prototype.hasOwnProperty.call(collectionApi, 'p')) {
                        delete collectionApi.p;
                    }
                }
                normalizePositiveWholeNumber(value) {
                    const page = Number(value);
                    return Number.isFinite(page) && Number.isInteger(page) && page >= 1 ? page : undefined;
                }
                toJSON() {
                    return !this.direct ? this.models.map((model) => model.toJSON()) : this.models;
                }
                add(target, options) {
                    if (!isObject(target)) {
                        console.error('collection.add: target object not set!');
                        return;
                    }
                    if (!options || typeof options !== 'object') {
                        options = {};
                    }
                    if (target instanceof Model) {
                        target.collection = this;
                    }
                    else {
                        options.collection = this;
                        target = new Model(options, target);
                        target.initialize();
                        if (options.autoSave || options.watch) {
                            if (target.isNew()) {
                                target.save();
                            }
                            else if (!target.completed) {
                                target.fetch();
                            }
                        }
                    }
                    if (options.save) {
                        target.save();
                    }
                    if (options.prepend) {
                        this.models.unshift(target);
                    }
                    else {
                        this.models.push(target);
                    }
                    if (options.trigger) {
                        this.trigger('add', target);
                    }
                    this.throttleTrigger('change');
                    return target;
                }
                remove(target) {
                    if (!this.direct) {
                        this.models.splice(this.models.indexOf(target), 1);
                        this.throttleTrigger('change');
                    }
                    return this;
                }
                find(predicate) {
                    return find(this.models, isFunction(predicate) ? predicate : (model) => model.get('id') === predicate);
                }
                map(predicate) {
                    return map(this.models, model => model instanceof Model ? model.get(predicate) : null);
                }
                pluck(attribute) {
                    return map(this.models, model => model instanceof Model ? model.pluck(attribute) : null);
                }
                exists(attribute) {
                    return !!reduce(this.pluck(attribute) || [], (memo, data) => memo || !isUndefined(data));
                }
                errorMessage(error) {
                    if (error instanceof ErrorBase) {
                        console.error(`[${error.code}] ${error.message}`, error);
                        return error.code !== 'Internal' ? error.message : null;
                    }
                    const digest = (error.responseText && isJSON(error.responseText)) ? JSON.parse(error.responseText) : null;
                    if (!digest) {
                        return null;
                    }
                    const message = get(digest, 'meta.status[0].message') || get(digest, 'error.exception[0].message') || null;
                    if (!message) {
                        return null;
                    }
                    if (!cookie('env') && has(digest, 'error.exception[0].message')) {
                        console.error('[xhr] server:', message);
                        return null;
                    }
                    return message;
                }
            } exports('Collection', Collection);
            Stratus.Services.Collection = [
                '$provide',
                ($provide) => {
                    $provide.factory('Collection', [() => Collection]);
                }
            ];
            Stratus.Data.Collection = Collection;

            var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
                function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
                return new (P || (P = Promise))(function (resolve, reject) {
                    function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
                    function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
                    function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
                    step((generator = generator.apply(thisArg, _arguments || [])).next());
                });
            };
            Stratus.Modules.ngSanitize = true;
            Stratus.Controllers.Generic = [
                '$scope',
                '$element',
                '$log',
                '$sce',
                '$parse',
                '$window',
                'Registry',
                ($scope, $element, $log, $sce, $parse, $window, R) => __awaiter$1(void 0, void 0, void 0, function* () {
                    $scope.uid = safeUniqueId('controller_generic_');
                    Stratus.Instances[$scope.uid] = $scope;
                    yield R.fetch($element, $scope);
                    $scope.ctrlParent = $scope.$parent;
                    $scope.Stratus = Stratus;
                    $scope._ = _;
                    $scope.cookie = cookie;
                    $scope.$window = $window;
                    $scope.getUrlParams = getUrlParams;
                    $scope.setUrlParams = setUrlParams;
                    $scope.$log = $log;
                    $scope.Math = Math;
                    $scope.isArray = isArray;
                    $scope.isDate = isDate;
                    $scope.isDefined = (value) => !isUndefined(value);
                    $scope.isElement = isElement;
                    $scope.isFunction = isFunction;
                    $scope.isNumber = isNumber;
                    $scope.isObject = isObject;
                    $scope.isString = isString;
                    $scope.isUndefined = isUndefined;
                    $scope.getHTML = $sce.trustAsHtml;
                    $scope.getURL = $sce.trustAsResourceUrl;
                    $scope.getAnchor = () => {
                        const url = window.location.href;
                        if (!url || !url.length) {
                            return false;
                        }
                        const anchor = url.indexOf('#');
                        if (anchor < 0) {
                            return false;
                        }
                        if ((anchor + 1) >= url.length) {
                            return false;
                        }
                        return url.substring(anchor + 1, url.length);
                    };
                    $scope.scrollToAnchor = (anchor, inUrl, delay) => {
                        if (!anchor || isEmpty(anchor)) {
                            $log.warn('anchor id not set!');
                            return false;
                        }
                        if (isUndefined(inUrl)) {
                            inUrl = false;
                        }
                        if (inUrl && anchor !== $scope.getAnchor()) {
                            return false;
                        }
                        const el = $window.document.getElementById(anchor);
                        if (!el) {
                            $log.warn(`element not found: ${anchor}`);
                            return false;
                        }
                        if (!delay) {
                            el.scrollIntoView({ behavior: 'smooth' });
                            return true;
                        }
                        return setTimeout(() => {
                            $scope.scrollToAnchor(anchor, inUrl, --delay);
                        }, 1);
                    };
                    if ($scope.data && isFunction($scope.data.on)) {
                        $scope.data.on('change', () => $scope.$applyAsync());
                    }
                    if (!$scope.collection || !($scope.collection instanceof Collection)) {
                        return;
                    }
                    const selected = {
                        id: $element.attr('data-selected'),
                        raw: $element.attr('data-raw')
                    };
                    if (!selected.id || !isString(selected.id)) {
                        return;
                    }
                    if (isJSON(selected.id)) {
                        selected.id = JSON.parse(selected.id);
                        $scope.$watch('collection.models', (models) => {
                            if ($scope.selected || $scope.selectedInit) {
                                return;
                            }
                            forEach(models, (model) => {
                                if (selected.id !== model.getIdentifier()) {
                                    return;
                                }
                                $scope.selected = selected.raw ? model.data : model;
                                $scope.selectedInit = true;
                            });
                        });
                    }
                    else {
                        selected.model = $parse(selected.id);
                        selected.value = selected.model($scope.$parent);
                        if (!isArray(selected.value)) {
                            return;
                        }
                        selected.value = selected.value.filter((n) => n);
                        if (selected.value.length) {
                            return;
                        }
                        $scope.selected = head(selected.value);
                    }
                })
            ];

            var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
                function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
                return new (P || (P = Promise))(function (resolve, reject) {
                    function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
                    function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
                    function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
                    step((generator = generator.apply(thisArg, _arguments || [])).next());
                });
            };
            let injector = getInjector();
            let $interpolate;
            const serviceVerify = () => __awaiter(void 0, void 0, void 0, function* () {
                return new Promise((resolve, _reject) => __awaiter(void 0, void 0, void 0, function* () {
                    if ($interpolate) {
                        resolve(true);
                        return;
                    }
                    if (!injector) {
                        injector = getInjector();
                    }
                    if (injector) {
                        $interpolate = injector.get('$interpolate');
                    }
                    if ($interpolate) {
                        resolve(true);
                        return;
                    }
                    setTimeout(() => {
                        if (cookie('env')) {
                            console.log('wait for $interpolate service:', $interpolate);
                        }
                        serviceVerify().then(resolve);
                    }, 250);
                }));
            });
            class Registry {
                constructor() {
                }
                fetch($element, $scope) {
                    return new Promise((resolve, _reject) => __awaiter(this, void 0, void 0, function* () {
                        if (typeof $element === 'string') {
                            $element = {
                                target: $element
                            };
                        }
                        const inputs = {};
                        const baseInputs = [
                            'id',
                            'api',
                            'temp',
                            'decouple',
                            'fetch'
                        ];
                        forEach(union(ModelOptionKeys, CollectionOptionKeys, baseInputs), (option) => set(inputs, option, 'data-' + kebabCase(option)));
                        const options = forEach(inputs, (value, key, list) => {
                            list[key] = $element.attr ? $element.attr(value) : $element[key];
                            if (!isJSON(list[key])) {
                                return;
                            }
                            list[key] = JSON.parse(list[key]);
                        });
                        options.api = sanitize(options.api);
                        options.temp = sanitize(options.temp);
                        let completed = 0;
                        const verify = () => {
                            if (!isNumber(completed) || completed !== size(options)) {
                                return;
                            }
                            resolve(this.build(options, $scope));
                        };
                        if (!$interpolate) {
                            yield serviceVerify();
                        }
                        forEach(options, (element, key) => __awaiter(this, void 0, void 0, function* () {
                            if (!element || typeof element !== 'string' || !$scope || !$scope.$parent) {
                                completed++;
                                verify();
                                return;
                            }
                            const interpreter = $interpolate(element, false, null, true);
                            const initial = interpreter($scope.$parent);
                            if (typeof initial !== 'undefined') {
                                options[key] = initial;
                                completed++;
                                verify();
                                return;
                            }
                            if (cookie('env')) {
                                console.log(`poll (${key}): start`);
                            }
                            let value;
                            try {
                                value = yield poll(() => interpreter($scope.$parent), 1500, 250);
                            }
                            catch (err) {
                                if (cookie('env') ||
                                    err.name !== 'Timeout') {
                                    console.error(err);
                                }
                            }
                            if (cookie('env')) {
                                console.log(`poll (${key}):`, value);
                            }
                            options[key] = value;
                            completed++;
                            verify();
                        }));
                    }));
                }
                build(options, $scope) {
                    let data;
                    if (options.payload || options.convoy) {
                        options.fetch = false;
                    }
                    if (options.target) {
                        options.target = ucfirst(options.target);
                        if (options.manifest || options.id) {
                            if (!Stratus.Catalog[options.target]) {
                                Stratus.Catalog[options.target] = {};
                            }
                            const id = options.id || 'manifest';
                            if (options.decouple || !Stratus.Catalog[options.target][id]) {
                                const modelOptions = {
                                    stagger: true
                                };
                                forEach(ModelOptionKeys, (element) => {
                                    const optionValue = get(options, element);
                                    if (isUndefined(optionValue)) {
                                        return;
                                    }
                                    set(modelOptions, element, optionValue);
                                });
                                data = new Model(modelOptions, {
                                    id: options.id
                                });
                                if (!options.decouple) {
                                    Stratus.Catalog[options.target][id] = data;
                                }
                            }
                            else if (Stratus.Catalog[options.target][id]) {
                                data = Stratus.Catalog[options.target][id];
                            }
                        }
                        else {
                            const registry = !options.direct ? 'Catalog' : 'Compendium';
                            if (!Stratus[registry][options.target]) {
                                Stratus[registry][options.target] = {};
                            }
                            if (options.decouple ||
                                !Stratus[registry][options.target].collection) {
                                const collectionOptions = {};
                                forEach(CollectionOptionKeys, (element) => {
                                    const optionValue = get(options, element);
                                    if (isUndefined(optionValue)) {
                                        return;
                                    }
                                    set(collectionOptions, element, optionValue);
                                });
                                data = new Collection(collectionOptions);
                                if (!options.decouple) {
                                    Stratus[registry][options.target].collection = data;
                                }
                            }
                            else if (Stratus[registry][options.target].collection) {
                                data = Stratus[registry][options.target].collection;
                            }
                        }
                        if (options.api) {
                            data.meta.set('api', isJSON(options.api) ? JSON.parse(options.api) : options.api);
                        }
                        if (options.temp && isObject(options.temp) && !data.completed) {
                            forEach(flatten(options.temp), (v, k) => {
                                console.log('setting temp:', `api.${k}`, v);
                                data.meta.temp(`api.${k}`, v);
                            });
                        }
                        if (data instanceof Model && data.stagger && typeof data.initialize === 'function') {
                            data.initialize();
                        }
                    }
                    if (typeof data === 'object' && data !== null) {
                        if (typeof $scope !== 'undefined') {
                            $scope.data = data;
                            if (data instanceof Model) {
                                $scope.model = data;
                            }
                            else if (data instanceof Collection) {
                                $scope.collection = data;
                            }
                            if (data instanceof EventManager && typeof $scope.$applyAsync === 'function') {
                                data.on('change', () => {
                                    $scope.$applyAsync();
                                });
                                data.on('error', () => {
                                    $scope.$applyAsync();
                                });
                                if (data.completed) {
                                    $scope.$applyAsync();
                                }
                            }
                        }
                        if (!data.pending
                            && !data.completed
                            && (isUndefined(options.fetch) || options.fetch)) {
                            data.fetch().then();
                        }
                    }
                    return data;
                }
            } exports('Registry', Registry);
            Stratus.Services.Registry = [
                '$provide',
                ($provide) => {
                    $provide.factory('Registry', [
                        () => {
                            return new Registry();
                        }
                    ]);
                }
            ];
            Stratus.Data.Registry = Registry;

            const min$1 = !cookie('env') ? '.min' : '';
            const name$1 = 'base';
            const localPath$1 = '@stratusjs/angularjs/src/components';
            Stratus.Components.Base = {
                transclude: {
                    model: '?stratusBaseModel'
                },
                bindings: {
                    elementId: '@',
                    ngModel: '=',
                    property: '@',
                    target: '@',
                    id: '@',
                    manifest: '@',
                    decouple: '@',
                    direct: '@',
                    api: '@',
                    urlRoot: '@',
                    limit: '@',
                    options: '<'
                },
                controller($scope, $attrs) {
                    $scope.uid = safeUniqueId(name$1);
                    Stratus.Instances[$scope.uid] = $scope;
                    $scope.elementId = $attrs.elementId || $scope.uid;
                    Stratus.Internals.CssLoader(`${Stratus.BaseUrl + Stratus.BundlePath + localPath$1}/${name$1}${min$1}.css`).then();
                    $scope.initialized = false;
                    $scope.property = $attrs.property || null;
                    $scope.data = null;
                    $scope.model = null;
                    $scope.collection = null;
                    if ($attrs.target) {
                        $scope.registry = $scope.registry || new Registry();
                        $scope.registry.fetch($attrs, $scope).then();
                    }
                    $scope.$watch('$ctrl.ngModel', (data) => {
                        if (data instanceof Model && data !== $scope.model) {
                            $scope.model = data;
                        }
                        else if (data instanceof Collection && data !== $scope.collection) {
                            $scope.collection = data;
                        }
                    });
                    $scope.initialize = () => {
                        if ($scope.initialized) {
                            return;
                        }
                        if ($scope.model) {
                            $scope.initialized = true;
                            $scope.model.on('change', () => {
                                console.log('model changed:', $scope.model.patch);
                            });
                        }
                        if ($scope.collection) {
                            $scope.initialized = true;
                            console.log('collection available');
                        }
                    };
                    $scope.$watch('$scope.model.completed', (newVal, oldVal) => {
                        if (!newVal || isEqual(newVal, oldVal)) {
                            return;
                        }
                        $scope.initialize();
                    });
                    $scope.$watch('$scope.collection.completed', (newVal, oldVal) => {
                        if (!newVal || isEqual(newVal, oldVal)) {
                            return;
                        }
                        $scope.initialize();
                    });
                    if (cookie('env')) {
                        console.log(name$1, 'component:', $scope, $attrs);
                    }
                },
                templateUrl: `${Stratus.BaseUrl}${Stratus.DeploymentPath}${localPath$1}/${name$1}${min$1}.html`
            };

            const min = !cookie('env') ? '.min' : '';
            const name = 'base';
            const localPath = '@stratusjs/angularjs/src/directives';
            Stratus.Directives.Base = function () {
                return {
                    restrict: 'A',
                    scope: {
                        ngModel: '='
                    },
                    link: ($scope, $element, $attrs) => {
                        const $ctrl = this;
                        $scope.uid = safeUniqueId(name);
                        Stratus.Instances[$scope.uid] = $scope;
                        $scope.elementId = $element.elementId || $scope.uid;
                        Stratus.Internals.CssLoader(Stratus.BaseUrl + Stratus.BundlePath + localPath + name + min + '.css').then();
                        $scope.initialized = false;
                        console.log('directive:', $ctrl, $scope, $element, $attrs);
                    },
                    templateUrl: Stratus.BaseUrl + Stratus.BundlePath + localPath + name + min + '.html'
                };
            };

        })
    };
}));
//# sourceMappingURL=angularjs.bundle.js.map