UNPKG

cronapp-framework-mobile-js

Version:
1,351 lines (1,220 loc) 103 kB
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>&nbsp;{{"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>&nbsp;{{"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, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&apos;'); } 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