UNPKG

@stratusjs/angularjs

Version:

This is the AngularJS package for StratusJS.

789 lines (787 loc) 39.2 kB
System.register(["lodash", "@stratusjs/runtime/stratus", "@stratusjs/core/errors/errorBase", "@stratusjs/core/misc", "@stratusjs/core/datastore/modelBase", "@stratusjs/core/environment", "@stratusjs/core/datastore/xhr", "../injector", "./collection", "toastify-js"], function (exports_1, context_1) { "use strict"; var lodash_1, stratus_1, errorBase_1, misc_1, modelBase_1, environment_1, xhr_1, injector_1, collection_1, toastify_js_1, injector, $rootScope, serviceVerify, ModelOptionKeys, Model; var __moduleName = context_1 && context_1.id; return { setters: [ function (lodash_1_1) { lodash_1 = lodash_1_1; }, function (stratus_1_1) { stratus_1 = stratus_1_1; }, function (errorBase_1_1) { errorBase_1 = errorBase_1_1; }, function (misc_1_1) { misc_1 = misc_1_1; }, function (modelBase_1_1) { modelBase_1 = modelBase_1_1; }, function (environment_1_1) { environment_1 = environment_1_1; }, function (xhr_1_1) { xhr_1 = xhr_1_1; }, function (injector_1_1) { injector_1 = injector_1_1; }, function (collection_1_1) { collection_1 = collection_1_1; }, function (toastify_js_1_1) { toastify_js_1 = toastify_js_1_1; } ], execute: function () { injector = injector_1.getInjector(); serviceVerify = async () => { return new Promise(async (resolve, _reject) => { if ($rootScope) { resolve(true); return; } if (!injector) { injector = injector_1.getInjector(); } if (injector) { $rootScope = injector.get('$rootScope'); } if ($rootScope) { resolve(true); return; } setTimeout(() => { if (environment_1.cookie('env')) { console.log('wait for $rootScope service:', { $rootScope }); } serviceVerify().then(resolve); }, 250); }); }; exports_1("ModelOptionKeys", ModelOptionKeys = ["autoSave", "autoSaveInterval", "autoSaveHalt", "collection", "completed", "manifest", "serviceId", "stagger", "target", "targetSuffix", "toast", "type", "urlRoot", "urlSync", "watch", "withCredentials", "payload", "convoy", "headers", "ignoreKeys", "received"]); Model = class Model extends modelBase_1.ModelBase { name = 'Model'; target = null; type = null; manifest = false; stagger = false; toast = true; identifier = null; urlRoot = '/Api'; targetSuffix = null; serviceId = null; header = new modelBase_1.ModelBase(); meta = new modelBase_1.ModelBase(); route = new modelBase_1.ModelBase(); collection = null; xhr; withCredentials = false; headers = {}; pending = false; error = false; completed = false; saving = false; changedExternal = false; watch = true; status = null; autoSave = false; autoSaveInterval = 4000; autoSaveHalt = true; autoSaveTimeout = null; urlSync = false; bracket = { match: /\[[\d+]]/, search: /\[([\d+])]/g, attr: /(^[^[]+)/ }; throttle = lodash_1.throttle(this.fetch, 1000); initialize = null; constructor(options = {}, attributes) { super(attributes); options = typeof options !== 'object' ? {} : options; options.received = options.received || false; lodash_1.extend(this, this.sanitizeOptions(options)); if (options.convoy) { const convoy = misc_1.isJSON(options.convoy) ? JSON.parse(options.convoy) : options.convoy; if (lodash_1.isObject(convoy)) { this.meta.set(convoy.meta || {}); const payload = convoy.payload; if (lodash_1.isObject(payload)) { lodash_1.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 = misc_1.isJSON(options.payload) ? JSON.parse(options.payload) : options.payload; if (lodash_1.isObject(payload)) { lodash_1.extend(this.data, payload); this.completed = true; options.received = true; } else { console.error('malformed payload:', payload); } } this.header = new modelBase_1.ModelBase(); this.meta = new modelBase_1.ModelBase(); this.route = new modelBase_1.ModelBase(); if (!lodash_1.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 ? lodash_1.cloneDeep(this.data) : {}; this.sent = {}; this.ignoreKeys = options.ignoreKeys || ['$$hashKey']; if (this.target) { this.urlRoot += '/' + misc_1.ucfirst(this.target); } const that = this; this.initialize = lodash_1.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(async (error) => { console.error('MANIFEST:', error); if (!that.toast) { return; } const errorMessage = that.errorMessage(error); const formatMessage = errorMessage ? `: ${errorMessage}` : '.'; toastify_js_1.default({ 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 = {}; lodash_1.forEach(ModelOptionKeys, (key) => { const data = lodash_1.get(options, key); if (lodash_1.isUndefined(data)) { return; } lodash_1.set(sanitizedOptions, key, data); }); return sanitizedOptions; } async watcher() { if (this.watching) { return true; } this.watching = true; if (!$rootScope) { await serviceVerify(); } $rootScope.$watch(() => this.data, (_newData, _priorData) => this.handleChanges(), true); } handleChanges(changeSet) { const isUserChangeSet = lodash_1.isUndefined(changeSet); if (isUserChangeSet) { changeSet = super.handleChanges(); } if (!changeSet || lodash_1.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 (environment_1.cookie('env')) { console.info('Attempting URL Sync for non-User ChangeSet:', changeSet); } } if (this.urlSync) { if (lodash_1.get(changeSet, 'id')) { const newUrl = misc_1.setUrlParams({ id: lodash_1.get(changeSet, 'id') || this.getIdentifier() }); if (newUrl !== document.location.href) { window.location.replace(newUrl); } } const version = misc_1.getAnchorParams('version'); const versionId = !lodash_1.isEmpty(version) ? parseInt(version, 10) : 0; if (versionId && versionId !== lodash_1.get(changeSet, 'version.id')) { if (environment_1.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() + (lodash_1.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 (misc_1.getUrlParams('version')) { url += url.includes('?') ? '&' : '?'; url += 'options[version]=' + misc_1.getUrlParams('version'); } return url; } serialize(obj, chain) { return misc_1.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 = lodash_1.cloneDeep(this.data); return new Promise(async (resolve, reject) => { action = action || 'GET'; options = options || {}; const request = { method: action, url: this.url(), headers: lodash_1.clone(this.headers), withCredentials: this.withCredentials, }; if (!lodash_1.isUndefined(data)) { if (['GET', 'DELETE'].includes(action)) { if (lodash_1.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 (environment_1.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]; }); } this.xhr = new xhr_1.XHR(request); this.xhr.send().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_1.Collection) { this.collection.throttleTrigger('change'); } }; if (!lodash_1.isObject(response) && !lodash_1.isArray(response)) { const error = new errorBase_1.ErrorBase({ payload: response, message: `Invalid Payload: ${request.method} ${request.url}` }, {}); propagateError(); reject(error); return; } this.header.set(this.xhr.getAllResponseHeaders() || {}); this.meta.set(response.meta || {}); this.route.set(response.route || {}); const payload = response.payload || response; this.error = false; if ((this.meta.has('success') && !this.meta.get('success'))) { this.error = true; } else if (lodash_1.isArray(payload) && payload.length) { this.recv = lodash_1.head(payload); } else if (lodash_1.isObject(payload) && !lodash_1.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_1.ErrorBase({ payload, message: `Invalid Payload: ${request.method} ${request.url}` }, {}); propagateError(); reject(error); return; } const incomingChangeSet = this.completed ? lodash_1.cloneDeep(misc_1.patch(this.recv, this.sent)) : {}; if (!lodash_1.isEmpty(incomingChangeSet)) { if (environment_1.cookie('env')) { console.log('Incoming ChangeSet detected:', environment_1.cookie('debug_change_set') ? JSON.stringify(incomingChangeSet) : incomingChangeSet); } this.handleChanges(incomingChangeSet); } const intermediateData = lodash_1.cloneDeep(this.recv); const intermediateChangeSet = lodash_1.cloneDeep(misc_1.patch(this.data, this.sent)); if (!lodash_1.isEmpty(intermediateChangeSet)) { if (environment_1.cookie('env')) { console.log('Intermediate ChangeSet detected:', environment_1.cookie('debug_change_set') ? JSON.stringify(intermediateChangeSet) : intermediateChangeSet); } lodash_1.forEach(intermediateChangeSet, (element, key) => { lodash_1.set(intermediateData, key, element); }); } this.data = lodash_1.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(); this.trigger('success', this); this.trigger('change', this); this.trigger('complete', this); if (this.collection instanceof collection_1.Collection) { this.collection.throttleTrigger('change'); } resolve(this.data); return; }) .catch((error) => { 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(async (resolve, reject) => { this.sync(action, data || this.meta.get('api'), options) .then(resolve) .catch(async (error) => { 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_js_1.default({ 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 (!lodash_1.isObject(options)) { console.warn('invalid options supplied:', options); options = {}; } if (lodash_1.has(options, 'force') && options.force) { options.patch = lodash_1.has(options, 'patch') ? options.patch : false; return this.doSave(options); } if (!this.isNew() && (this.pending || !this.completed || lodash_1.isEmpty(this.toPatch()))) { console.warn(`Blocked attempt to save ${lodash_1.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 (!lodash_1.isObject(options)) { console.warn('invalid options supplied:', options); options = {}; } options.patch = lodash_1.has(options, 'patch') ? options.patch : true; return new Promise(async (resolve, reject) => { this.sync(this.getIdentifier() ? 'PUT' : 'POST', this.toJSON({ patch: options.patch })) .then(resolve) .catch(async (error) => { this.error = true; this.resetXHRFlags(); console.error('SAVE:', error); if (!this.toast) { reject(error); return; } const errorMessage = this.errorMessage(error); const formatMessage = errorMessage ? `: ${errorMessage}` : '.'; toastify_js_1.default({ text: `Unable to Save ${this.target}${formatMessage}`, 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() || lodash_1.isEmpty(this.toPatch())) { 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 (!lodash_1.isObject(options)) { options = {}; } options.patch = (options.patch && !this.isNew()); let data = super.toJSON(options); const metaData = this.meta.get('api'); if (metaData) { data = { meta: metaData, payload: data }; } return data; } buildPath(path) { const acc = []; if (!lodash_1.isString(path)) { return acc; } let cur; let search; lodash_1.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 lodash_1.get(this.data, attr); } find(attr, key, value) { if (typeof attr === 'string') { attr = this.get(attr); } return !lodash_1.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') { lodash_1.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; } lodash_1.set(this.data, attr, value); this.throttleTrigger('change', this); this.throttleTrigger(`change:${attr}`, value); } toggle(attribute, item, options) { if (typeof options === 'object' && !lodash_1.isUndefined(options.multiple) && lodash_1.isUndefined(options.strict)) { options.strict = true; } options = lodash_1.extend({ multiple: true }, lodash_1.isObject(options) ? options : {}); const request = attribute.split('[].'); let target = this.get(request.length > 1 ? request[0] : attribute); if (lodash_1.isUndefined(target) || (options.strict && lodash_1.isArray(target) !== options.multiple)) { target = options.multiple ? [] : null; this.set(request.length > 1 ? request[0] : attribute, target); } if (lodash_1.isArray(target)) { if (lodash_1.isUndefined(item)) { this.set(attribute, null); } else if (!this.exists(attribute, item)) { target.push(item); } else { lodash_1.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 || (lodash_1.isString(childId) && lodash_1.isString(itemId) && misc_1.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 (lodash_1.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 || !lodash_1.isArray(attr)) { return undefined; } const list = lodash_1.filter(lodash_1.map(attr, (element) => lodash_1.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 (lodash_1.isArray(attribute)) { return typeof attribute.find((element) => element === item || ((typeof element === 'object' && element.id && element.id === item) || lodash_1.isEqual(element, item))) !== 'undefined'; } return attribute === item || (typeof attribute === 'object' && attribute.id && (lodash_1.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(async (_resolve, reject) => { let deleteData = {}; if (!lodash_1.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(async (error) => { 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_js_1.default({ 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_1.ErrorBase) { console.error(`[${error.code}] ${error.message}`, error); return error.code !== 'Internal' ? error.message : null; } const digest = (error.responseText && misc_1.isJSON(error.responseText)) ? JSON.parse(error.responseText) : null; if (!digest) { return null; } const message = lodash_1.get(digest, 'meta.status[0].message') || lodash_1.get(digest, 'error.exception[0].message') || null; if (!message) { return null; } if (!environment_1.cookie('env') && lodash_1.has(digest, 'error.exception[0].message')) { console.error('[xhr] server:', message); return null; } return message; } }; exports_1("Model", Model); stratus_1.Stratus.Services.Model = [ '$provide', ($provide) => { $provide.factory('Model', [ () => { return Model; } ]); } ]; stratus_1.Stratus.Data.Model = Model; } }; }); //# sourceMappingURL=model.js.map