@stratusjs/angularjs
Version:
This is the AngularJS package for StratusJS.
663 lines (661 loc) • 32.4 kB
JavaScript
System.register(["lodash", "@stratusjs/runtime/stratus", "@stratusjs/core/errors/errorBase", "@stratusjs/core/datastore/modelBase", "@stratusjs/core/events/eventManager", "@stratusjs/core/environment", "@stratusjs/core/misc", "@stratusjs/core/datastore/xhr", "./model", "toastify-js"], function (exports_1, context_1) {
"use strict";
var lodash_1, stratus_1, errorBase_1, modelBase_1, eventManager_1, environment_1, misc_1, xhr_1, model_1, toastify_js_1, CollectionOptionKeys, Collection;
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 (modelBase_1_1) {
modelBase_1 = modelBase_1_1;
},
function (eventManager_1_1) {
eventManager_1 = eventManager_1_1;
},
function (environment_1_1) {
environment_1 = environment_1_1;
},
function (misc_1_1) {
misc_1 = misc_1_1;
},
function (xhr_1_1) {
xhr_1 = xhr_1_1;
},
function (model_1_1) {
model_1 = model_1_1;
},
function (toastify_js_1_1) {
toastify_js_1 = toastify_js_1_1;
}
],
execute: function () {
exports_1("CollectionOptionKeys", CollectionOptionKeys = ["autoSave", "autoSaveInterval", "cache", "direct", "target", "targetSuffix", "urlRoot", "watch", "payload", "convoy", "headers"]);
Collection = class Collection extends eventManager_1.EventManager {
name = 'Collection';
direct = false;
target = null;
targetSuffix = null;
urlRoot = '/Api';
toast = true;
qualifier = '';
serviceId = null;
infinite = false;
threshold = 0.5;
decay = 0;
header = new modelBase_1.ModelBase();
meta = new modelBase_1.ModelBase();
model = model_1.Model;
models = [];
types = [];
xhr;
withCredentials = false;
headers = {};
cacheResponse = {};
cacheHeaders = {};
cache = false;
pending = false;
error = false;
completed = false;
filtering = false;
paginate = false;
collectionApiHydratedFromUrl = false;
watch = false;
autoSave = false;
autoSaveInterval = 2500;
throttle = lodash_1.throttle(this.fetch, 1000);
constructor(options = {}) {
super();
options = (!options || typeof options !== 'object') ? {} : options;
lodash_1.extend(this, options);
if (this.target) {
this.urlRoot += '/' + misc_1.ucfirst(this.target);
}
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 models = convoy.payload;
if (lodash_1.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 = misc_1.isJSON(options.payload) ? JSON.parse(options.payload) : options.payload;
if (lodash_1.isArray(models)) {
this.inject(models);
this.completed = true;
}
else {
console.error('malformed payload:', models);
}
}
}
sanitizeOptions(options) {
const sanitizedOptions = {};
lodash_1.forEach(CollectionOptionKeys, (key) => {
const data = lodash_1.get(options, key);
if (lodash_1.isUndefined(data)) {
return;
}
lodash_1.set(sanitizedOptions, key, data);
});
return sanitizedOptions;
}
serialize(obj, chain) {
const str = [];
obj = obj || {};
lodash_1.forEach(obj, (value, key) => {
if (lodash_1.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 (!lodash_1.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_1.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(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 (lodash_1.isObject(data) && Object.prototype.hasOwnProperty.call(data, 'p')) {
const validPage = this.normalizePositiveWholeNumber(data.p);
if (lodash_1.isUndefined(validPage)) {
delete data.p;
}
else {
data.p = validPage;
}
}
if (action === 'GET') {
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 = 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_1.XHR(request);
this.xhr = xhr;
const handler = (response, responseXhr = xhr) => {
if (!lodash_1.isObject(response) && !lodash_1.isArray(response)) {
const error = new errorBase_1.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] = lodash_1.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 (lodash_1.isArray(payload)) {
this.inject(payload);
}
else if (lodash_1.isObject(payload)) {
lodash_1.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 = !lodash_1.isEmpty(this.meta.get('api.q'));
this.paginate = !lodash_1.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_1.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(async (resolve, reject) => {
this.hydrateCollectionApiFromUrl();
this.sync(action, data || this.meta.get('api'), options)
.then(resolve)
.catch(async (error) => {
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;
});
});
}
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 (!lodash_1.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 (!lodash_1.isUndefined(query) && query !== null && query !== '') {
this.meta.set('api.q', query);
this.filtering = true;
filterApplied = true;
}
const tags = this.getUrlSearchArray(url, ['tags', 'tag']);
if (!lodash_1.isEmpty(tags)) {
this.meta.set('api.tags', lodash_1.map(tags, this.parseNumberLikeValue));
filterApplied = true;
}
const contentTypes = this.getUrlSearchArray(url, ['contentType', 'contentTypes']);
if (!lodash_1.isEmpty(contentTypes)) {
this.meta.set('api.contentType', lodash_1.map(contentTypes, this.parseNumberLikeValue));
filterApplied = true;
}
lodash_1.forEach([
'authorId',
'status',
'filterPublished',
'filterTimeField',
'filterTimeMin',
'filterTimeMax',
'filterTimeRange',
'sort',
'sortOrder',
'filter'
], (apiKey) => {
if (!lodash_1.has(urlApi, apiKey)) {
return;
}
const value = lodash_1.get(urlApi, apiKey);
if (lodash_1.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 (!lodash_1.isUndefined(value) && value !== null && value !== '') {
return this.parseUrlValue(value);
}
}
return undefined;
}
getUrlSearchArray(url, keys) {
const values = [];
url.searchParams.forEach((value, key) => {
if (!lodash_1.find(keys, (name) => key === name || key === `${name}[]` || new RegExp(`^${name}\\[\\d+\\]$`).test(key))) {
return;
}
const parsedValue = this.parseUrlValue(value);
if (lodash_1.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;
lodash_1.forEach(parts, (part, index) => {
const last = index === parts.length - 1;
const key = part === '' ? this.nextArrayKey(cursor) : part;
if (last) {
if (lodash_1.isUndefined(cursor[key])) {
cursor[key] = rawValue;
}
else if (lodash_1.isArray(cursor[key])) {
cursor[key].push(rawValue);
}
else {
cursor[key] = [cursor[key], rawValue];
}
return;
}
if (!lodash_1.isObject(cursor[key])) {
cursor[key] = parts[index + 1] === '' || /^\d+$/.test(parts[index + 1]) ? [] : {};
}
cursor = cursor[key];
});
}
nextArrayKey(value) {
return lodash_1.isArray(value) ? value.length : Object.keys(value).length;
}
parseUrlValue(value) {
if (lodash_1.isArray(value)) {
return lodash_1.map(value, (item) => this.parseUrlValue(item));
}
if (lodash_1.isObject(value)) {
return lodash_1.reduce(value, (result, item, key) => {
result[key] = this.parseUrlValue(item);
return result;
}, {});
}
if (typeof value !== 'string') {
return value;
}
if (misc_1.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 = !lodash_1.isEmpty(query);
this.meta.set('api.q', !lodash_1.isUndefined(query) ? query : '');
this.meta.set('api.p', 1);
return this.fetch();
}
throttleFilter(query) {
this.meta.set('api.q', !lodash_1.isUndefined(query) ? query : '');
return new Promise((resolve, reject) => {
const request = this.throttle();
if (environment_1.cookie('env')) {
console.log('request:', request);
}
request.then((models) => {
if (environment_1.cookie('env')) {
}
resolve(models);
}).catch(reject);
});
}
page(page) {
const validPage = this.normalizePositiveWholeNumber(page);
this.paginate = !lodash_1.isUndefined(validPage);
if (lodash_1.isUndefined(validPage)) {
this.clearApiPage();
return;
}
this.meta.set('api.p', validPage);
this.fetch().then();
this.clearApiPage();
}
clearApiPage() {
const collectionApi = this.meta.get('api');
if (lodash_1.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 (!lodash_1.isObject(target)) {
console.error('collection.add: target object not set!');
return;
}
if (!options || typeof options !== 'object') {
options = {};
}
if (target instanceof model_1.Model) {
target.collection = this;
}
else {
options.collection = this;
target = new model_1.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 lodash_1.find(this.models, lodash_1.isFunction(predicate) ? predicate : (model) => model.get('id') === predicate);
}
map(predicate) {
return lodash_1.map(this.models, model => model instanceof model_1.Model ? model.get(predicate) : null);
}
pluck(attribute) {
return lodash_1.map(this.models, model => model instanceof model_1.Model ? model.pluck(attribute) : null);
}
exists(attribute) {
return !!lodash_1.reduce(this.pluck(attribute) || [], (memo, data) => memo || !lodash_1.isUndefined(data));
}
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("Collection", Collection);
stratus_1.Stratus.Services.Collection = [
'$provide',
($provide) => {
$provide.factory('Collection', [() => Collection]);
}
];
stratus_1.Stratus.Data.Collection = Collection;
}
};
});
//# sourceMappingURL=collection.js.map