cronapp-common-js
Version:
Cronapp Commons Functions
1,422 lines (1,231 loc) • 166 kB
JavaScript
//v2.1.0
var ISO_PATTERN = new RegExp("(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))");
var TIME_PATTERN = new RegExp("PT(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)(?:\\.(\\d+)?)?S)?");
var DEP_PATTERN = new RegExp("\\{\\{(.*?)\\|raw\\}\\}");
var HTTP_STATUS = new RegExp("HTTP\\/1\\.1 (\\d+) (.*)");
var DS_ID = new RegExp("[\\w\\_\\.\\d]+");
const isCronapiApiPath = (url) => /^\/?api\/cronapi\/.*\/?$/.test(url);
(function ($) {
var defaults = {
callback: function () { },
runOnLoad: true,
frequency: 100,
previousVisibility : null
};
var methods = {};
methods.checkVisibility = function (element, options) {
if (jQuery.contains(document, element[0])) {
var previousVisibility = options.previousVisibility;
var isVisible = element.is(':visible');
options.previousVisibility = isVisible;
var initialLoad = previousVisibility == null
if (initialLoad) {
if (options.runOnLoad) {
options.callback(element, isVisible, initialLoad);
}
} else if (previousVisibility !== isVisible) {
options.callback(element, isVisible, initialLoad);
}
setTimeout(function() {
methods.checkVisibility(element, options);
}, options.frequency);
}
};
$.fn.visibilityChanged = function (options) {
var settings = $.extend({}, defaults, options);
return this.each(function () {
methods.checkVisibility($(this), settings);
});
};
})(jQuery);
var initDatasource = function(scope, element, attrs, DatasetManager, $timeout, $parse, Notification, $translate, $location, $rootScope, $compile, $interpolate) {
var instanceId = parseInt(Math.random() * 9999);
//Add in header the path from the request was executed
if (cronapi.internal.isJsonString(attrs.headers)) {
let headersJson = JSON.parse(attrs.headers);
headersJson.push({"key" : "origin-path", "type" : "string", "value" : $location.path()});
attrs.headers = JSON.stringify(headersJson);
}
else {
var originPath = "origin-path:" + $location.path();
if (attrs.headers === undefined || attrs.headers === null) {
attrs.headers = originPath;
} else {
attrs.headers = attrs.headers.concat(";", originPath);
}
}
var props = {
name: attrs.name,
entity: attrs.entity,
apiVersion: attrs.apiVersion,
enabled: (attrs.hasOwnProperty('enabled')) ? (attrs.enabled === "true") : true,
keys: attrs.keys,
endpoint: attrs.endpoint,
lazy: attrs.lazy === "true",
append: !attrs.hasOwnProperty('append') || attrs.append === "true",
prepend: (attrs.hasOwnProperty('prepend') && attrs.prepend === "") || attrs.prepend === "true",
watch: attrs.watch,
rowsPerPage: attrs.rowsPerPage,
offset: attrs.offset,
filterURL: attrs.filter,
watchFilter: attrs.watchFilter,
deleteMessage: attrs.deleteMessage || attrs.deleteMessage === "" ? attrs.deleteMessage : $translate.instant('General.RemoveData'),
headers: attrs.headers,
autoPost: attrs.autoPost === "true",
autoRefresh: (attrs.autoRefresh !== undefined && attrs.autoRefresh !== null) ? attrs.autoRefresh : 0,
onError: attrs.onError,
onAfterFill: attrs.onAfterFill,
onBeforeCreate: attrs.onBeforeCreate,
onAfterCreate: attrs.onAfterCreate,
onBeforeUpdate: attrs.onBeforeUpdate,
onAfterUpdate: attrs.onAfterUpdate,
onBeforeDelete: attrs.onBeforeDelete,
onAfterDelete: attrs.onAfterDelete,
onChangeStatus: attrs.onChangeStatus,
onGet: attrs.onGet,
onPost: attrs.onPost,
onPut: attrs.onPut,
onDelete: attrs.onDelete,
defaultNotSpecifiedErrorMessage: $translate.instant('General.ErrorOnServerCommunication'),
dependentBy: attrs.dependentBy,
dependentLazyPost: attrs.dependentLazyPost,
batchPost: attrs.batchpost === "true",
dependentLazyPostField: attrs.dependentLazyPostField,
parameters: attrs.parameters,
parametersNullStrategy: attrs.parametersNullStrategy?attrs.parametersNullStrategy:"default",
parametersExpression: $(element).attr('parameters'),
conditionExpression: $(element).attr('condition'),
condition: attrs.condition,
orderBy: attrs.orderBy,
loadDataStrategy: attrs.loadDataStrategy,
schema: attrs.schema ? JSON.parse(attrs.schema) : undefined,
checkRequired: !attrs.hasOwnProperty('checkrequired') || attrs.checkrequired === "" || attrs.checkrequired === "true",
fetchOnVisible: (attrs.fetchOnVisible !== undefined && attrs.fetchOnVisible !== null) ? (attrs.fetchOnVisible === 'true') : false,
offline: attrs.offline === "true" ? true : false,
insideModal: attrs.insideModal === "true" ? true : false
}
var firstLoad = {
filter: true,
entity: true,
enabled: true,
parameters: true
}
var urlParameters;
if (scope.params) {
for (var paramKey in scope.params) {
if (scope.params.hasOwnProperty(paramKey)) {
var value = scope.params[paramKey];
if (paramKey.startsWith("$"+attrs.name+".")) {
var key = paramKey.split(".");
if (key.length == 2) {
if (key[1] == "$filterMode") {
props.startMode = value;
} else {
if (urlParameters) {
urlParameters += ";";
} else {
urlParameters = "";
}
if (!isNaN(value)) {
urlParameters += key[1]+"="+value;
} else {
urlParameters += key[1]+"='"+value+"'";
}
}
}
}
}
}
if (urlParameters) {
props.parameters = urlParameters;
props.parametersExpression = urlParameters;
}
}
var instanceId = parseInt(Math.random() * 9999);
var datasource = DatasetManager.initDataset(props, scope, $compile, $parse, $interpolate, instanceId, $translate);
var timeoutPromise;
attrs.$observe('filter', function(value) {
if (datasource.isPostingBatchData()) {
return;
}
if (!firstLoad.filter) {
// Stop the pending timeout
$timeout.cancel(timeoutPromise);
// Start a timeout
timeoutPromise = $timeout(function() {
if (datasource.events.overRideRefresh) {
datasource.callDataSourceEvents('overRideRefresh', 'filter', value);
} else {
datasource.filter(value, function (data) {
if (datasource.events.refresh) {
datasource.callDataSourceEvents('refresh', data, 'filter');
}
});
}
datasource.lastFilterParsed = value;
}, 100);
} else {
$timeout(function() {
firstLoad.filter = false;
}, 0);
}
});
if (!urlParameters) {
attrs.$observe('parameters', function(value) {
if (datasource.isPostingBatchData()) {
return;
}
if (datasource.parameters != value) {
datasource.parameters = value;
$timeout.cancel(timeoutPromise);
timeoutPromise =$timeout(function() {
datasource.callDataSourceEvents('changeDependency', 'parameters', datasource.parameters);
if (datasource.events.overRideRefresh) {
datasource.callDataSourceEvents('overRideRefresh', 'parameters', datasource.parameters);
} else {
datasource.fetch({
params: {}
}, {
success: function (data) {
if (datasource.events.refresh) {
datasource.callDataSourceEvents('refresh', data, 'parameters');
}
}
});
}
}, 0);
}
});
}
let calculate = {
lastCondition: undefined,
timeForCondition: undefined,
typingSpeedLimit: 1000,
maxWaitingValue: 1000,
wait: function() {
this.timeForCondition = 0;
if (this.lastCondition) {
let elapsedTime = new Date().getTime() - this.lastCondition.getTime();
if (elapsedTime < this.typingSpeedLimit) {
this.timeForCondition = this.maxWaitingValue;
}
}
this.lastCondition = new Date();
return this.timeForCondition;
}
};
attrs.$observe('condition', function(value) {
if (datasource.isPostingBatchData()) {
return;
}
if (datasource.condition != value) {
datasource.condition = value;
if (datasource.loadDataStrategy === "button") {
return;
}
$timeout.cancel(timeoutPromise);
timeoutPromise =$timeout(function() {
datasource.callDataSourceEvents('changeDependency', 'condition', datasource.condition);
if (datasource.events.overRideRefresh) {
datasource.callDataSourceEvents('overRideRefresh', 'condition', datasource.condition);
} else {
datasource.fetch({
params: {}
}, {
success: function (data) {
if (datasource.events.refresh) {
datasource.callDataSourceEvents('refresh', data, 'condition');
}
}
});
}
}, calculate.wait());
}
});
attrs.$observe('enabled', function(value) {
var boolValue = (value === "true");
if (datasource.enabled != boolValue) {
datasource.enabled = boolValue;
if (datasource.enabled) {
$timeout.cancel(timeoutPromise);
timeoutPromise =$timeout(function () {
if (datasource.events.overRideRefresh) {
datasource.callDataSourceEvents('overRideRefresh', 'enabled', datasource.parameters);
} else {
datasource.fetch({
params: {}
},
{
success: function (data) {
if (datasource.events.refresh) {
datasource.callDataSourceEvents('refresh', data, 'enabled');
}
}
}
);
}
}, 200);
}
}
});
attrs.$observe('entity', function(value) {
datasource.entity = value;
if (datasource.entity.match(DS_ID) && !isCronapiApiPath(datasource.entity)) {
datasource.entity = "api/cronapi/odata/v2/" + datasource.entity.replaceAll(".", "/");
}
if (!firstLoad.entity) {
// Only fetch if it's not the first load
$timeout.cancel(timeoutPromise);
timeoutPromise = $timeout(function() {
datasource.fetch({
params: {}
},
{
success : function (data) {
if (datasource.events.refresh) {
datasource.callDataSourceEvents('refresh', data, 'entity');
}
}
}
);
}, 200);
} else {
$timeout(function() {
firstLoad.entity = false;
});
}
});
attrs.$observe('offline', function (value) {
datasource.offline = value === 'true';
});
scope.$on('$destroy', function () {
if ($rootScope[attrs.name] && $rootScope[attrs.name + ".instanceId"] == instanceId) {
$rootScope[attrs.name].destroy();
delete window[attrs.name];
delete $rootScope[attrs.name];
delete $rootScope[attrs.name+".instanceId"];
}
});
};
angular.module('datasourcejs', [])
/**
* Global factory responsible for managing all datasets
*/
.factory('DatasetManager', ['$http', '$q', '$timeout', '$rootScope', '$window', 'Notification', 'SyncService', function($http, $q, $timeout, $rootScope, $window, Notification, SyncService) {
// Global dataset List
this.datasets = {};
let DatasetManagerUtil = {
getMainScope: function () {
return angular.element(document.getElementById('main-view')).scope();
},
resolveExpressionMap: async function (map) {
let result;
let $scope = this.getMainScope();
if (cronapi.internal.isJsonString(map)) {
result = {};
let json = JSON.parse(map);
for (var idx in json) {
let m = json[idx];
if (m.type === 'blockly') {
let toPromise = this.onMatchRegex(/cronapi.server/g, '.run(', '.toPromise().disableNotification()');
result[m.key] = await $scope.$eval(toPromise(m.value));
}
else if (m.type === 'expression') {
result[m.key] = $scope.$eval(m.value);
}
else {
result[m.key] = m.value;
}
}
}
return result;
},
getHeaders: async function(headersParam) {
let result;
if (headersParam && headersParam.length > 0) {
if (!cronapi.internal.isJsonString(headersParam)) {
result = {};
let headers = headersParam.trim().split(";");
let header;
for (var i = 0; i < headers.length; i++) {
header = headers[i].split(":");
if (header.length === 2) {
result[header[0]] = header[1];
}
}
}
else {
result = await this.resolveExpressionMap(headersParam);
}
}
if (result)
result["X-From-DataSource"] = "true";
return result;
},
onMatchRegex: function (regex, run, complete) {
return (callbackFunction) => {
let result = regex.exec(callbackFunction);
if (result && result.length) return callbackFunction.split(run).join(complete + run);
return callbackFunction;
}
}
};
const $httpLegacy = (config) => {
var promise = {
verb: config.method,
successCallback: null,
errorCallback: null,
success: function(successCallback) {
this.successCallback = successCallback;
return this;
},
error: function(errorCallback) {
this.errorCallback = errorCallback;
return this;
}
};
DatasetManagerUtil.getHeaders(config.headers).then(function (headers) {
config.headers = headers || config.headers;
if (config.offlineMode) {
SyncService.toQueue(config);
let result = config.originalObject || config.rawData || config.data;
this.successCallback(result, 200, null, config);
}
else {
let http = $http(config);
http.then(response => {
if (this.successCallback)
this.successCallback(response.data, response.status, response.headers, response.config);
});
http.catch(response => {
if (this.errorCallback)
this.errorCallback(response.data, response.status, response.headers, response.config);
});
}
}.bind(promise));
return promise;
};
/**
* Class representing a single dataset
*/
var DataSet = function(name, scope, fetchOnVisible) {
var NO_IMAGE_UPLOAD = "data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjEyOHB4IiBoZWlnaHQ9IjEyOHB4IiB2aWV3Qm94PSIwIDAgNDQuNTAyIDQ0LjUwMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDQuNTAyIDQ0LjUwMjsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8Zz4KCTxnPgoJCTxwYXRoIGQ9Ik05Ljg2MiwzNS42MzhoMjQuNzc5YzAtNS41NDYtMy44NjMtMTAuMjAzLTkuMTEzLTExLjYwNGMyLjc1LTEuMjQ4LDQuNjY4LTQuMDEzLDQuNjY4LTcuMjI5ICAgIGMwLTQuMzg4LTMuNTU5LTcuOTQyLTcuOTQyLTcuOTQyYy00LjM4NywwLTcuOTQzLDMuNTU3LTcuOTQzLDcuOTQyYzAsMy4yMTksMS45MTYsNS45OCw0LjY2OCw3LjIyOSAgICBDMTMuNzI1LDI1LjQzNSw5Ljg2MiwzMC4wOTIsOS44NjIsMzUuNjM4eiIgZmlsbD0iIzkxOTE5MSIvPgoJCTxwYXRoIGQ9Ik0xLjUsMTQuMTY5YzAuODI4LDAsMS41LTAuNjcyLDEuNS0xLjVWNC4zMzNoOC4zMzZjMC44MjgsMCwxLjUtMC42NzIsMS41LTEuNWMwLTAuODI4LTAuNjcyLTEuNS0xLjUtMS41SDIuNzc1ICAgIEMxLjI0NCwxLjMzMywwLDIuNTc3LDAsNC4xMDh2OC41NjFDMCwxMy40OTcsMC42NywxNC4xNjksMS41LDE0LjE2OXoiIGZpbGw9IiM5MTkxOTEiLz4KCQk8cGF0aCBkPSJNNDEuNzI3LDEuMzMzaC04LjU2MmMtMC44MjcsMC0xLjUsMC42NzItMS41LDEuNWMwLDAuODI4LDAuNjczLDEuNSwxLjUsMS41aDguMzM2djguMzM2YzAsMC44MjgsMC42NzMsMS41LDEuNSwxLjUgICAgczEuNS0wLjY3MiwxLjUtMS41di04LjU2QzQ0LjUwMiwyLjU3OSw0My4yNTYsMS4zMzMsNDEuNzI3LDEuMzMzeiIgZmlsbD0iIzkxOTE5MSIvPgoJCTxwYXRoIGQ9Ik00My4wMDIsMzAuMzMzYy0wLjgyOCwwLTEuNSwwLjY3Mi0xLjUsMS41djguMzM2aC04LjMzNmMtMC44MjgsMC0xLjUsMC42NzItMS41LDEuNXMwLjY3MiwxLjUsMS41LDEuNWg4LjU2ICAgIGMxLjUzLDAsMi43NzYtMS4yNDYsMi43NzYtMi43NzZ2LTguNTZDNDQuNTAyLDMxLjAwNSw0My44MywzMC4zMzMsNDMuMDAyLDMwLjMzM3oiIGZpbGw9IiM5MTkxOTEiLz4KCQk8cGF0aCBkPSJNMTEuMzM2LDQwLjE2OUgzdi04LjMzNmMwLTAuODI4LTAuNjcyLTEuNS0xLjUtMS41Yy0wLjgzLDAtMS41LDAuNjcyLTEuNSwxLjV2OC41NmMwLDEuNTMsMS4yNDQsMi43NzYsMi43NzUsMi43NzZoOC41NjEgICAgYzAuODI4LDAsMS41LTAuNjcyLDEuNS0xLjVTMTIuMTY1LDQwLjE2OSwxMS4zMzYsNDAuMTY5eiIgZmlsbD0iIzkxOTE5MSIvPgoJPC9nPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=";
var NO_FILE_UPLOAD = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4IiB2aWV3Qm94PSIwIDAgNTQ4LjE3NiA1NDguMTc2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1NDguMTc2IDU0OC4xNzY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPGc+Cgk8cGF0aCBkPSJNNTI0LjMyNiwyOTcuMzUyYy0xNS44OTYtMTkuODktMzYuMjEtMzIuNzgyLTYwLjk1OS0zOC42ODRjNy44MS0xMS44LDExLjcwNC0yNC45MzQsMTEuNzA0LTM5LjM5OSAgIGMwLTIwLjE3Ny03LjEzOS0zNy40MDEtMjEuNDA5LTUxLjY3OGMtMTQuMjczLTE0LjI3Mi0zMS40OTgtMjEuNDExLTUxLjY3NS0yMS40MTFjLTE4LjA4MywwLTMzLjg3OSw1LjkwMS00Ny4zOSwxNy43MDMgICBjLTExLjIyNS0yNy40MS0yOS4xNzEtNDkuMzkzLTUzLjgxNy02NS45NWMtMjQuNjQ2LTE2LjU2Mi01MS44MTgtMjQuODQyLTgxLjUxNC0yNC44NDJjLTQwLjM0OSwwLTc0LjgwMiwxNC4yNzktMTAzLjM1Myw0Mi44MyAgIGMtMjguNTUzLDI4LjU0NC00Mi44MjUsNjIuOTk5LTQyLjgyNSwxMDMuMzUxYzAsMi40NzQsMC4xOTEsNi41NjcsMC41NzEsMTIuMjc1Yy0yMi40NTksMTAuNDY5LTQwLjM0OSwyNi4xNzEtNTMuNjc2LDQ3LjEwNiAgIEM2LjY2MSwyOTkuNTk0LDAsMzIyLjQzLDAsMzQ3LjE3OWMwLDM1LjIxNCwxMi41MTcsNjUuMzI5LDM3LjU0NCw5MC4zNThjMjUuMDI4LDI1LjAzNyw1NS4xNSwzNy41NDgsOTAuMzYyLDM3LjU0OGgzMTAuNjM2ICAgYzMwLjI1OSwwLDU2LjA5Ni0xMC43MTEsNzcuNTEyLTMyLjEyYzIxLjQxMy0yMS40MDksMzIuMTIxLTQ3LjI0NiwzMi4xMjEtNzcuNTE2QzU0OC4xNzIsMzM5Ljk0NCw1NDAuMjIzLDMxNy4yNDgsNTI0LjMyNiwyOTcuMzUyICAgeiBNMzYyLjcyOSwyODkuNjQ4Yy0xLjgxMywxLjgwNC0zLjk0OSwyLjcwNy02LjQyLDIuNzA3aC02My45NTN2MTAwLjUwMmMwLDIuNDcxLTAuOTAzLDQuNjEzLTIuNzExLDYuNDIgICBjLTEuODEzLDEuODEzLTMuOTQ5LDIuNzExLTYuNDIsMi43MTFoLTU0LjgyNmMtMi40NzQsMC00LjYxNS0wLjg5Ny02LjQyMy0yLjcxMWMtMS44MDQtMS44MDctMi43MTItMy45NDktMi43MTItNi40MlYyOTIuMzU1ICAgSDE1NS4zMWMtMi42NjIsMC00Ljg1My0wLjg1NS02LjU2My0yLjU2M2MtMS43MTMtMS43MTQtMi41NjgtMy45MDQtMi41NjgtNi41NjZjMC0yLjI4NiwwLjk1LTQuNTcyLDIuODUyLTYuODU1bDEwMC4yMTMtMTAwLjIxICAgYzEuNzEzLTEuNzE0LDMuOTAzLTIuNTcsNi41NjctMi41N2MyLjY2NiwwLDQuODU2LDAuODU2LDYuNTY3LDIuNTdsMTAwLjQ5OSwxMDAuNDk1YzEuNzE0LDEuNzEyLDIuNTYyLDMuOTAxLDIuNTYyLDYuNTcxICAgQzM2NS40MzgsMjg1LjY5NiwzNjQuNTM1LDI4Ny44NDUsMzYyLjcyOSwyODkuNjQ4eiIgZmlsbD0iI2NlY2VjZSIvPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=";
// Publiic members
this.fetchOnVisible = fetchOnVisible;
this.parent = $("[name='"+name+"']").parent();
if (this.fetchOnVisible) {
this.parent.visibilityChanged({
callback: function(element, visible, initialLoad) {
if (visible && this.holdServiceCall) {
this.holdServiceCall();
}
}.bind(this),
runOnLoad: false,
frequency: 100
});
}
this.Notification = Notification;
this.$scope = scope;
this.noImageUpload = NO_IMAGE_UPLOAD;
this.noFileUpload = NO_FILE_UPLOAD;
this.$apply = function(fc) {
scope.safeApply(fc);
}.bind(scope);
this.columns = [];
this.data = [];
this.name = name;
this.keys = [];
this.enabled = true;
this.endpoint = null;
this.active = {};
this.inserting = false;
this.editing = false;
this.fetchSize = 2;
this.observers = [];
this.rowsPerPage = null;
this.append = true;
this.headers = null;
this.responseHeaders = null;
this._activeValues = null;
this.errorMessage = "";
this.onError = null;
this.links = null;
this.loadedFinish = null;
this.lastLoadedTime = 0;
this.lastFilterParsed = null;
this.rowsCount = -1;
this.events = {};
this.busy = false;
this.cursor = 0;
this._savedProps;
this.hasMoreResults = false;
this.loaded = false;
this.unregisterDataWatch = null;
this.dependentBufferLazyPostData = null;
this.lastAction = null;
this.dependentData = null;
this.hasMemoryData = false;
this.batchPost= false;
this.caseInsensitive = null;
this.terms = null;
this.checkRequired = true;
this.schema;
var _self = this;
var service = null;
this.odataFile = [];
function reverseArr(input) {
if (input) {
var ret = new Array;
for (var i = input.length - 1; i >= 0; i--) {
ret.push(input[i]);
}
return ret;
} else {
return [];
}
}
this.destroy = function() {
}
// Public methods
/**
* Initialize a single datasource
*/
this.init = function() {
var dsScope = this;
// Get the service resource
service = {
save: function(object) {
return this.call(_self.entity, "POST", object, true);
},
update: function(url, object) {
return this.call(url, "PUT", object, false);
},
remove: function(url, object) {
return this.call(url, "DELETE", object, true);
},
call: function(url, verb, obj, applyScope) {
var object = {};
var isCronapiQuery = (url.indexOf('/cronapi/query/') >= 0);
if (isCronapiQuery) {
object.inputs = [obj];
var fields = {};
var _callback;
var _callbackError;
_self.busy = true;
url = url.replace('/specificSearch', '');
url = url.replace('/generalSearch', '');
if (_self && _self.$scope && _self.$scope.vars) {
fields["vars"] = {};
for (var attr in _self.$scope.vars) {
fields.vars[attr] = _self.$scope.vars[attr];
}
}
for (var key in _self.$scope) {
if (_self.$scope[key] && _self.$scope[key].constructor && _self.$scope[key].constructor.name == "DataSet") {
fields[key] = {};
fields[key].active = _self.$scope[key].active;
}
}
object.fields = fields;
} else {
object = obj;
}
var cloneObject = {};
_self.copy(object, cloneObject, true);
delete cloneObject.__original;
delete cloneObject.__status;
delete cloneObject.__originalIdx;
delete cloneObject.__sender;
delete cloneObject.__$id;
delete cloneObject.__parentId;
delete cloneObject.$$hashKey;
delete cloneObject.__fromMemory;
delete cloneObject.__odatafiles;
delete cloneObject.__$masterExpression;
for (var key in cloneObject) {
if (key.indexOf('__odataFile_') > -1) {
cloneObject[key.replace('__odataFile_','')] = undefined;
delete cloneObject[key];
}
}
for (var key in cloneObject) {
if (cloneObject.hasOwnProperty(key)) {
let value = cloneObject[key];
if (_self.isAutoGeneratedValue(value)) {
delete cloneObject[key];
}
}
}
var success = function(data, status, headers, config, batchPostponed) {
_self.busy = false;
if (_callback) {
if (_self.isOData()) {
if (data.d != null && data.d.result != null) {
_self.normalizeData(data.d.result);
_callback(data.d.result, true, batchPostponed);
} else if (data.d != null) {
_self.normalizeObject(data.d);
_callback(data.d, true, batchPostponed);
} else {
_callback(data, true, batchPostponed);
}
} else {
_callback(isCronapiQuery?data.value:data, true, batchPostponed);
}
}
if (isCronapiQuery || _self.isOData()) {
var commands = data;
if (_self.isOData()) {
commands = {};
commands.commands = data.__callback;
_self.normalizeData(commands.commands);
}
_self.$scope.cronapi.evalInContext(JSON.stringify(commands));
}
};
var ds = _self.getParentDatasource();
let batchObj = ds.getBatchDataItem(object);
if (batchObj && batchObj.result) {
let idx = ds.batchServiceData.indexOf(batchObj);
ds.batchServiceData.splice(idx, 1);
$timeout(() => {
success(batchObj.result);
},0);
this.$promise = {};
} else {
// Get an ajax promise
this.$promise = _self.getService(verb, object)({
method: verb,
url: _self.removeSlash(((window.hostApp || "") + url)),
data: verb!='DELETE'?((object) ? JSON.stringify(cloneObject) : null):null,
headers: _self.headers,
rawData: (object) ? cloneObject : null,
originalObject: (object) ? object : null,
urlPart: url,
offlineMode: _self.isOfflineMode()
}).success(success).error(function (data, status, headers, config) {
_self.busy = false;
var msg;
if (_self.isOData()) {
msg = data.error.message.value;
} else {
msg = isCronapiQuery && data.value ? data.value : data
}
_self.handleError(msg);
if (_callbackError) {
_callbackError(msg);
}
});
}
this.$promise.then = function (callback) {
_callback = callback;
return this;
}
this.$promise.error = function (callback) {
_callbackError = callback;
return this;
}
return this;
}
}
this.isAutoGeneratedValue = function(value) {
return typeof value === 'string'
&& value.length > 10
&& (value.startsWith("$autogenerated$") || value.substring(1).startsWith("$autogenerated$"));
}
this.getIndexedDB = function(properties) {
if (!window.indexedDB) {
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
}
return {
properties: properties,
successCallback: null,
errorCallback: null,
type: null,
args: null,
success: function(successCallback) {
this.successCallback = successCallback;
return this;
},
error: function(errorCallback) {
this.errorCallback = errorCallback;
return this;
},
post: function() {
this.type = 'put';
this.args = arguments;
return this;
},
put: function() {
this.type = 'put';
this.args = arguments;
return this;
},
delete: function() {
this.type = 'delete';
this.args = arguments;
return this;
},
get: function() {
this.type = 'getAll';
this.args = arguments;
return this;
},
call: function() {
request = window.indexedDB.open(this.properties.dbname, this.properties.dbversion||1);
request.onerror = function (event) {
if (this.errorCallback) {
this.errorCallback(event);
}
}.bind(this);
request.onsuccess = function (event) {
this.proceed(event.target.result);
}.bind(this);
request.onupgradeneeded = function (event) {
var db = event.target.result;
var objectStore = db.createObjectStore(this.properties.objectStore, {keyPath: this.properties.key});
objectStore.transaction.oncomplete = function(event) {
var objectStore = db.transaction(this.properties.objectStore, "readwrite").objectStore(this.properties.objectStore);
objectStore.add({id: 123, name: 'Jose'});
}.bind(this);
}.bind(this);
},
proceed: function(db) {
if (this.type) {
var transaction = db.transaction(this.properties.objectStore, "readwrite")
var store = transaction.objectStore(this.properties.objectStore);
var request = store[this.type].apply(store, this.args);
request.onsuccess = function(event) {
if (this.successCallback) {
if (this.type === 'put') {
this.successCallback(this.args[0]);
} else {
this.successCallback(event.target.result);
}
}
}.bind(this);
} else {
if (this.successCallback) {
this.successCallback();
}
}
}
};
}
this.batchServiceData = [];
this.getBatchService = function(verb) {
return function(properties) {
var promise = {
verb: verb,
properties: properties,
successCallback: null,
errorCallback: null,
success: function (successCallback) {
this.successCallback = successCallback;
return this;
},
error: function (errorCallback) {
this.errorCallback = errorCallback;
return this;
}
};
$timeout(function() {
var ds = this.getParentDatasource();
let obj = properties.originalObject || properties.rawData || properties.data || this.active
let entity = this.entity;
if (verb == 'PUT') {
entity = this.getEditionURL(obj);
}
if (verb == 'DELETE') {
entity = this.getDeletionURL(obj);
}
if (entity.indexOf("/") > 0) {
entity = entity.substring(entity.lastIndexOf("/")+1, entity.length);
}
ds.batchServiceData.push({
entity: entity,
data: obj,
promise: promise
});
if (promise.successCallback) {
promise.successCallback.call(this, obj, null, null, null, true);
}
}.bind(this));
return promise;
}.bind(this);
}
this.batchEnabled = function(obj) {
if (obj && this.hasPendingChanges()) {
return this.getBatchDataItem(obj) != null ? false : true;
}
return false;
}
this.getBatchDataItem = function(obj) {
if (obj) {
var ds = this.getParentDatasource();
for (let i = 0; i < ds.batchServiceData.length; i++) {
let data = ds.batchServiceData[i].data;
if (data.__$id == obj.__$id) {
return ds.batchServiceData[i];
}
}
}
return null;
}
this.performBatchPost = function(originCallback) {
return new Promise((resolve, reject) => {
let boundary = "batch_" + this.uuidv4();
let odataPost = "";
odataPost += "--" + boundary + "\n";
let changeSet = "changeset_" + this.uuidv4();
odataPost += "Content-Type: multipart/mixed; boundary=" + changeSet + "\n";
for (let i = 0; i < this.batchServiceData.length; i++) {
let batchData = this.batchServiceData[i];
odataPost += "--" + changeSet + "\n";
odataPost += "Content-Type: application/http\n";
odataPost += "Content-Transfer-Encoding:binary\n";
odataPost += batchData.promise.properties.method + " " + batchData.entity + " HTTP/1.1\n";
odataPost += "Content-Type: application/json\n";
odataPost += "Accept: application/json\n";
odataPost += "X-Master-Id: "+batchData.promise.properties.originalObject.__$id+"\n";
if (batchData.promise.properties.originalObject.__$masterExpression) {
odataPost += "X-Detail-Fill: " + batchData.promise.properties.originalObject.__$masterExpression + "\n";
}
let headers = batchData.promise.properties.headers;
for (var key in headers) {
if (headers.hasOwnProperty(key)) {
odataPost += key + ": " + headers[key] + "\n";
}
}
if (batchData.promise.properties.method != 'DELETE') {
odataPost += batchData.promise.properties.data + "\n";
}
}
odataPost += "--" + changeSet + "--\n";
odataPost += "--" + boundary + "--\n";
console.log(odataPost);
let url = this.entity;
if (url.indexOf("/") > 0) {
url = url.substring(0, url.lastIndexOf("/"));
}
let headers = {};
this.copy(this.headers, headers);
headers["Content-Type"] = "multipart/mixed; boundary=" + boundary;
let error = (data, status, headers, config) => {
let callback;
if (this.batchServiceData.length > 0) {
callback = this.batchServiceData[0].promise.errorCallback;
}
if (callback) {
callback(data, status, headers, config, false);
} else {
this.handleError(data)
}
this.batchServiceData = [];
};
$httpLegacy({
method: "POST",
url: _self.removeSlash(((window.hostApp || "") + url + "/$batch")),
data: odataPost,
headers: headers,
offlineMode: _self.isOfflineMode()
}).success(function (data, status, headers, config, batchPostponed) {
let result = this.parseBatchResult(data);
for (let i = 0; i < result.length; i++) {
if (result[i].status >= 400) {
error(result[i].data, result[i].status);
return;
}
}
for (let i = 0; i < this.batchServiceData.length; i++) {
this.batchServiceData[i].result = !result[i] || result[i].data == null ? this.batchServiceData[i].data : result[i].data;
}
if (originCallback) {
originCallback();
} else {
let callback;
if (this.batchServiceData.length > 0) {
callback = this.batchServiceData[0].promise.successCallback;
}
if (callback) {
this.batchServiceData.splice(0, 1);
callback(result[0].data, result[0].status, headers, config, false);
}
}
}.bind(this)).error(error);
});
}
this.parseBatchResult = function(data) {
let results = [];
let lines = data.split("\n");
let status;
for (let j=0;j<lines.length;j++) {
let line = lines[j];
if (line.startsWith("{")) {
results.push({
status: status,
data: JSON.parse(line)
});
}
if (line.startsWith("HTTP/1.1")) {
var g = HTTP_STATUS.exec(line);
status = g[1];
}
if (line.startsWith("HTTP/1.1") && line.indexOf("No Content") > 0) {
results.push({
status: status,
data: null
});
}
}
return results;
}
this.uuidv4 = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
this.getService = function(verb, object) {
_self = this;
var event = eval("this.on" + verb);
if ( verb === 'GET' && ( event || this.isLocalData() || this.isNavigatorOffline()) ) {
return function (properties) {
var promise = {
verb: verb,
properties: properties,
successCallback: null,
errorCallback: null,
success: function(successCallback) {
this.successCallback = successCallback;
return this;
},
error: function(errorCallback) {
this.errorCallback = errorCallback;
return this;
}
};
$timeout(function() {
var contextVars = {
'currentData': this.properties.rawData||this.properties.data||_self.active,
'filter': this.properties.filter||"",
'datasource': _self,
'selectedIndex': _self.cursor,
'index': _self.cursor,
'selectedRow': _self.active,
'item': _self.active,
'selectedKeys': _self.getKeyValues(_self.active, true),
'selectedKey': _self.getFirstKeyValue(_self.active, true),
'callback': this.successCallback
};
var result;
if (event) {
result = _self.$scope.$eval(event, contextVars);
if (result instanceof Promise) {
result.then(function(result) {
if (result && Object.prototype.toString.call(result) !== '[object Array]') {
result = [result];
}
_self.$scope.safeApply(function() {
this.successCallback(result)
}.bind(this));
}.bind(this)).catch(function(reason){_self.handleError(reason)}.bind(this))
} else if(result){
this.successCallback(result);
}
} else {
var args = [];
let db = _self.getPouchDB();
if (this.verb == 'GET' && !this.properties.filter) {
db.allDocs({
include_docs: true,
attachments: true
}).catch(this.errorCallback).then(function(result) {
var data = [];
for (var i=0;i<result.rows.length;i++) {
data.push(result.rows[i].doc);
}
result.rows = data;
if (this.successCallback) {
this.successCallback(result);
}
}.bind(this));
}
else if (this.verb == 'GET') {
var filter = peg$parse(this.properties.filter);
db.find({selector: filter}).catch(this.errorCallback).then(function(result) {
result.rows = result.docs;
result.total_rows = result.rows.length;
if (this.successCallback) {
this.successCallback(result);
}
}.bind(this));
}
}
}.bind(promise),0);
return promise;
}
}
if (this.hasOfflineSupport() && object) {
this.operationPouchDB(verb, object);
}
if (this.batchEnabled(object) && verb != 'GET') {
return this.getBatchService(verb);
}
return $httpLegacy;
}
/**
* Check if the datasource is waiting for any request response
*/
this.isBusy = function() {
return this.busy;
}
/**
* Check if the datasource was loaded by service
*/
this.isLoaded = function() {
return this.loaded;
}
this.toString = function() {
return "[Datasource]"
}
this.handleAfterCallBack = function(callBackFunction, callback) {
if (callBackFunction) {
try {
var contextVars = {
'currentData': this.data,
'datasource': this,
'selectedIndex': this.cursor,
'index': this.cursor,
'selectedRow': this.active,
'item': this.active,
'selectedKeys': this.getKeyValues(this.active, true),
'selectedKey': this.getFirstKeyValue(_self.active, true),
'callback': callback
};
this.$scope.$eval(callBackFunction, contextVars);
} catch (e) {
this.handleError(e);
}
}
}
this.handleBeforeCallBack = async function(callBackFunction, callback) {
var isValid = true;
let toPromise = DatasetManagerUtil.onMatchRegex(/cronapi.server/g, '.run(', '.toPromise().disableNotification()');
if (callBackFunction) {
try {
var contextVars = {
'currentData': this.data,
'datasource': this,
'selectedIndex': this.cursor,
'index': this.cursor,
'selectedRow': this.active,
'item': this.active,
'selectedKeys': this.getKeyValues(this.active, true),
'selectedKey': this.getFirstKeyValue(_self.active, true),
'callback': callback
};
await this.$scope.$eval(toPromise(callBackFunction), contextVars);
} catch (e) {
isValid = false;
this.handleError(e);
}
}
return isValid;
}
/**
* Error Handler function
*/
this.handleError = function(data) {
console.log(data);
if (data instanceof XMLDocument) {
var errorMsg = data.getElementsByTagName("message")[0].textContent;
data = errorMsg;
console.log(data);
}
var error = "";
if (data) {
if (Object.prototype.toString.call(data) === "[object String]") {
error = data;
} else {
var errorMsg = (data.msg || data.desc || data.message || data.error || data.responseText);
if (this.isOData() && data.error.message && data.error.message.value) {
errorMsg = data.error.message.value;
}
if (errorMsg) {
error = errorMsg;
}
}
}
if (!error) {
error = this.defaultNotSpecifiedErrorMessage;
}
var regex = /<h1>(.*)<\/h1>/gmi;
result = regex.exec(error);
if (result && result.length >= 2) {
error = result[1];
}
this.errorMessage = error;
if (this.onError && this.onError !== '') {
if (typeof(this.onError) === 'string') {
try {
var contextVars = {
'currentData': this.active,
'filter': "",
'error': error,
'datasource': this,
'selectedIndex': this.cursor,
'index': this.cursor,
'selectedRow': this.active,
'item': this.active,
'selectedKeys': this.getKeyValues(this.active, true),
'selectedKey': this.getFirstKeyValue(this.active, true),
'callback': this.successCallback
};
var func = this.$scope.$eval(this.onError, contextVars);
if (typeof(func) === 'function') {
this.onError = func;
}
} catch (e) {
isValid = false;
Notification.error(e);
}
}
} else {
this.onError = function(error) {
Notification.error(error);
};
}
if (typeof(this.onError) === 'function') {
this.onError.call(this, error);
}
}
// Start watching for changes in activeRow to notify observers
if (this.observers && this.observers.length > 0) {
$rootScope.$watch(function() {
return this.active;
}.bind(this), function(activeRow) {
if (activeRow) {
this.notifyObservers(activeRow);
}
}.bind(this), true);
}
}
//Public methods
this.setFile = function($file, object, field) {
if ($file && $file.$error === 'pattern') {
return;
}
if ($file) {
toBase64($file, function(base64Data) {
this.$apply = function(value) {
object[field] = value;
scope.$apply(object);
}.bind(scope);
this.$apply(base64Data);
});
}
};
this.downloadFile = function(field, keys) {
if (keys === undefined)
return;
var url = (window.hostApp || "") + this.entity + "/download/" + field;
for (var index = 0; index < keys.length; index++) {
url += "/" + keys[index];
}
var req = {
url: url,
method: 'GET',
responseType: 'arraybuffer'
};
$http(req).then(function(result) {
var blob = new Blob([result.data], {
type: 'application/*'
});
$window.open(URL.createObjectURL(blob));
});
};
function toBase64(file, cb) {
var fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = function(e) {
var base64Data = e.target.result.substr(e.target.result.indexOf('base64,') + 'base64,'.length);
cb(base64Data);
};
}
this.openImage = function(data) {
if (data.indexOf('https://') == -1 && data.indexOf('http://') == -1) {
var value = 'data:image/png;base64,' + data;
var w = $window.open("", '_blank', 'height=300,width=400');
w.document.write('<img src="'+ value + '"/>');
} else {
$window.open(data, '_blank', 'height=300,width=400');
}
};
this.byteSize = function(base64String) {
if (!angular.isString(base64String)) {
return '';
}
function endsWith(suffix, str) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function paddingSize(base64String) {
if (endsWith('==', base64String)) {
return 2;
}
if (endsWith('=', base64String)) {
return 1;
}
return 0;
}
function size(base64String) {
return base64String.length / 4 * 3 - paddingSize(base64String);
}
function formatAsBytes(size) {
return size.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ' bytes';
}
return formatAsBytes(size(base64String));
};
function uuid() {
var uuid = "", i, random;
for (i = 0; i < 32; i++) {
random = Math.random() * 16 | 0;
if (i == 8 || i == 12 || i == 16 || i == 20) {
uuid += "-"
}
uuid += (i == 12 ? 4 : (i == 16 ? (random & 3 | 8) : random)).toString(16);
}
return uuid;
}
/**
* Append a new value to the end of this dataset.
*/
this.insert = async function(obj, onSuccess, onError, forceSave) {
if (await this.handleBeforeCallBack(this.onBeforeCreate)) {
//Check if contains dependentBy, if contains, only store in data
if ((this.dependentLazyPost || this.batchPost) && !forceSave) {
obj.__status = 'inserted';
if (this.dependentLazyPost) {
obj.__parentId = eval(this.dependentLazyPost).active.__$id;
}
this.hasMemoryData = true;
this.notifyPendingChanges(this.hasMemoryData);
if (onSuccess)
onSuccess(obj);
} else {
service.save(obj).$promise.error(onError).then(onSuccess);
}
}
};
//Public methods
/**
* Append a datasource to be notify when has a post or cancel
*/
this.addDependentDatasource = function(dts) {
if (!this.children)
this.children = [];
this.children.push(dts);
if (this.dependentLaz