cronapp-framework-js
Version:
Javascript library for CronApp's projects
1,243 lines (1,146 loc) • 320 kB
JavaScript
//Version 2.0.6
(function($app) {
app.common = {
generateId: function() {
var numbersOnly = '0123456789';
var result = Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
if (numbersOnly.indexOf(result.substr(0,1)) > -1)
return this.generateId();
return result;
}
}
var isoDate = /(\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 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))");
Number.MAX_SAFE_INTEGER_32 = 2147483647;
/**
* Função que retorna o formato que será utilizado no componente
* capturando o valor do atributo format do elemento, para mais formatos
* consulte os formatos permitidos em http://momentjs.com/docs/#/parsing/string-format/
*
*/
var patternFormat = function(element) {
if (element) {
return $(element).attr('format') || 'DD/MM/YYYY';
}
return 'DD/MM/YYYY';
}
var parsePermission = function(perm) {
var result = {
visible: {
public: true
},
enabled: {
public: true
},
render: {
public: true
},
notvisible: {
public: false
},
notenabled: {
public: false
},
notrender: {
public: false
}
}
if (perm) {
var perms = perm.toLowerCase().trim().split(",");
for (var i=0;i<perms.length;i++) {
var p = perms[i].trim();
if (p) {
var pair = p.split(":");
if (pair.length == 2) {
var key = pair[0].trim();
var value = pair[1].trim();
if (value) {
var values = value.split(";");
var json = {};
for (var j=0;j<values.length;j++) {
var v = values[j].trim();
if (v) {
json[v] = true;
}
}
result[key] = json;
}
}
}
}
}
return result;
};
app.directive('cronCalendar', ['$timeout', function ($timeout) {
return {
restrict: 'E',
link: async function (scope, element, attrs, ngModelCtrl) {
let options = {};
try {
options = JSON.parse(attrs.options);
} catch (e) {
console.error(e);
}
const cronCalendarElement = $(element);
const culture = navigator.language || navigator.userLanguage;
const expressionInitialDate = options.expressionInitialDate;
const expressionSelectDates = options.expressionSelectDates;
const expressionDisableDates = options.expressionDisableDates;
const expressionMinDate = options.expressionMinDate;
const expressionMaxDate = options.expressionMaxDate;
const expressionOnChange = options.expressionOnChange;
const expressionOnNavigate = options.expressionOnNavigate;
const initialDate = expressionInitialDate ? await scope.$eval(generateBlocklyCall(expressionInitialDate, true)) : new Date();
const selectDates = (expressionSelectDates && options.isSelectableMultiple) ? await scope.$eval(generateBlocklyCall(expressionSelectDates, true)) : [];
const disableDates = expressionDisableDates ? await scope.$eval(generateBlocklyCall(expressionDisableDates, true)) : null;
const min = expressionMinDate ? await scope.$eval(generateBlocklyCall(expressionMinDate, true)) : new Date(1900, 0, 1);
const max = expressionMaxDate ? await scope.$eval(generateBlocklyCall(expressionMaxDate), true) : new Date(2099, 11, 31);
cronCalendarElement.kendoCalendar({
culture: culture.startsWith('pt') ? 'pt-BR' : 'en-US',
componentType: options.isClassicType ? 'classic' : 'modern',
selectable: options.isSelectableSingle ? 'single' : 'multiple',
weekNumber: options.showWeekNumbers,
value: initialDate,
selectDates: selectDates,
disableDates: disableDates,
min: min,
max: max,
start: options.startView
});
let calendar = cronCalendarElement.data('kendoCalendar');
calendar.bind("change", function () {
let value = this.value();
//value is the selected date in the calendar
if (expressionOnChange) {
scope.$eval(generateBlocklyCall(expressionOnChange));
}
});
calendar.bind("navigate", function () {
let view = this.view();
//name of the current view
let current = this.current();
//currently focused date
if (expressionOnChange) {
scope.$eval(generateBlocklyCall(expressionOnNavigate));
}
});
function updateView(value) {
ngModelCtrl.$viewValue = value;
ngModelCtrl.$render();
}
function updateModel(value) {
ngModelCtrl.$modelValue = value;
scope.ngModel = value; // overwrites ngModel value
}
}
}
}]);
app.directive('justGage', ['$timeout', function ($timeout) {
return {
restrict: 'EA',
scope: {
id: '@',
class: '@',
min: '=',
max: '=',
title: '@',
label: '@',
value: '@',
options: '='
},
template: '<div id="{{id}}-justgage" class="{{class}}"></div>',
link: function (scope, element, attrs) {
$timeout(function () {
var options = {
id: scope.id + '-justgage',
min: scope.min || 0,
max: scope.max || 100,
title: scope.title,
label: scope.label || '',
value: scope.value
};
if (scope.options) {
for (var key in scope.options) {
options[key] = scope.options[key];
}
}
var graph = new JustGage(options);
scope.$watch('max', function (updatedMax) {
if (updatedMax !== undefined) {
graph.refresh(scope.value, updatedMax);
}
}, true);
scope.$watch('value', function (updatedValue) {
if (updatedValue !== undefined) {
graph.refresh(updatedValue);
}
}, true);
});
}
};
}]);
app.directive('crnAnchor', ['$rootScope', '$location', '$anchorScroll', function ($rootScope, $location, $anchorScroll) {
return {
restrict: 'A',
link: function (scope, instanceElement, instanceAttributes) {
instanceElement.bind('click', function() {
let target = instanceAttributes["crnAnchor"];
$anchorScroll(target);
$('#' + target).get(0).focus();
});
}
}
}]);
app.directive('crnTooltip', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (attr.crnTooltip && attr.crnTooltip.toString() && attr.crnTooltip.toString().trim().length > 0) {
try {
let configTooltip = JSON.parse(attr.crnTooltip.toString());
if (configTooltip.content && configTooltip.content.toString() && attr.crnTooltip.toString().trim().length > 0) {
$(element).kendoTooltip(configTooltip).data("kendoTooltip");
}
} catch (e) {
console.error("Formato do atributo tooltip deve ser um json.");
console.error("Valor atual: " + attr.crnTooltip);
console.error("Error ocorrido: " + e);
}
}
}
};
});
app.directive('input', transformText);
app.directive('textarea', transformText);
var generateBlocklyCall = function(blocklyInfo, hasToPromise) {
var call = "";
if (!blocklyInfo) return call;
if (blocklyInfo.type == "client") {
call = "cronapi.client('" + blocklyInfo.blocklyClass + "." + blocklyInfo.blocklyMethod + "')";
var params = "";
blocklyInfo.blocklyParams.forEach(function(p) {
if (params.length > 0) {
params += ", ";
}
params += (p.value ? p.value : "null");
});
call += ".run("+params+")";
}
else if (blocklyInfo.type == "server") {
var blocklyName = blocklyInfo.blocklyClass + '.' + blocklyInfo.blocklyMethod;
call = "cronapi.server('"+blocklyName+"')" + (hasToPromise ? '.toPromise()' : '');
var params = "";
blocklyInfo.blocklyParams.forEach(function(p) {
if (params.length > 0) {
params += ", ";
}
params += (p.value ? p.value : "null");
});
call += ".run("+params+")";
}
return call;
}
app.directive('asDate', maskDirectiveAsDate)
.directive('ngDestroy', function() {
return {
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
element.on('$destroy', function() {
if (attrs.ngDestroy && attrs.ngDestroy.length > 0)
if (attrs.ngDestroy.indexOf('app.') > -1 || attrs.ngDestroy.indexOf('blockly.') > -1)
scope.$eval(attrs.ngDestroy);
else
eval(attrs.ngDestroy);
});
}
}
})
.directive('dynamicImage', function($compile) {
var template = '';
return {
restrict: 'E',
replace: true,
scope: {
ngModel: '@',
width: '@',
height: '@',
style: '@',
class: '@'
},
require: 'ngModel',
template: '<div></div>',
init: function(s) {
if (!s.ngModel)
s.ngModel = '';
if (!s.width)
s.width = '128';
if (!s.height)
s.height = '128';
if (!s.style)
s.style = '';
if (!s.class)
s.class = '';
if (!this.containsLetter(s.width))
s.width += 'px';
if (!this.containsLetter(s.height))
s.height += 'px';
},
containsLetter: function(value) {
var containsLetter;
for (var i=0; i<value.length; i++) {
containsLetter = true;
for (var number = 0; number <10; number++)
if (parseInt(value[i]) == number)
containsLetter = false;
if (containsLetter)
break;
}
return containsLetter;
},
link: function(scope, element, attr) {
this.init(scope);
var s = scope;
var required = (attr.ngRequired && attr.ngRequired == "true"?"required":"");
var templateDyn = '<div class="form-group upload-image-component" ngf-drop="" ngf-drag-over-class="dragover">\
<img class="$class$" style="$style$; height: $height$; width: $width$;" ng-if="$ngModel$" data-ng-src="{{$ngModel$.startsWith(\'http\') || ($ngModel$.startsWith(\'/\') && $ngModel$.length < 1000)? $ngModel$ : \'data:image/png;base64,\' + $ngModel$}}">\
<img class="$class$" style="$style$; height: $height$; width: $width$;" ng-if="!$ngModel$" data-ng-src="/plugins/cronapp-framework-js/img/selectImg.svg" class="btn" ng-if="!$ngModel$" ngf-drop="" ngf-select="" ngf-change="cronapi.internal.setFile(\'$ngModel$\', $file)" accept="image/*;capture=camera">\
<button class="remove btn btn-danger btn-xs" ng-if="$ngModel$" ng-click="$ngModel$=null">\
<span class="glyphicon glyphicon-remove"></span>\
<span class="sr-only">{{"Remove" | translate}}</span>\
</button>\
<button class="btn btn-info btn-xs start-camera-button" ng-if="!$ngModel$" ng-click="cronapi.internal.startCamera(\'$ngModel$\')">\
<span class="glyphicon glyphicon-facetime-video"></span>\
<span class="sr-only">{{"Upload.camera" | translate}}</span>\
</button>\
<input ng-if="!$ngModel$" autocomplete="off" tabindex="-1" class="uiSelectRequired ui-select-offscreen" style="top: inherit !important; margin-left: 85px !important;margin-top: 50px !important;" type=text ng-model="$ngModel$" $required$>\
</div>';
element.append(templateDyn
.split('$height$').join(s.height)
.split('$width$').join(s.width)
.split('$ngModel$').join(s.ngModel)
.split('$style$').join(s.style)
.split('$class$').join(s.class)
.split('$required$').join(required)
);
$compile(element)(element.scope());
}
}
})
.directive('dynamicImage', function($compile, $translate) {
var template = '';
return {
restrict: 'A',
scope: true,
require: 'ngModel',
link: function(scope, element, attr) {
var required = (attr.ngRequired && attr.ngRequired == "true"?"required":"");
var content = element.html();
const objectFit = attr.objectFit || 'unset';
if (objectFit === 'scale-down') {
const parentDynamicImageDiv = element.closest('[data-component="crn-dynamic-image"]');
if (parentDynamicImageDiv.length > 0) {
parentDynamicImageDiv.css('display', 'inline-block');
}
}
let templateDyn =`<div ngf-drop="" ngf-drag-over-class="dragover" style="display: flex; justify-content: center; width: 100%; height: 100%; position: relative; ">
<img alt="$picture$" style="width: 100%; object-fit: ${objectFit}" ng-if="$ngModel$"
data-ng-src="{{$ngModel$.startsWith(\'http\') || ($ngModel$.startsWith(\'/\') && $ngModel$.length < 1000)? $ngModel$ : \'data:image/png;base64,\' + $ngModel$}}">
<input id="$id$" aria-label="$userHtml$" ng-if="!$ngModel$" autocomplete="off" tabindex="-1"
class="uiSelectRequired ui-select-offscreen"
style="top: inherit !important; margin-left: 85px !important;margin-top: 50px !important; display: none;" type=text
ng-model="$ngModel$" $required$>
<button id="$idbutton$" class="btn" ng-if="!$ngModel$" ngf-drop="" ngf-select=""
ngf-change="cronapi.internal.setFile(\'$ngModel$\', $file)" ngf-pattern="\'image/*\'" ngf-max-size="$maxFileSize$">
$userHtml$
</button>
<button class="remove-image-button btn btn-danger btn-xs" ng-if="$ngModel$" ng-click="$ngModel$=null">
<span class="glyphicon glyphicon-remove"></span>
<span class="sr-only">{{"Remove" | translate}}</span>
</button>
<button class="btn btn-info btn-xs start-camera-button-attribute" ng-if="!$ngModel$"
ng-click="cronapi.internal.startCamera(\'$ngModel$\')">
<span class="glyphicon glyphicon-facetime-video"></span>
<span class="sr-only">{{"Upload.camera" | translate}}</span>
</button>
</div>`;
var maxFileSize = "";
if (attr.maxFileSize)
maxFileSize = attr.maxFileSize;
var imgAltText = "";
attr.imgAltText ? imgAltText = attr.imgAltText : imgAltText = "Admin.view.Picture";
templateDyn = $(templateDyn
.split('$id$').join(attr.id?attr.id+"-input":"textinput-picture")
.split('$idbutton$').join(attr.id?attr.id+"-button":"textinput-picture-button")
.split('$ngModel$').join(attr.ngModel)
.split('$required$').join(required)
.split('$userHtml$').join(content)
.split('$maxFileSize$').join(maxFileSize)
.split('$picture$').join($translate.instant(imgAltText))
);
var readOnly = attr.readOnly == 'true';
if (readOnly) {
templateDyn.find('.remove-image-button').remove();
templateDyn.find('.start-camera-button-attribute').remove();
}
element.html(templateDyn);
$compile(templateDyn)(element.scope());
}
}
})
.directive('dynamicFile', function($compile, $translate) {
var template = '';
return {
restrict: 'A',
scope: true,
require: 'ngModel',
link: function(scope, element, attr) {
var s = scope;
var required = (attr.ngRequired && attr.ngRequired == "true"?"required":"");
var splitedNgModel = attr.ngModel.split('.');
var datasource = splitedNgModel[0];
var field = splitedNgModel[splitedNgModel.length-1];
var number = Math.floor((Math.random() * 1000) + 20);
var content = element.html();
var maxFileSize = "";
var acceptFile = attr.acceptFile || "'*.*'";
if (attr.maxFileSize)
maxFileSize = attr.maxFileSize;
let fileInfo = attr.fileInfo ? `'${attr.fileInfo}'`: 'undefined';
var templateDyn = '\
<div ng-show="!$ngModel$" ngf-drop="" ngf-drag-over-class="dragover">\
<input id="$id$" aria-label="$userHtml$" ng-if="!$ngModel$" autocomplete="off" tabindex="-1" class="uiSelectRequired ui-select-offscreen" style="top: inherit !important;margin-left: 85px !important;margin-top: 50px !important; display: none;" type=text ng-model="$ngModel$" $required$>\
<button id="$idbutton$" class="btn" ngf-drop="" ngf-select="" ngf-change="cronapi.internal.uploadFile(\'$ngModel$\', $file, \'uploadprogress$number$\', $fileInfo$, $invalidFiles)" ngf-max-size="$maxFileSize$" ngf-pattern="$acceptPattern$" ngf-accept="$acceptFile$">\
$userHtml$\
</button>\
<div class="progress" data-type="bootstrapProgress" id="uploadprogress$number$" style="display:none">\
<div class="progress-bar" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" style="width:0%">\
<span class="sr-only"></span>\
</div>\
</div>\
</div> \
<div ng-show="$ngModel$" class="upload-image-component-attribute"> \
<button class="btn btn-danger btn-xs ng-scope" style="float:right;" ng-if="$ngModel$" ng-click="$ngModel$=null"> \
<span class="glyphicon glyphicon-remove"></span> \
<span class="sr-only">{{"Remove" | translate}}</span> \
</button> \
<div> \
<div ng-bind-html="cronapi.internal.generatePreviewDescriptionByte($ngModel$, $fileInfo$)"></div> \
<a href="javascript:void(0)" ng-click="cronapi.internal.downloadFileEntity($datasource$,\'$field$\', undefined, $fileInfo$)">$lblDownload$</a> \
</div> \
</div> \
';
templateDyn = $(templateDyn
.split('$id$').join(attr.id?attr.id+"-input":"textinput-file")
.split('$idbutton$').join(attr.id?attr.id+"-button":"textinput-file-button")
.split('$ngModel$').join(attr.ngModel)
.split('$datasource$').join(datasource)
.split('$field$').join(field)
.split('$number$').join(number)
.split('$required$').join(required)
.split('$userHtml$').join(content)
.split('$maxFileSize$').join(maxFileSize)
.split('$lblDownload$').join($translate.instant('download'))
.split('$fileInfo$').join(fileInfo)
.split('$acceptPattern$').join(acceptFile)
.split('$acceptFile$').join(acceptFile)
);
element.html(templateDyn);
$compile(templateDyn)(element.scope());
}
}
})
.directive('dynamicFile', function($compile) {
var template = '';
return {
restrict: 'E',
replace: true,
scope: {
ngModel: '@',
},
require: 'ngModel',
template: '<div></div>',
init: function(s) {
if (!s.ngModel)
s.ngModel = '';
},
link: function(scope, element, attr) {
this.init(scope);
var s = scope;
var required = (attr.ngRequired && attr.ngRequired == "true"?"required":"");
var splitedNgModel = s.ngModel.split('.');
var datasource = splitedNgModel[0];
var field = splitedNgModel[splitedNgModel.length-1];
var number = Math.floor((Math.random() * 1000) + 20);
var templateDyn = '\
<div ng-show="!$ngModel$">\
<input ng-if="!$ngModel$" autocomplete="off" tabindex="-1" class="uiSelectRequired ui-select-offscreen" style="top: inherit !important;margin-left: 85px !important;margin-top: 50px !important;" type=text ng-model="$ngModel$" $required$>\
<div class="form-group upload-image-component" ngf-drop="" ngf-drag-over-class="dragover"> \
<img class="ng-scope" style="height: 128px; width: 128px;" ng-if="!$ngModel$" data-ng-src="/plugins/cronapp-framework-js/img/selectFile.png" ngf-drop="" ngf-select="" ngf-change="cronapi.internal.uploadFile(\'$ngModel$\', $file, \'uploadprogress$number$\')" accept="*">\
<progress id="uploadprogress$number$" max="100" value="0" style="position: absolute; width: 128px; margin-top: -134px;">0</progress>\
</div>\
</div> \
<div ng-show="$ngModel$" class="form-group upload-image-component"> \
<div class="btn btn-danger btn-xs ng-scope" style="float:right;" ng-if="$ngModel$" ng-click="$ngModel$=null"> \
<span class="glyphicon glyphicon-remove"></span> \
</div> \
<div> \
<div ng-bind-html="cronapi.internal.generatePreviewDescriptionByte($ngModel$)"></div> \
<a href="javascript:void(0)" ng-click="cronapi.internal.downloadFileEntity($datasource$,\'$field$\')">download</a> \
</div> \
</div> \
';
element.append(templateDyn
.split('$ngModel$').join(s.ngModel)
.split('$datasource$').join(datasource)
.split('$field$').join(field)
.split('$number$').join(number)
.split('$required$').join(required)
);
$compile(element)(element.scope());
}
}
})
.directive('pwCheck', [function() {
'use strict';
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var firstPassword = '#' + attrs.pwCheck;
elem.add(firstPassword).on('keyup', function() {
scope.$apply(function() {
var v = elem.val() === $(firstPassword).val();
ctrl.$setValidity('pwmatch', v);
});
});
}
}
}])
.directive('ngClick', [function() {
'use strict';
return {
link: function(scope, elem, attrs, ctrl) {
if (scope.rowData) {
var crnDatasource = elem.closest('[crn-datasource]')
if (crnDatasource.length > 0) {
elem.on('click', function() {
scope.$apply(function() {
var datasource = eval(crnDatasource.attr('crn-datasource'));
datasource.active = scope.rowData;
});
});
}
}
}
}
}])
/**
* Validação de campos CPF e CNPJ,
* para utilizar essa diretiva, adicione o atributo valid com o valor
* do tipo da validação (cpf ou cnpj). Exemplo <input type="text" valid="cpf">
*/
.directive('valid', function() {
return {
require: '?ngModel',
restrict: 'A',
link: function(scope, element, attrs, ngModel) {
var validator = {
'cpf': CPF,
'cnpj': CNPJ
};
if (ngModel) {
ngModel.$validators[attrs.valid] = function(modelValue, viewValue) {
var value = modelValue || viewValue;
var fieldValid = validator[attrs.valid].isValid(value);
if (!fieldValid && value !== null) {
element.scope().$applyAsync(function(){ element[0].setCustomValidity(element[0].dataset['errorMessage']); }) ;
} else {
element[0].setCustomValidity("");
}
return (fieldValid || !value);
};
} else {
let validate = function() {
setTimeout(()=>{
var value = element.data('rawvalue');
var fieldValid = validator[attrs.valid].isValid(value);
if (!fieldValid && value !== null) {
element.addClass('k-invalid');
} else {
element.removeClass('k-invalid');
}
})
};
element.on('keydown', validate).on('keyup', validate);
}
}
}
})
.directive('cronappSecurity', function($rootScope) {
return {
restrict: 'A',
priority: Number.MIN_SAFE_INTEGER,
link: function(scope, element, attrs) {
if (attrs.cronappSecurity == '' || attrs.cronappSecurity == undefined || attrs.cronappSecurity == null) {
return;
}
var roles = [];
var user = JSON.parse(localStorage.getItem('_u'))
if (user && user.roles) {
roles = user.roles.toLowerCase().split(",");
}
var perms = parsePermission(attrs.cronappSecurity);
var show = false;
var enabled = false;
var render = false;
for (var i=0;i<roles.length;i++) {
var role = roles[i].trim();
if (role) {
if (perms.visible[role]) {
show = true;
}
if (perms.enabled[role]) {
enabled = true;
}
if (perms.render[role]) {
render = true;
}
}
}
for (var i=0;i<roles.length;i++) {
var role = roles[i].trim();
if (role) {
if (perms.notvisible[role]) {
show = false;
}
if (perms.notenabled[role]) {
enabled = false;
}
if (perms.notrender[role]) {
render = false;
}
}
}
let $element = $(element);
let applyPermission = () => {
if (!show) {
$element.hide();
}
if (!enabled) {
$element.find('*')
.addBack()
.css('pointer-events', 'none')
.attr('disabled', true)
.off('click')
.on('click', e => e.preventDefault());
}
if (!render) {
$element.remove();
}
};
let wait = setInterval(()=>{
if ($rootScope.renderFinished) {
applyPermission();
clearInterval(wait);
}
});
}
}
})
.directive('qr', ['$window', '$timeout', function($window, $timeout){
return {
restrict: 'A',
require: '^ngModel',
template: '<canvas ng-hide="image"></canvas><img ng-if="image" ng-src="{{canvasImage}}"/>',
link: function postlink(scope, element, attrs, ngModel){
if (scope.size === undefined && attrs.size) {
scope.text = attrs.size;
}
var getTypeNumeber = function(){
return scope.typeNumber || 0;
};
var getCorrection = function(){
var levels = {
'L': 1,
'M': 0,
'Q': 3,
'H': 2
};
var correctionLevel = scope.correctionLevel || 0;
return levels[correctionLevel] || 0;
};
var getText = function(){
return ngModel.$modelValue || "";
};
var getSize = function(){
let outerWidth = $(element).outerWidth();
let outerHeight = $(element).outerHeight();
let bestFit = outerWidth < outerHeight ? outerWidth : outerHeight;
return scope.size || bestFit || 100;
};
var isNUMBER = function(text){
var ALLOWEDCHARS = /^[0-9]*$/;
return ALLOWEDCHARS.test(text);
};
var isALPHA_NUM = function(text){
var ALLOWEDCHARS = /^[0-9A-Z $%*+\-./:]*$/;
return ALLOWEDCHARS.test(text);
};
var is8bit = function(text){
for (var i = 0; i < text.length; i++) {
var code = text.charCodeAt(i);
if (code > 255) {
return false;
}
}
return true;
};
var checkInputMode = function(inputMode, text){
if (inputMode === 'NUMBER' && !isNUMBER(text)) {
throw new Error('The `NUMBER` input mode is invalid for text.');
}
else if (inputMode === 'ALPHA_NUM' && !isALPHA_NUM(text)) {
throw new Error('The `ALPHA_NUM` input mode is invalid for text.');
}
else if (inputMode === '8bit' && !is8bit(text)) {
throw new Error('The `8bit` input mode is invalid for text.');
}
else if (!is8bit(text)) {
throw new Error('Input mode is invalid for text.');
}
return true;
};
var getInputMode = function(text){
var inputMode = scope.inputMode;
inputMode = inputMode || (isNUMBER(text) ? 'NUMBER' : undefined);
inputMode = inputMode || (isALPHA_NUM(text) ? 'ALPHA_NUM' : undefined);
inputMode = inputMode || (is8bit(text) ? '8bit' : '');
return checkInputMode(inputMode, text) ? inputMode : '';
};
var canvas = element.find('canvas')[0];
var canvas2D = !!$window.CanvasRenderingContext2D;
scope.TYPE_NUMBER = getTypeNumeber();
scope.TEXT = getText();
scope.CORRECTION = getCorrection();
scope.SIZE = getSize();
scope.INPUT_MODE = getInputMode(scope.TEXT);
scope.canvasImage = '';
var draw = function(context, qr, modules, tile){
for (var row = 0; row < modules; row++) {
for (var col = 0; col < modules; col++) {
var w = (Math.ceil((col + 1) * tile) - Math.floor(col * tile)),
h = (Math.ceil((row + 1) * tile) - Math.floor(row * tile));
context.fillStyle = qr.isDark(row, col) ? '#000' : '#fff';
context.fillRect(Math.round(col * tile), Math.round(row * tile), w, h);
}
}
};
var render = function(){
var trim = /^\s+|\s+$/g;
var text = scope.TEXT.replace(trim, '');
var qr = new QRCode(scope.TYPE_NUMBER, scope.CORRECTION, scope.INPUT_MODE);
qr.addData(text);
qr.make();
var context = canvas.getContext('2d');
var modules = qr.getModuleCount();
var tile = scope.SIZE / modules;
canvas.width = canvas.height = scope.SIZE;
if (canvas2D) {
draw(context, qr, modules, tile);
scope.canvasImage = canvas.toDataURL() || '';
}
};
scope.$watch(function(){return ngModel.$modelValue}, function(value, old){
if (value !== old || value !== scope.TEXT) {
scope.text = ngModel.$modelValue;
scope.TEXT = getText();
scope.INPUT_MODE = getInputMode(scope.TEXT);
$timeout(function() {
scope.SIZE = getSize();
render();
});
}
});
$timeout(function() {
scope.SIZE = getSize();
render();
});
}
};
}])
.directive('uiSelect', function ($compile) {
return {
restrict: 'E',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
let waitAngularReady = () => {
if (scope.$$phase !== '$apply' && scope.$$phase !== '$digest') {
element.find('i').remove();
}
else {
setTimeout( () => waitAngularReady(), 200);
}
};
waitAngularReady();
if (attrs.required != undefined || attrs.ngRequired === "true") {
$(element).append("<input autocomplete=\"off\" tabindex=\"-1\" class=\"uiSelectRequired ui-select-offscreen\" style=\"left: 50%!important; top: 100%!important;\" type=text ng-model=\""+attrs.ngModel+"\" required>");
var input = $(element).find("input.uiSelectRequired");
$compile(input)(element.scope());
}
}
};
})
.filter('raw',function($translate) {
return function(o) {
if (o != null && o !== undefined) {
if (typeof o == 'number') {
return o + "";
}
if (typeof o == 'boolean') {
return o + "";
}
if (o instanceof Date) {
return "datetimeoffset'" + o.toISOString() + "'";
}
else {
if (o.length >= 10 && o.match(ISO_PATTERN)) {
return "datetimeoffset'" + o + "'";
} else {
return "'" + o.replaceAll("'", "''") + "'";
}
}
} else {
return "";
}
}
})
.filter('js',function($translate) {
return function(o) {
if (o != null && o !== undefined) {
if (typeof o == 'number' || typeof o == 'boolean') {
return o + "";
}
if (o instanceof Date) {
return cronapi.toDate(o.toISOString());
}
else {
if (o.length >= 10 && o.match(ISO_PATTERN)) {
return cronapi.toDate(o);
} else {
return "'" + o + "'";
}
}
}
else {
return "undefined";
}
}
})
.filter('mask',function($translate) {
return function(value, maskValue, type) {
maskValue = parseMaskType(maskValue, $translate);
if (!maskValue)
return value;
var useUTC;
if (type !== undefined) {
useUTC = type == 'datetime' || type == 'time';
if (!window.fixedTimeZone) {
useUTC = false;
}
} else {
useUTC = window.fixedTimeZone;
}
if (maskValue.indexOf(";local") > 0) {
useUTC = false;
}
maskValue = maskValue.replace(';1', '').replace(';0', '').replace(';local', '').trim();
if ((typeof value == "string" && value.match(isoDate)) || value instanceof Date) {
if (useUTC) {
return moment(value).utcOffset(window.timeZoneOffset).format(maskValue);
} else {
return moment(value).format(maskValue);
}
} else if (typeof value == 'number') {
return format(maskValue, value);
} else if (value != undefined && value != null && value != "" && maskValue != '') {
var input = $("<input type=\"text\">");
input.mask(maskValue);
return input.masked(value);
} else {
return value;
}
};
})
.directive('screenParams', [function() {
'use strict';
return {
link: function(scope, elem, attrs, ctrl) {
var screenParams = eval(attrs.screenParams);
if (screenParams && screenParams.length) {
screenParams.forEach(function(screenParam) {
if (scope.params && !scope.params[screenParam.key])
scope.params[screenParam.key] = screenParam.value || '';
});
}
}
}
}])
.directive('screenVariables', [function() {
'use strict';
return {
link: async function(scope, elem, attrs, ctrl) {
let screenVariables = eval(attrs.screenVariables);
if (screenVariables && screenVariables.length) {
screenVariables.forEach(async (screenVariables) => {
if (scope.vars && !scope.vars[screenVariables.key]) {
let value = screenVariables.value || '';
if (screenVariables.type === 'blockly') {
value = await scope.$eval(value.replace('.run(','.toPromise().run('));
}
else if (screenVariables.type === 'expression') {
if (value.startsWith('params')) {
value = scope.params[value.replace('params.','')];
}
else if (value.startsWith('vars')) {
value = scope.vars[value.replace('vars.','')];
}
else {
value = await scope.$eval(value);
}
}
scope.vars[screenVariables.key] = value;
}
});
}
}
}
}])
.directive('mask', maskDirectiveMask)
.directive('cronappFilter', function($compile) {
return {
restrict: 'A',
require: '?ngModel',
setFilterInButton: function($element, bindedFilter, operator) {
var fieldset = $element.closest('fieldset');
if (!fieldset)
return;
var button = fieldset.find('button[cronapp-filter]');
if (!button)
return;
var filters = button.data('filters');
if (!filters)
filters = [];
var index = -1;
var ngModel = $element.attr('ng-model');
$(filters).each(function(idx) {
if (this.ngModel == ngModel)
index = idx;
});
if (index > -1)
filters.splice(index, 1);
if (bindedFilter.length > 0) {
var bindedFilterJson = {
"ngModel" : ngModel,
"bindedFilter" : bindedFilter
};
filters.push(bindedFilterJson);
}
button.data('filters', filters);
},
makeAutoPostSearch: function($element, bindedFilter, datasource, attrs) {
var fieldset = $element.closest('fieldset');
if (fieldset && fieldset.length > 0) {
var button = fieldset.find('button[cronapp-filter]');
if (button && button.length > 0) {
var filters = button.data('filters');
if (filters && filters.length > 0) {
bindedFilter = '';
$(filters).each(function() {
bindedFilter += this.bindedFilter+";";
});
}
}
}
datasource.search(bindedFilter, (attrs.cronappFilterCaseinsensitive=="true"));
},
inputBehavior: function(scope, element, attrs, ngModelCtrl, $element, typeElement, operator, autopost) {
var filterTemplate = '';
var filtersSplited = attrs.cronappFilter.split(';');
var datasource;
if (attrs.crnDatasource) {
datasource = eval(attrs.crnDatasource);
} else {
var fieldset = $element.closest('fieldset');
if (!fieldset)
return;
var button = fieldset.find('button[cronapp-filter]');
if (!button)
return;
if (!button.attr('crn-datasource')) {
return;
}
datasource = eval(button.attr('crn-datasource'));
}
var isOData = datasource.isOData()
$(filtersSplited).each(function() {
if (this.length > 0) {
if (filterTemplate != "") {
if (isOData) {
filterTemplate += " or ";
} else {
filterTemplate += ";";
}
}
if (isOData) {
if (operator == "=" && typeElement == 'text') {
filterTemplate += "substringof({value.lower}, tolower("+this+"))";
}
else if (operator == "=") {
filterTemplate += this + " eq {value}";
}
else if (operator == "!=") {
filterTemplate += this + " ne {value}";
}
else if (operator == ">") {
filterTemplate += this + " gt {value}";
}
else if (operator == ">=") {
filterTemplate += this + " ge {value}";
}
else if (operator == "<") {
filterTemplate += this + " lt {value}";
}
else if (operator == "<=") {
filterTemplate += this + " le {value}";
}
} else {
if (typeElement == 'text') {
filterTemplate += this + '@' + operator + '%{value}%';
} else {
filterTemplate += this + operator + '{value}';
}
}
}
});
if (filterTemplate.length == 0) {
if (isOData) {
filterTemplate = "{value}";
} else {
filterTemplate = '%{value}%';
}
}
var selfDirective = this;
if (ngModelCtrl) {
scope.$watch(attrs.ngModel, function(newVal, oldVal) {
if (angular.equals(newVal, oldVal)) { return; }
var eType = $element.data('type') || $element.attr('type');
var value = ngModelCtrl.$modelValue;
if (isOData) {
if (value instanceof Date) {
if (eType == "datetime-local") {
value = "datetimeoffset'" + value.toISOString() + "'";
} else {
value = "datetime'" + value.toISOString().substring(0, 23) + "'";
}
}
else if (typeof value == "number") {
value = value + "M";
}
else if (typeof value == "boolean") {
value = value;
} else {
value = "'" + value + "'";
}
} else {
if (value instanceof Date) {
value = value.toISOString();
if (eType == "date") {
value = value + "@@date";
}
else if (eType == "time" || eType == "time-local") {
value = value + "@@time";
}
else {
value = value + "@@datetime";
}
}
else if (typeof value == "number") {
value = value + "@@number";
}
else if (typeof value == "boolean") {
value = value + "@@boolean";
}
}
var bindedFilter = filterTemplate.split('{value}').join(value);
if (typeof value == 'string') {
if (bindedFilter.startsWith('substringof({')) {
var values = value.split("'").join("").toLowerCase().trim().split(' ');
var fulltextIndexFilter = '';
values.forEach(function(v, ix) {
fulltextIndexFilter += bindedFilter.split('{value.lower}').join("'" + v + "'");
if (ix < (values.length - 1)) {
fulltextIndexFilter += ' or ';
}
});
bindedFilter = fulltextIndexFilter;
}
else {
bindedFilter = bindedFilter.split('{value.lower}').join(value.toLowerCase());
}
} else {
bindedFilter = bindedFilter.split('{value.lower}').join(value);
}
if (ngModelCtrl.$viewValue.length == 0)
bindedFilter = '';
selfDirective.setFilterInButton($element, bindedFilter, operator);
if (autopost)
selfDirective.makeAutoPostSearch($element, bindedFilter, datasource, attrs);
});
}
else {
if (typeElement == 'text') {
$element.on("keyup", function() {
var datasource = eval(attrs.crnDatasource);
var value = undefined;
if (ngModelCtrl && ngModelCtrl != undefined)
value = ngModelCtrl.$viewValue;
else
value = this.value;
var bindedFilter = filterTemplate.split('{value}').join(value);
if (this.value.length == 0)
bindedFilter = '';
selfDirective.setFilterInButton($element, bindedFilter, operator);
if (autopost)
selfDirective.makeAutoPostSearch($element, bindedFilter, datasource, attrs);
});
}
else {
$element.on("change", function() {
var datasource = eval(attrs.crnDatasource);
var value = undefined;
var typeElement = $(this).attr('type');
if (attrs.asDate != undefined)
typeElement = 'date';
if (ngModelCtrl && ngModelCtrl != undefined) {
value = ngModelCtrl.$viewValue;
}
else {
if (typeElement == 'checkbox')
value = $(this).is(':checked');
else if (typeElement == 'date') {
value = this.value;
if (this.value.length > 0) {
var momentDate = moment(this.value, patternFormat(this));
value = momentDate.toDate().toISOString();
}
}
else
value = this.value;
}
var bindedFilter = filterTemplate.split('{value}').join(value);
if (value.toString().length == 0)
bindedFilter = '';
selfDirective.setFilterInButton($element, bindedFilter, operator);
if (autopost)
selfDirective.makeAutoPostSearch($element, bindedFilter, data