cronapp-framework-mobile-js
Version:
Javascript library for CronApp's projects
1,351 lines (1,220 loc) • 103 kB
JavaScript
window.addEventListener('message', function(event) {
if (event.data == "reload") {
window.location.reload();
}
else if (event.data == "reload(true)") {
window.location.reload(true);
}
});
(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))/;
/**
* 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('updateLanguage', function ($rootScope) {
return {
link: function (scope, element) {
let listener = function (event, translationResp) {
let defaultLang = "en";
let currentLang = translationResp.language ? translationResp.language.split('_')[0] : null;
element.attr("lang", currentLang || defaultLang);
};
$rootScope.$on('$translateChangeSuccess', listener);
}
};
});
app.directive('asDate', maskDirectiveAsDate);
app.directive('input', transformText);
app.directive('textarea', transformText)
.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);
});
}
}
})
.filter('mask',function($translate) {
return function(value, maskValue, type) {
maskValue = parseMaskType(maskValue, $translate);
if (!maskValue)
return value;
var useUTC;
if (type !== undefined) {
useUTC = type == 'date' || 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('dynamicImage', function($compile, $translate) {
return {
restrict: 'A',
scope: true,
require: 'ngModel',
link: function(scope, element, attr) {
let templateDyn =
'<div ngf-drop="" ngf-drag-over-class="dragover" class="dynamicImage" >\
<img role="img" alt="$altText$" style="width: 100%;" ng-if="$ngModel$" data-ng-src="{{$ngModel$.startsWith(\'http\') || ($ngModel$.startsWith(\'/\') && $ngModel$.length < 1000)? $ngModel$ : \'data:image/png;base64,\' + $ngModel$}}">\
<div class="btn action-button-border" ng-if="!$ngModel$" ngf-drop="" ngf-select="" ngf-change="cronapi.internal.setFile(\'$ngModel$\', $file)" ngf-pattern="\'image/*\'" ngf-max-size="$maxFileSize$">\
$userHtml$\
</div>\
<div aria-label="$closeAriaText$" class="remove-image-button button button-assertive" ng-if="$ngModel$" ng-click="$ngModel$=null">\
<span class="icon ion-android-close"></span>\
</div>\
<div aria-label="$videocamAriaText$" class="button button-default buttomImage" ng-if="!$ngModel$" ng-click="cronapi.internal.startCamera(\'$ngModel$\',\'$quality$\',\'$allowEdit$\',\'$targetWidth$\',\'$targetHeight$\')">\
<span class="icon ion-ios-videocam"></span>\
</div>\
</div>';
const imageQuality = attr.quality || $(element).closest('[data-component="crn-dynamic-image"]').attr('quality') || '60';
const attributes = {
ngModel: attr.ngModel,
required: (attr.ngRequired && attr.ngRequired == "true")?"required":"",
content: element.html(),
altText: attr.alt ? attr.alt : $translate.instant('Users.view.Picture'),
closeAriaText: $translate.instant('Home.view.Close'),
videocamAriaText: $translate.instant('OpenCamera'),
maxFileSize: attr.maxFileSize ? attr.maxFileSize : "",
quality: imageQuality,
allowEdit: attr.allowEdit ? attr.allowEdit : "false",
targetWidth: attr.targetWidth ? attr.targetWidth : "640",
targetHeight: attr.targetHeight ? attr.targetHeight : "480"
};
templateDyn = $(templateDyn
.split('$ngModel$').join(attributes.ngModel)
.split('$required$').join(attributes.required)
.split('$userHtml$').join(attributes.content)
.split('$maxFileSize$').join(attributes.maxFileSize)
.split('$altText$').join(attributes.altText)
.split('$closeAriaText$').join(attributes.closeAriaText)
.split('$videocamAriaText$').join(attributes.videocamAriaText)
.split('$quality$').join(attributes.quality)
.split('$allowEdit$').join(attributes.allowEdit)
.split('$targetWidth$').join(attributes.targetWidth)
.split('$targetHeight$').join(attributes.targetHeight)
);
$(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 closeAriaText = $translate.instant('Home.view.Close');
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;
var fileInfo = attr.fileInfo ? `'${attr.fileInfo}'`: 'undefined';
var templateDyn = '\
<div ng-show="!$ngModel$" ngf-drop="" ngf-drag-over-class="dragover">\
<div class="btn action-button-border" ngf-drop="" ngf-select="" ngf-change="cronapi.internal.uploadFile(\'$ngModel$\', $file, \'uploadprogress$number$\', $fileInfo$)" ngf-max-size="$maxFileSize$" ngf-pattern="$acceptPattern$" ngf-accept="$acceptFile$">\
$userHtml$\
</div>\
<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"> \
<div aria-label="$closeAriaText$" class="button button-assertive" style="float:right;" ng-if="$ngModel$" ng-click="$ngModel$=null"> \
<span role="img" alt="$closeAriaText$" class="icon ion-android-close"></span> \
</div> \
<div> \
<div ng-bind-html="cronapi.internal.generatePreviewDescriptionByte($ngModel$, $fileInfo$)"></div> \
<div aria-label="Download" class="button button-positive" ng-click="cronapi.internal.downloadFileEntityMobile($datasource$,\'$field$\', undefined, $fileInfo$)">$lblDownload$</div> \
</div> \
</div> \
';
templateDyn = $(templateDyn
.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('$closeAriaText$').join(closeAriaText)
.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('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('qr', ['$window', function($window){
return {
restrict: 'EA',
require: '^ngModel',
template: '<canvas ng-hide="image"></canvas><img alt="qr-code" 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(){
return scope.size || $(element).outerWidth();
};
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(canvas, value, typeNumber, correction, size, inputMode){
var trim = /^\s+|\s+$/g;
var text = value.replace(trim, '');
var qr = new QRCode(typeNumber, correction, inputMode);
qr.addData(text);
qr.make();
var context = canvas.getContext('2d');
var modules = qr.getModuleCount();
var tile = size / modules;
canvas.width = canvas.height = 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);
render(canvas, scope.TEXT, scope.TYPE_NUMBER, scope.CORRECTION, scope.SIZE, scope.INPUT_MODE);
}
});
render(canvas, scope.TEXT, scope.TYPE_NUMBER, scope.CORRECTION, scope.SIZE, scope.INPUT_MODE);
}};
}])
/**
* 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
};
ngModel.$validators[attrs.valid] = function(modelValue, viewValue) {
var value = modelValue || viewValue;
var fieldValid = validator[attrs.valid].isValid(value);
if (!fieldValid) {
element.scope().$applyAsync(function(){ element[0].setCustomValidity(element[0].dataset['errorMessage']); }) ;
} else {
element[0].setCustomValidity("");
}
return (fieldValid || !value);
};
}
}
})
.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().attr('disabled', true).off('click').on('click', e => e.preventDefault());
}
if (!render) {
$element.remove();
}
};
let wait = setInterval(()=>{
if ($rootScope.$$phase !== '$apply' && $rootScope.$$phase !== '$digest') {
applyPermission();
clearInterval(wait);
}
});
}
}
})
.directive('cronappStars', [function() {
'use strict';
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs, ngModelCtrl) {
var $elem = $(elem);
var $star = $('<i style="font-size: 200%" class="component-holder ion ion-android-star-outline" style="" xattr-size="" data-component="crn-icon"></i>' );
$elem.html("");
var stars = [];
for (var i=1;i<=5;i++) {
var clonned = $star.clone();
$elem.append(clonned);
clonned.attr("idx", i);
clonned.click(function() {
scope.$apply(function() {
ngModelCtrl.$viewValue = parseInt($(this).attr("idx")); //set new view value
ngModelCtrl.$commitViewValue();
}.bind(this));
});
stars.push(clonned);
}
function changeStars(value) {
for (var i=1;i<=5;i++) {
stars[i-1].removeClass('ion-android-star-outline');
stars[i-1].removeClass('ion-android-star');
if (i <= value) {
stars[i-1].addClass('ion-android-star');
} else {
stars[i-1].addClass('ion-android-star-outline');
}
}
return value;
}
ngModelCtrl.$parsers.push(changeStars);
ngModelCtrl.$formatters.push(changeStars);
}
}
}])
.directive('cronappRating', [function() {
'use strict';
return {
restrict: 'E',
require: 'ngModel',
link: function(scope, elem, attrs, ngModelCtrl) {
attrs.theme = $(elem).find('i').attr('xattr-theme');
attrs.iconOn = $(elem).find('i').attr('class');
attrs.iconSize = $(elem).find('i').attr('icon-size');
var $elem = $(elem);
var starArray = []
if(attrs.xattrDefaultValue){
ngModelCtrl.$viewValue = 0; //set new view value
ngModelCtrl.$commitViewValue();
}
for (var i=1;i<=5;i++) {
starArray.push($(elem).find('i').get(i - 1));
$(starArray[i-1]).addClass(attrs.iconOff || "ion ion-android-star-outline");
}
$elem.html("");
var stars = [];
for (var i=1;i<=5;i++) {
var clonned = $(starArray[i-1]).clone();
$elem.append(clonned);
clonned.attr("idx", i);
clonned.click(function() {
scope.$apply(function() {
let idx = parseInt($(this).attr("idx"));
//set new view value or reset the value
ngModelCtrl.$viewValue = ( ngModelCtrl.$viewValue !== idx ) ? idx : 0;
ngModelCtrl.$commitViewValue();
}.bind(this));
});
stars.push(clonned);
}
function changeStars(value) {
for (var i=1;i<=5;i++) {
stars[i-1].removeClass(attrs.iconOff || "ion ion-android-star-outline");
stars[i-1].removeClass(attrs.iconOn);
stars[i-1].removeClass(attrs.theme);
if (i <= value) {
stars[i-1].addClass(attrs.iconOn);
stars[i-1].addClass(attrs.theme);
} else {
stars[i-1].addClass(attrs.iconOff || "ion ion-android-star-outline");
stars[i-1].addClass(attrs.theme);
stars[i-1].addClass(attrs.iconSize);
}
}
return value;
}
ngModelCtrl.$parsers.push(changeStars);
ngModelCtrl.$formatters.push(changeStars);
}
}
}])
.directive('ngInitialValue', function($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
if (attrs.ngInitialValue) {
var modelGetter = $parse(attrs['ngModel']);
var modelSetter = modelGetter.assign;
var evaluated;
try {
evaluated = scope.$eval(attrs.ngInitialValue);
} catch (e) {
evaluated = attrs.ngInitialValue;
}
// verifica se é um checkbox para transformar para um valor booleano
if (element[0].type == 'checkbox' && evaluated) {
evaluated = ('' + evaluated).toLowerCase() == 'true';
}
modelSetter(scope, evaluated);
}
}
}
})
.directive('crnAllowNullValues', [function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, el, attrs, ctrl) {
ctrl.$formatters = [];
ctrl.$parsers = [];
let falseValue = attrs.ngFalseValue ? attrs.ngFalseValue.split("'").join("") : false;
let trueValue = attrs.ngTrueValue ? attrs.ngTrueValue.split("'").join("") : true;
let isAllowNullValues = (value) => {
return value === 'true' || value === undefined
|| value === null || value.length == 0;
};
if (isAllowNullValues(attrs.crnAllowNullValues)) {
ctrl.$render = function () {
let viewValue = ctrl.$viewValue;
el.data('checked', viewValue);
switch (viewValue) {
case true:
case trueValue:
el.prop('indeterminate', false);
setTimeout(() => el.prop('checked', true));
viewValue = trueValue;
break;
case false:
case falseValue:
el.prop('indeterminate', false);
setTimeout(() => el.prop('checked', false));
viewValue = falseValue;
break;
default:
viewValue = null;
el.prop('indeterminate', true);
}
setTimeout(() => ctrl.$setViewValue(viewValue));
};
} else {
ctrl.$render = function () {
let viewValue = ctrl.$viewValue;
if (ctrl.$viewValue === undefined || ctrl.$viewValue === null) {
ctrl.$setViewValue(false);
viewValue = false;
}
if (viewValue === falseValue) {
let modelForEval = attrs.ngFalseValue ? `${el.attr('ng-model')}=${attrs.ngFalseValue}` : `${el.attr('ng-model')}=${falseValue}`;
scope.$eval(modelForEval);
}
el.data('checked', viewValue);
switch (viewValue) {
case true:
case trueValue:
el.prop('indeterminate', false);
setTimeout(() => el.prop('checked', true));
viewValue = trueValue;
break;
default:
el.prop('indeterminate', false);
setTimeout(() => el.prop('checked', false));
viewValue = falseValue;
break;
}
setTimeout(() => ctrl.$setViewValue(viewValue));
};
}
el.bind('click', function (e) {
let checked;
switch (el.data('checked')) {
case false:
case falseValue:
checked = attrs.ngTrueValue ? trueValue : true;
break;
default:
checked = attrs.ngFalseValue ? falseValue : false;
}
ctrl.$setViewValue(checked);
scope.$apply(ctrl.$render);
e.preventDefault();
});
}
};
}])
.directive('cronappFilter', function($compile) {
var setFilterInButton = function($element, bindedFilter, operator) {
var fieldset = $element.closest('div');
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);
}
var makeAutoPostSearch = function($element, bindedFilter, datasource, attrs) {
var fieldset = $element.closest('div');
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"));
}
var 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('div');
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 == "") {
filterTemplate = "substringof({value.lower}, tolower("+this+"))";
}
else if (operator == "=") {
filterTemplate += " substringof({value.lower},tolower("+this+"))";
}
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}%';
}
}
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;
}
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') {
bindedFilter = bindedFilter.split('{value.lower}').join(value.toLowerCase());
} else {
bindedFilter = bindedFilter.split('{value.lower}').join(value);
}
if (ngModelCtrl.$viewValue.length == 0)
bindedFilter = '';
setFilterInButton($element, bindedFilter, operator);
if (autopost)
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 = '';
setFilterInButton($element, bindedFilter, operator);
if (autopost)
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 = '';
setFilterInButton($element, bindedFilter, operator);
if (autopost)
makeAutoPostSearch($element, bindedFilter, datasource, attrs);
});
}
}
}
var forceDisableDatasource = function(datasourceName, scope) {
var disableDatasource = setInterval(function() {
try {
var datasourceInstance = eval(datasourceName);
if (datasourceInstance) {
$(document).ready(function() {
var time = 0;
var intervalForceDisable = setInterval(function() {
if (time < 10) {
scope.$apply(function () {
datasourceInstance.enabled = false;
datasourceInstance.data = [];
});
time++;
}
else
clearInterval(intervalForceDisable);
}, 20);
});
clearInterval(disableDatasource);
}
}
catch(e) {
//try again, until render
}
},10);
}
var buttonBehavior = function(scope, element, attrs, ngModelCtrl, $element, typeElement, operator, autopost) {
var datasourceName = '';
if (attrs.crnDatasource)
datasourceName = attrs.crnDatasource;
else
datasourceName = $element.parent().attr('crn-datasource');
var datasource = eval(datasourceName);
var isOData = datasource.isOData()
var requiredFilter = attrs.requiredFilter && attrs.requiredFilter.toString() == "true";
if (requiredFilter) {
this.forceDisableDatasource(datasourceName, scope);
}
$element.on('click', function() {
var $this = $(this);
var filters = $this.data('filters');
if (datasourceName && datasourceName.length > 0 && filters) {
var bindedFilter = '';
$(filters).each(function() {
if (bindedFilter != '') {
bindedFilter += (isOData?" and ":";");
}
bindedFilter += this.bindedFilter;
});
var datasourceToFilter = eval(datasourceName);
if (requiredFilter) {
datasourceToFilter.enabled = bindedFilter.length > 0;
if (datasourceToFilter.enabled) {
datasourceToFilter.search(bindedFilter, (attrs.cronappFilterCaseinsensitive=="true"));
}
else {
scope.$apply(function () {
datasourceToFilter.data = [];
});
}
}
else
datasourceToFilter.search(bindedFilter, (attrs.cronappFilterCaseinsensitive=="true"));
}
});
}
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
var $element = $(element);
var typeElement = $element.data('type') || $element.attr('type');
if (attrs.asDate != undefined)
typeElement = 'date';
var operator = '=';
if (attrs.cronappFilterOperator && attrs.cronappFilterOperator.length > 0)
operator = attrs.cronappFilterOperator;
var autopost = true;
if (attrs.cronappFilterAutopost && attrs.cronappFilterAutopost == "false")
autopost = false;
//Correção para aceitar datasources fora de ordem
setTimeout(function() {
if ($element[0].tagName == "INPUT")
inputBehavior(scope, element, attrs, ngModelCtrl, $element, typeElement, operator, autopost);
else
buttonBehavior(scope, element, attrs, ngModelCtrl, $element, typeElement, operator, autopost);
}, 100);
}
}
})
.directive('cronList', ['$compile', '$parse', function($compile, $parse){
'use strict';
const defaultAdvancedTemplate =
`<ion-list type="" can-swipe="listCanSwipe">
<ion-item ng-class="{'cron-list-selected' : isChecked(rowData)}" class="item {{options.editableButtonClass}} {{options.iconClassPosition}} {{options.imageClassPosition}}" ng-repeat="rowData in datasource">
<ul ng-if="options.allowMultiselect" class="checkbox-group component-holder {{'cron-list-multiselect-' + options.imageType}}" data-component="crn-checkbox"><label class="checkbox"><input ng-checked="isChecked(rowData);" type="checkbox"></label></ul>
<img alt='Thumbnail' ng-if="options.imageType !== 'do-not-show' && rowData[options.fields.image]"
ng-src="{{options.isImageFromDropbox ? '' : 'data:image/png;base64,'}}{{rowData[options.fields.image]}}" class="{{options.imageToClassPosition}}">
<div class="{{options.xattrTextPosition}} {{options.textToClassPosition}}">
<h2 ng-if="rowData[options.fields.field0]">{{rowData[options.fields.field0]|mask:options.fields.mask0:options.fields.type0}}</h2>
<p class="dark" ng-if="hasValue(rowData[options.fields.field1])">{{rowData[options.fields.field1]|mask:options.fields.mask1:options.fields.type1}}</p>
<p class="dark" ng-if="hasValue(rowData[options.fields.field2])">{{rowData[options.fields.field2]|mask:options.fields.mask2:options.fields.type2}}</p>
<p class="dark" ng-if="hasValue(rowData[options.fields.field3])">{{rowData[options.fields.field3]|mask:options.fields.mask3:options.fields.type3}}</p>
<p class="dark" ng-if="hasValue(rowData[options.fields.field4])">{{rowData[options.fields.field4]|mask:options.fields.mask4:options.fields.type4}}</p>
<p class="dark" ng-if="hasValue(rowData[options.fields.field5])">{{rowData[options.fields.field5]|mask:options.fields.mask5:options.fields.type5}}</p>
<i ng-if="options.icon" class="{{options.icon}}" xattr-theme="dark"></i>
</div>
</ion-item>
</ion-list>
<ion-infinite-scroll></ion-infinite-scroll>`;
const defaultSearchTemplate =
"<div class=\"item item-input-inset\">\n" +
" <div class=\"item-input-wrapper\">\n" +
" <i class=\"icon ion-search placeholder-icon\"></i>\n" +
" <input aria-label=\"{{'template.crud.search' | translate}}\" type=\"text\" ng-model=\"vars.searchableList[options.randomModel]\" cronapp-filter=\"{{options.filterFields}}\" cronapp-filter-operator=\"\" cronapp-filter-caseinsensitive=\"false\"\n" +
" cronapp-filter-autopost=\"true\" crn-datasource=\"{{options.dataSourceScreen.name}}\" placeholder=\"{{\'template.crud.search\' | translate}}\">\n" +
" </div>\n" +
" <button ng-if=\"showButton()\" ng-click=\"limparSelecao()\"\n" +
" class=\"button-small cron-list-button-clean button button-inline button-positive component-holder\">\n" +
" <span cron-list-button-text>Limpar Seleção</span></button>\n" +
"</div>";
var getExpression = function(dataSourceName) {
return 'rowData in '.concat(dataSourceName).concat('.data');
}
var buildFormat = function(column) {
var result = '';
result = ' | mask: "' + column.type + '"';
if(column.format){
result = ' | mask: "' + column.format + '":"'+column.type+'"';
}
return result;
}
var getEditCommand = function(dataSourceName) {
return dataSourceName + '.startEditing(rowData)';
}
var getCronappSecurity = function(column) {
if (column.security)
return `cronapp-security="${column.security}"`;
return "";
}
var addDefaultButton = function(dataSourceName, column) {
const EDIT_TEMPLATE = '<ion-option-button ' + getCronappSecurity(column) + ' class="button-positive ion-edit button-edit" ng-click="' + getEditCommand(dataSourceName) + '"><span> {{"Permission.view.Edit" | translate}}</span></ion-option-button>';
const DELETE_TEMPLATE = '<ion-option-button ' + getCronappSecurity(column) + ' class="button-assertive ion-trash-a button-delete" ng-click="' + dataSourceName + '.remove(rowData)"><span> {{"Permission.view.Remove" | translate}}</span></ion-option-button>';
if (column.command == 'edit|destroy') {
return EDIT_TEMPLATE.concat(DELETE_TEMPLATE);
} else if (column.command == 'edit') {
return EDIT_TEMPLATE;
} else if (column.command == 'destroy') {
return DELETE_TEMPLATE;
}
}
var encodeHTML = function(value) {
return value.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
var generateBlocklyCall = function(blocklyInfo) {
var call;
if (blocklyInfo.type == "client") {
var splitedClass = blocklyInfo.blocklyClass.split('/');
var blocklyName = splitedClass[splitedClass.length-1];
call = "blockly.js.blockly." + blocklyName;
call += "." + blocklyInfo.blocklyMethod;
var params = "()";
if (blocklyInfo.blocklyParams.length > 0) {
params = "(";
blocklyInfo.blocklyParams.forEach(function(p) {
params += (p.value ? encodeHTML(p.value) : "''") + ",";
}.bind(this))
params = params.substr(0, params.length - 1);
params += ")";
}
call += params;
} else if (blocklyInfo.type == "server") {
var blocklyName = blocklyInfo.blocklyClass + ':' + blocklyInfo.blocklyMethod;
call = "cronapi.util.makeCallServerBlocklyAsync('"+blocklyName+"',null,null,";
if (blocklyInfo.blocklyParams.length > 0) {
blocklyInfo.blocklyParams.forEach(function(p) {
call += (p.value ? encodeHTML(p.value) : "''") + ",";
}.bind(this))
}
call = call.substr(0, call.length - 1);
call += ")";
}
return call;
}
var addBlockly = function(column) {
return '<ion-option-button class="button-dark ' + getCronappSecurity(column) + ' ion-navigate" ng-click="'
+ generateBlocklyCall(column.blocklyInfo)
+ '"></ion-option-button>';
}
var isImage = function(fieldName, schemaFields) {
for (var i = 0; i < schemaFields.length; i++) {
var field = schemaFields[i];
if (fieldName == field.name) {
return (field.type == 'Binary');
}
}
return false;
}
var addCustomButton = function(column) {
return `<ion-option-button ${getCronappSecurity(column)} class="button-dark ${column.iconClass}" ng-click="listButtonClick($index, rowData, '${window.stringToJs(column.execute)}', $event)">${column.label}</ion-option-button> `
}
return {
restrict: 'E',
require: '?ngModel',
scope: true,
priority: 9999998,
terminal: true,
link: function(scope, element, attrs, ngModelCtrl) {
scope.hasValue = value => value !== null && value !== undefined;
var optionsList = {};
var dataSourceName = '';
var buttons = '';
try {
optionsList = JSON.parse(attrs.options);
dataSourceName = optionsList.dataSourceScreen.name;
var dataSource = eval(optionsList.dataSourceScreen.name);
var imageDirection = optionsList.imagePosition ? optionsList.imagePosition : "left";
var iconDirection = optionsList.iconPosition ? optionsList.iconPosition : "right";
var bothDirection = imageDirection === 'left' && iconDirection === 'left' ? 'left' : (imageDirection === 'right' && iconDirection === 'right' ? 'right' : '');
var checkboxTemplate = '';
var modelArrayToInsert = [];
var isKey = false;
scope.options = optionsList;
if(attrs['ngModel']){
var modelGetter = $parse(attrs['ngModel']);
var modelSetter = modelGetter.assign;
if(optionsList.allowMultiselect){
scope.verifyIsKey = function(rowData){
isKey = false;
if(optionsList.fieldType && optionsList.fieldType === "key"){
rowData = this.changeRowDataField(rowData);
isKey = true;
}
return rowData;
}
scope.limparSelecao = function(){
modelSetter(scope, []);
}
scope.isChecked = function(rowData) {
let hasObject = false;
modelArrayToInsert = modelGetter(scope);
rowData = scope.verifyIsKey(rowData);
hasObject = scope.hasObjectChecked(isKey, rowData, null, event);
scope.isSelected = hasObject;
return hasObject;
}
scope.hasObjectChecked = function(isKey, rowData, fn, event){
let hasObject = false;
if(Array.isArray(modelArrayToInsert)){
if(isKey && typeof rowData !== "object"){
modelArrayToInsert.forEach((el, idx) => {
if(rowData === el){
hasObject = true;
}
});
}
else{
modelArrayToInsert.forEach((el, idx) => {
if(dataSource.objectIsEquals(rowData, el)){
hasObject = true;
}
});
}
}
return hasObject;
}
scope.checkboxButtonClick = function(idx, rowData, fn, event) {
let hasObject = false;
let currentTarget = $(event.currentTarget);
let checkedSize = currentTarget.find('input[type=checkbox]:checked').length;
modelArrayToInsert = modelGetter(scope);
if(!Array.isArray(modelArrayToInsert)){
modelArrayToInsert = [];
}
if(!$(event.target).is('input[type=checkbox]') && !fn){
if(checkedSize > 0){
currentTarget.find("input[type=checkbox]").prop('checked', false);
}
else{
currentTarget.find("input[type=checkbox]").prop('checked', true);
}
}
let currentCheckbox = $(event.currentTarget).find('input[typ