UNPKG

@stratusjs/angularjs

Version:

This is the AngularJS package for StratusJS.

996 lines (993 loc) 82.6 kB
System.register(['angular', '@stratusjs/runtime/stratus', 'lodash', 'angular-material', '@stratusjs/core/errors/errorBase', '@stratusjs/core/misc', '@stratusjs/core/datastore/modelBase', '@stratusjs/core/environment', '@stratusjs/core/datastore/xhr', '@stratusjs/core/events/eventManager', 'toastify-js', '@stratusjs/core/conversion', 'angular-sanitize'], (function (exports) { 'use strict'; var element, Stratus, throttle, extend, isObject, isArray, forEach, get, isUndefined, set, clone, isEmpty, find, isFunction, map, reduce, has, cloneDeep, once, isNumber, head, isString, filter, isEqual, union, kebabCase, size, _, isDate, isElement, ErrorBase, ucfirst, isJSON, setUrlParams, getAnchorParams, getUrlParams, serializeUrlParams, patch, strcmp, poll, flatten, safeUniqueId, ModelBase, cookie, XHR, EventManager, Toastify, sanitize; return { setters: [function (module) { element = module.element; }, function (module) { Stratus = module.Stratus; }, function (module) { throttle = module.throttle; extend = module.extend; isObject = module.isObject; isArray = module.isArray; forEach = module.forEach; get = module.get; isUndefined = module.isUndefined; set = module.set; clone = module.clone; isEmpty = module.isEmpty; find = module.find; isFunction = module.isFunction; map = module.map; reduce = module.reduce; has = module.has; cloneDeep = module.cloneDeep; once = module.once; isNumber = module.isNumber; head = module.head; isString = module.isString; filter = module.filter; isEqual = module.isEqual; union = module.union; kebabCase = module.kebabCase; size = module.size; _ = module.default; isDate = module.isDate; isElement = module.isElement; }, null, function (module) { ErrorBase = module.ErrorBase; }, function (module) { ucfirst = module.ucfirst; isJSON = module.isJSON; setUrlParams = module.setUrlParams; getAnchorParams = module.getAnchorParams; getUrlParams = module.getUrlParams; serializeUrlParams = module.serializeUrlParams; patch = module.patch; strcmp = module.strcmp; poll = module.poll; flatten = module.flatten; safeUniqueId = module.safeUniqueId; }, function (module) { ModelBase = module.ModelBase; }, function (module) { cookie = module.cookie; }, function (module) { XHR = module.XHR; }, function (module) { EventManager = module.EventManager; }, function (module) { Toastify = module.default; }, function (module) { sanitize = module.sanitize; }, null], 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()); }); }; 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.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 + ']'; } str.push(this.serialize(value, key)); } 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$3(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 (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]; } } this.xhr = new XHR(request); const handler = (response) => { 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] = this.xhr.getAllResponseHeaders(); } else { responseHeaders = this.cacheHeaders[queryHash]; } } this.header.set(responseHeaders || this.xhr.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; } this.xhr.send() .then(handler) .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$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* () { 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; })); })); } 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) { this.paginate = !isEmpty(page); this.meta.set('api.p', page); this.fetch().then(); delete this.meta.get('api').p; } 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$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()); }); }; let injector$1 = getInjector(); let $rootScope; const serviceVerify$1 = () => __awaiter$2(void 0, void 0, void 0, function* () { return new Promise((resolve, _reject) => __awaiter$2(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$2(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; } watcher() { return __awaiter$2(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(); } 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$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 (['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]; }); } this.xhr = new 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) { 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() || {}); 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 (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, 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(); 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) => { 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$2(this, void 0, void 0, function* () { this.sync(action, data || this.meta.get('api'), options) .then(resolve) .catch((error) => __awaiter$2(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.toPatch()))) { 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$2(this, void 0, void 0, function* () { this.sync(this.getIdentifier() ? 'PUT' : 'POST', this.toJSON({ patch: options.patch })) .then(resolve) .catch((error) => __awaiter$2(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); const formatMessage = errorMessage ? `: ${errorMessage}` : '.'; Toastify({ 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() || 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 (!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 (!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]