UNPKG

cronapp-framework-js

Version:
1,243 lines (1,146 loc) 320 kB
//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