(function() { 'use strict'; function maxValidator(ctrl, value, limit) { var max = parseFloat(limit); var validity = ctrl.$isEmpty(value) || isNaN(max)|| value <= max; ctrl.$setValidity('max', validity); return value; } function minValidator(ctrl, value, limit) { var min = parseFloat(limit); var validity = ctrl.$isEmpty(value) || isNaN(min) || value >= min; ctrl.$setValidity('min', validity); return value; } var cnpjPattern = new StringMask('00.000.000\/0000-00'); var cpfPattern = new StringMask('000.000.000-00'); function numberViewMask (decimals, decimalDelimiter, thousandsDelimiter) { var mask = '#' + thousandsDelimiter + '##0'; if(decimals > 0) { mask += decimalDelimiter; for (var i = 0; i < decimals; i++) { mask += '0'; } } return new StringMask(mask, { reverse:true }); } function numberModelMask (decimals) { var mask = '###0'; if(decimals > 0) { mask += '.'; for (var i = 0; i < decimals; i++) { mask += '0'; } } return new StringMask(mask, { reverse:true }); } function clearDelimitersAndLeadingZeros (value) { var cleanValue = value.replace(/^0*/, ''); cleanValue = cleanValue.replace(/[^0-9]/g, ''); return cleanValue; } function preparePercentageToFormatter (value, decimals) { return clearDelimitersAndLeadingZeros((parseFloat(value)*100).toFixed(decimals)); } function prepareNumberToFormatter (value, decimals) { return clearDelimitersAndLeadingZeros((parseFloat(value)).toFixed(decimals)); } function uiBrCpfMask() { function applyCpfMask (value) { if(!value) { return value; } var formatedValue = cpfPattern.apply(value); return formatedValue.trim().replace(/[^0-9]$/, ''); } return { restrict: 'A', require: '?ngModel', link: function (scope, element, attrs, ctrl) { if (!ctrl) { return; } ctrl.$formatters.push(function(value) { return applyCpfMask(value); }); ctrl.$parsers.push(function(value) { if(!value) { return value; } var actualNumber = value.replace(/[^\d]/g,''); var formatedValue = applyCpfMask(actualNumber); ctrl.$setValidity('cpf', BrV.cpf.validate(formatedValue)); if (ctrl.$viewValue !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } return formatedValue.replace(/[^\d]+/g,''); }); } }; } function uiBrCnpjMask() { function applyCnpjMask (value) { if(!value) { return value; } var formatedValue = cnpjPattern.apply(value); return formatedValue.trim().replace(/[^0-9]$/, ''); } return { restrict: 'A', require: '?ngModel', link: function (scope, element, attrs, ctrl) { if (!ctrl) { return; } ctrl.$formatters.push(function(value) { return applyCnpjMask(value); }); ctrl.$parsers.push(function(value) { if(!value) { return value; } var actualNumber = value.replace(/[^\d]+/g,''); var formatedValue = applyCnpjMask(actualNumber); ctrl.$setValidity('cnpj', BrV.cnpj.validate(formatedValue)); if (ctrl.$viewValue !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } return formatedValue.replace(/[^\d]+/g,''); }); } }; } function uiBrCpfCnpjMask() { function applyCpfCnpjMask (value) { if(!value) { return value; } var formatedValue; if (value.length > 11) { formatedValue = cnpjPattern.apply(value); } else { formatedValue = cpfPattern.apply(value); } return formatedValue.trim().replace(/[^0-9]$/, ''); } return { restrict: 'A', require: '?ngModel', link: function (scope, element, attrs, ctrl) { if (!ctrl) { return; } ctrl.$formatters.push(function(value) { return applyCpfCnpjMask(value); }); ctrl.$parsers.push(function(value) { if(!value) { return value; } var actualNumber = value.replace(/[^\d]+/g,''); var formatedValue = applyCpfCnpjMask(actualNumber); if (actualNumber.length > 11) { ctrl.$setValidity('cnpj', BrV.cnpj.validate(formatedValue)); ctrl.$setValidity('cpf', true); } else { ctrl.$setValidity('cpf', BrV.cpf.validate(formatedValue)); ctrl.$setValidity('cnpj', true); } if (ctrl.$viewValue !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } return formatedValue.replace(/[^\d]+/g,''); }); } }; } angular.module('ui.utils.masks', []) .directive('uiPercentageMask', ['$locale', function ($locale) { var decimalDelimiter = $locale.NUMBER_FORMATS.DECIMAL_SEP, thousandsDelimiter = $locale.NUMBER_FORMATS.GROUP_SEP; return { restrict: 'A', require: '?ngModel', scope: { min: '=?min', max: '=?max' }, link: function (scope, element, attrs, ctrl) { if (!ctrl) { return; } var decimals = parseInt(attrs.uiPercentageMask); if(isNaN(decimals)) { decimals = 2; } var numberDecimals = decimals + 2; var viewMask = numberViewMask(decimals, decimalDelimiter, thousandsDelimiter), modelMask = numberModelMask(numberDecimals); ctrl.$formatters.push(function(value) { if(!value) { return ' %'; } var valueToFormat = preparePercentageToFormatter(value, decimals); return viewMask.apply(valueToFormat) + ' %'; }); ctrl.$parsers.push(function(value) { function renderValue(formatedValue) { if (ctrl.$viewValue !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } } if(!value) { renderValue(' %'); return value; } var valueToFormat = clearDelimitersAndLeadingZeros(value); if(value && value.indexOf('%') < 0 && valueToFormat.length >= 1) { valueToFormat = valueToFormat.substr(0,valueToFormat.length-1); } var formatedValue = ' %'; var actualNumber; if (valueToFormat) { formatedValue = viewMask.apply(valueToFormat) + ' %'; actualNumber = parseFloat(modelMask.apply(valueToFormat)); } renderValue(formatedValue); return actualNumber; }); if(attrs.min){ ctrl.$parsers.push(function(value) { return minValidator(ctrl, value, scope.min); }); scope.$watch('min', function() { minValidator(ctrl, ctrl.$modelValue, scope.min); }); } if(attrs.max) { ctrl.$parsers.push(function(value) { return maxValidator(ctrl, value, scope.max); }); scope.$watch('max', function() { maxValidator(ctrl, ctrl.$modelValue, scope.max); }); } } }; }]) .directive('uiNumberMask', ['$locale', function ($locale) { var decimalDelimiter = $locale.NUMBER_FORMATS.DECIMAL_SEP, thousandsDelimiter = $locale.NUMBER_FORMATS.GROUP_SEP; return { restrict: 'A', require: '?ngModel', scope: { min: '=?min', max: '=?max' }, link: function (scope, element, attrs, ctrl) { if (!ctrl) { return; } var decimals = parseInt(attrs.uiNumberMask); if(isNaN(decimals)) { decimals = 2; } var viewMask = numberViewMask(decimals, decimalDelimiter, thousandsDelimiter), modelMask = numberModelMask(decimals); ctrl.$formatters.push(function(value) { if(!value) { return value; } var valueToFormat = prepareNumberToFormatter(value, decimals); return viewMask.apply(valueToFormat); }); ctrl.$parsers.push(function(value) { if(!value) { return value; } var valueToFormat = clearDelimitersAndLeadingZeros(value); var formatedValue = viewMask.apply(valueToFormat); var actualNumber = parseFloat(modelMask.apply(valueToFormat)); if(angular.isDefined(attrs.uiNegativeNumber)){ var isNegative = (value[0] === '-'), needsToInvertSign = (value.slice(-1) === '-'); //only apply the minus sign if is negative or(exclusive) needs to be negative if(needsToInvertSign ^ isNegative) { actualNumber *= -1; formatedValue = '-' + formatedValue; } } if (ctrl.$viewValue !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } return actualNumber; }); if(attrs.min){ ctrl.$parsers.push(function(value) { return minValidator(ctrl, value, scope.min); }); scope.$watch('min', function() { minValidator(ctrl, ctrl.$modelValue, scope.min); }); } if(attrs.max) { ctrl.$parsers.push(function(value) { return maxValidator(ctrl, value, scope.max); }); scope.$watch('max', function() { maxValidator(ctrl, ctrl.$modelValue, scope.max); }); } } }; }]) .directive('uiBrCpfMask', [uiBrCpfMask]) .directive('uiBrCnpjMask', [uiBrCnpjMask]) .directive('uiBrCpfcnpjMask', [uiBrCpfCnpjMask]) // deprecated: will be removed in the next major version .directive('uiCpfMask', [uiBrCpfMask]) // deprecated: will be removed in the next major version .directive('uiCnpjMask', [uiBrCnpjMask]) // deprecated: will be removed in the next major version .directive('uiCpfcnpjMask', [uiBrCpfCnpjMask]) .directive('uiMoneyMask', ['$locale', function ($locale) { var decimalDelimiter = $locale.NUMBER_FORMATS.DECIMAL_SEP; var thousandsDelimiter = $locale.NUMBER_FORMATS.GROUP_SEP; var currencySym = $locale.NUMBER_FORMATS.CURRENCY_SYM; return { restrict: 'A', require: '?ngModel', link: function (scope, element, attrs, ctrl) { if (!ctrl) { return; } var decimals = parseInt(attrs.uiMoneyMask); if(isNaN(decimals)) { decimals = 2; } var decimalsPattern = decimals > 0 ? decimalDelimiter + new Array(decimals + 1).join('0') : ''; var maskPattern = currencySym+' #'+thousandsDelimiter+'##0'+decimalsPattern; var moneyMask = new StringMask(maskPattern, {reverse: true}); ctrl.$formatters.push(function(value) { if(!value) { return value; } return moneyMask.apply(value.toFixed(decimals).replace(/[^\d]+/g,'')); }); ctrl.$parsers.push(function(value) { if (!value) { return value; } var actualNumber = value.replace(/[^\d]+/g,''); actualNumber = actualNumber.replace(/^[0]+([1-9])/,'$1'); var formatedValue = moneyMask.apply(actualNumber); if (value !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } return parseInt(formatedValue.replace(/[^\d]+/g,''))/Math.pow(10,decimals); }); } }; }]) .directive('uiBrPhoneNumber',function() { /** * FIXME: all numbers will have 9 digits after 2016. * see http://portal.embratel.com.br/embratel/9-digito/ */ var phoneMask8D = new StringMask('(00) 0000-0000'), phoneMask9D = new StringMask('(00) 00000-0000'); function clearValue (value) { if(!value) { return value; } return value.replace(/[^0-9]/g, ''); } function applyPhoneMask (value) { if(!value) { return value; } var formatedValue; if(value.length < 11){ formatedValue = phoneMask8D.apply(value); }else{ formatedValue = phoneMask9D.apply(value); } return formatedValue.trim().replace(/[^0-9]$/, ''); } return { restrict: 'A', require: '?ngModel', link: function(scope, element, attrs, ctrl) { if (!ctrl) { return; } ctrl.$formatters.push(function(value) { return applyPhoneMask(value); }); ctrl.$parsers.push(function(value) { if (!value) { return value; } var cleanValue = clearValue(value); var formatedValue = applyPhoneMask(cleanValue); if (ctrl.$viewValue !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } return clearValue(formatedValue); }); } }; }) .directive('uiBrCepMask',function() { var cepMask = new StringMask('00000-000'); function clearValue (value) { if(!value) { return value; } return value.replace(/[^0-9]/g, ''); } function applyCepMask (value, ctrl) { if(!value) { return value; } var processed = cepMask.process(value); ctrl.$setValidity('cep', processed.valid); var formatedValue = processed.result; return formatedValue.trim().replace(/[^0-9]$/, ''); } return { restrict: 'A', require: '?ngModel', link: function(scope, element, attrs, ctrl) { if (!ctrl) { return; } ctrl.$formatters.push(function(value) { return applyCepMask(value, ctrl); }); ctrl.$parsers.push(function(value) { if (!value) { return value; } var cleanValue = clearValue(value); var formatedValue = applyCepMask(cleanValue, ctrl); if (ctrl.$viewValue !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } return clearValue(formatedValue); }); } }; }) .directive('uiBrIeMask',function() { var ieMasks = { 'AC': [{mask: new StringMask('00.000.000/000-00')}], 'AL': [{mask: new StringMask('000000000')}], 'AM': [{mask: new StringMask('00.000.000-0')}], 'AP': [{mask: new StringMask('000000000')}], 'BA': [{chars: 8, mask: new StringMask('000000-00')}, {mask: new StringMask('0000000-00')}], 'CE': [{mask: new StringMask('00000000-0')}], 'DF': [{mask: new StringMask('00000000000-00')}], 'ES': [{mask: new StringMask('00000000-0')}], 'GO': [{mask: new StringMask('00.000.000-0')}], 'MA': [{mask: new StringMask('000000000')}], 'MG': [{mask: new StringMask('000.000.000/0000')}], 'MS': [{mask: new StringMask('000000000')}], 'MT': [{mask: new StringMask('0000000000-0')}], 'PA': [{mask: new StringMask('00-000000-0')}], 'PB': [{mask: new StringMask('00000000-0')}], 'PE': [{chars: 9, mask: new StringMask('0000000-00')}, {mask: new StringMask('00.0.000.0000000-0')}], 'PI': [{mask: new StringMask('000000000')}], 'PR': [{mask: new StringMask('000.00000-00')}], 'RJ': [{mask: new StringMask('00.000.00-0')}], 'RN': [{chars: 9, mask: new StringMask('00.000.000-0')}, {mask: new StringMask('00.0.000.000-0')}], 'RO': [{mask: new StringMask('0000000000000-0')}], 'RR': [{mask: new StringMask('00000000-0')}], 'RS': [{mask: new StringMask('000/0000000')}], 'SC': [{mask: new StringMask('000.000.000')}], 'SE': [{mask: new StringMask('00000000-0')}], 'SP': [{mask: new StringMask('000.000.000.000')}, {mask: new StringMask('-00000000.0/000')}], 'TO': [{mask: new StringMask('00000000000')}] }; function clearValue (value) { if(!value) { return value; } return value.replace(/[^0-9]/g, ''); } function getMask(uf, value) { if(!uf || !ieMasks[uf]) { return undefined; } var _uf = uf.toUpperCase(); if (_uf === 'SP' && /^P/i.test(value)) { return ieMasks.SP[1].mask; } var masks = ieMasks[uf]; var i = 0; while(masks[i].chars && masks[i].chars < clearValue(value).length && i < masks.length - 1) { i++; } return masks[i].mask; } function applyIEMask (value, uf, ctrl) { var mask = getMask(uf, value); if(!value || !mask) { return value; } var processed = mask.process(clearValue(value)); ctrl.$setValidity('ie', BrV.ie(uf).validate(value)); var formatedValue = processed.result; if (uf && uf.toUpperCase() === 'SP' && /^p/i.test(value)) { return 'P'+(formatedValue ? formatedValue.trim().replace(/[^0-9]$/, '') : ''); } return formatedValue.trim().replace(/[^0-9]$/, ''); } return { restrict: 'A', require: '?ngModel', scope: { state: '=uiBrIeMask' }, link: function(scope, element, attrs, ctrl) { if (!ctrl) { return; } scope.$watch('state', function(state) { applyIEMask(ctrl.$viewValue, state, ctrl); }); ctrl.$formatters.push(function(value) { return applyIEMask(value, scope.state, ctrl); }); ctrl.$parsers.push(function(value) { if (!value) { return value; } var formatedValue = applyIEMask(value, scope.state, ctrl); if (ctrl.$viewValue !== formatedValue) { ctrl.$setViewValue(formatedValue); ctrl.$render(); } if (scope.state && scope.state.toUpperCase() === 'SP' && /^p/i.test(value)) { return 'P'+clearValue(formatedValue); } return clearValue(formatedValue); }); } }; }); })();