ENDRPrint_12CRelease/web/js/mask/ngMask.js
2024-08-14 10:58:03 +07:00

718 lines
23 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function() {
'use strict';
angular.module('ngMask', []);
})();(function() {
'use strict';
angular.module('ngMask')
.directive('mask', ['$log', '$timeout', 'MaskService', function($log, $timeout, MaskService) {
return {
restrict: 'A',
require: 'ngModel',
compile: function($element, $attrs) { 
if (!$attrs.mask || !$attrs.ngModel) {
$log.info('Mask and ng-model attributes are required!');
return;
}
var maskService = MaskService.create();
var timeout;
var promise;
function setSelectionRange(selectionStart){
if (typeof selectionStart !== 'number') {
return;
}
// using $timeout:
// it should run after the DOM has been manipulated by Angular
// and after the browser renders (which may cause flicker in some cases)
$timeout.cancel(timeout);
timeout = $timeout(function(){
var selectionEnd = selectionStart + 1;
var input = $element[0];
if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(selectionStart, selectionEnd);
} else if (input.createTextRange) {
var range = input.createTextRange();
range.collapse(true);
range.moveEnd('character', selectionEnd);
range.moveStart('character', selectionStart);
range.select();
}
});
}
return {
pre: function($scope, $element, $attrs, controller) {
promise = maskService.generateRegex({
mask: $attrs.mask,
// repeat mask expression n times
repeat: ($attrs.repeat || $attrs.maskRepeat),
// clean model value - without divisors
clean: (($attrs.clean || $attrs.maskClean) === 'true'),
// limit length based on mask length
limit: (($attrs.limit || $attrs.maskLimit || 'true') === 'true'),
// how to act with a wrong value
restrict: ($attrs.restrict || $attrs.maskRestrict || 'select'), //select, reject, accept
// set validity mask
validate: (($attrs.validate || $attrs.maskValidate || 'true') === 'true'),
// default model value
model: $attrs.ngModel,
// default input value
value: $attrs.ngValue
});
},
post: function($scope, $element, $attrs, controller) {
promise.then(function() {
// get initial options
var options = maskService.getOptions();
function parseViewValue(value) {
// set default value equal 0
value = value || '';
// get view value object
var viewValue = maskService.getViewValue(value);
// get mask without question marks
var maskWithoutOptionals = options['maskWithoutOptionals'] || '';
// get view values capped
// used on view
var viewValueWithDivisors = viewValue.withDivisors(true);
// used on model
var viewValueWithoutDivisors = viewValue.withoutDivisors(true);
try {
// get current regex
var regex = maskService.getRegex(viewValueWithDivisors.length - 1);
var fullRegex = maskService.getRegex(maskWithoutOptionals.length - 1);
// current position is valid
var validCurrentPosition = regex.test(viewValueWithDivisors) || fullRegex.test(viewValueWithDivisors);
// difference means for select option
var diffValueAndViewValueLengthIsOne = (value.length - viewValueWithDivisors.length) === 1;
var diffMaskAndViewValueIsGreaterThanZero = (maskWithoutOptionals.length - viewValueWithDivisors.length) > 0;
if (options.restrict !== 'accept') {
if (options.restrict === 'select' && (!validCurrentPosition || diffValueAndViewValueLengthIsOne)) {
var lastCharInputed = value[(value.length-1)];
var lastCharGenerated = viewValueWithDivisors[(viewValueWithDivisors.length-1)];
if ((lastCharInputed !== lastCharGenerated) && diffMaskAndViewValueIsGreaterThanZero) {
viewValueWithDivisors = viewValueWithDivisors + lastCharInputed;
}
var wrongPosition = maskService.getFirstWrongPosition(viewValueWithDivisors);
if (angular.isDefined(wrongPosition)) {
setSelectionRange(wrongPosition);
}
} else if (options.restrict === 'reject' && !validCurrentPosition) {
viewValue = maskService.removeWrongPositions(viewValueWithDivisors);
viewValueWithDivisors = viewValue.withDivisors(true);
viewValueWithoutDivisors = viewValue.withoutDivisors(true);
// setSelectionRange(viewValueWithDivisors.length);
}
}
if (!options.limit) {
viewValueWithDivisors = viewValue.withDivisors(false);
viewValueWithoutDivisors = viewValue.withoutDivisors(false);
}
// Set validity
if (options.validate && controller.$dirty) {
if (fullRegex.test(viewValueWithDivisors) || controller.$isEmpty(controller.$modelValue)) {
controller.$setValidity('mask', true);
} else {
controller.$setValidity('mask', false);
}
}
// Update view and model values
if(value !== viewValueWithDivisors){
controller.$setViewValue(angular.copy(viewValueWithDivisors), 'input');
controller.$render();
}
} catch (e) {
$log.error('[mask - parseViewValue]');
throw e;
}
// Update model, can be different of view value
if (options.clean) {
return viewValueWithoutDivisors;
} else {
return viewValueWithDivisors;
}
}
controller.$parsers.push(parseViewValue);
$element.on('click input paste keyup', function() {
parseViewValue($element.val());
$scope.$apply();
});
// Register the watch to observe remote loading or promised data
// Deregister calling returned function
var watcher = $scope.$watch($scope.ngModel, function (newValue, oldValue) {
if (angular.isDefined(newValue)) {
parseViewValue(newValue);
watcher();
}
});
// $evalAsync from a directive
// it should run after the DOM has been manipulated by Angular
// but before the browser renders
if(options.value) {
$scope.$evalAsync(function($scope) {
controller.$setViewValue(angular.copy(options.value), 'input');
controller.$render();
});
}
});
}
}
}
}
}]);
})();(function() {
'use strict';
angular.module('ngMask')
.factory('MaskService', ['$q', 'OptionalService', 'UtilService', function($q, OptionalService, UtilService) {
function create() {
var options;
var maskWithoutOptionals;
var maskWithoutOptionalsLength = 0;
var maskWithoutOptionalsAndDivisorsLength = 0;
var optionalIndexes = [];
var optionalDivisors = {};
var optionalDivisorsCombinations = [];
var divisors = [];
var divisorElements = {};
var regex = [];
var patterns = {
'9': /[0-9]/,
'8': /[0-8]/,
'7': /[0-7]/,
'6': /[0-6]/,
'5': /[0-5]/,
'4': /[0-4]/,
'3': /[0-3]/,
'2': /[0-2]/,
'1': /[0-1]/,
'0': /[0]/,
'*': /./,
'w': /\w/,
'W': /\W/,
'd': /\d/,
'D': /\D/,
's': /\s/,
'S': /\S/,
'b': /\b/,
'A': /[A-Z]/,
'a': /[a-z]/,
'Z': /[A-ZÇÀÁÂÃÈÉÊẼÌÍÎĨÒÓÔÕÙÚÛŨ]/,
'z': /[a-zçáàãâéèêẽíìĩîóòôõúùũüû]/,
'@': /[a-zA-Z]/,
'#': /[a-zA-ZçáàãâéèêẽíìĩîóòôõúùũüûÇÀÁÂÃÈÉÊẼÌÍÎĨÒÓÔÕÙÚÛŨ]/,
'%': /[0-9a-zA-ZçáàãâéèêẽíìĩîóòôõúùũüûÇÀÁÂÃÈÉÊẼÌÍÎĨÒÓÔÕÙÚÛŨ]/
};
// REGEX
function generateIntermetiateElementRegex(i, forceOptional) {
var charRegex;
try {
var element = maskWithoutOptionals[i];
var elementRegex = patterns[element];
var hasOptional = isOptional(i);
if (elementRegex) {
charRegex = '(' + elementRegex.source + ')';
} else { // is a divisor
if (!isDivisor(i)) {
divisors.push(i);
divisorElements[i] = element;
}
charRegex = '(' + '\\' + element + ')';
}
} catch (e) {
throw e;
}
if (hasOptional || forceOptional) {
charRegex += '?';
}
return new RegExp(charRegex);
}
function generateIntermetiateRegex(i, forceOptional) {
var elementRegex
var elementOptionalRegex;
try {
var intermetiateElementRegex = generateIntermetiateElementRegex(i, forceOptional);
elementRegex = intermetiateElementRegex;
var hasOptional = isOptional(i);
var currentRegex = intermetiateElementRegex.source;
if (hasOptional && ((i+1) < maskWithoutOptionalsLength)) {
var intermetiateRegex = generateIntermetiateRegex((i+1), true).elementOptionalRegex();
currentRegex += intermetiateRegex.source;
}
elementOptionalRegex = new RegExp(currentRegex);
} catch (e) {
throw e;
}
return {
elementRegex: function() {
return elementRegex;
},
elementOptionalRegex: function() {
// from element regex, gets the flow of regex until first not optional
return elementOptionalRegex;
}
};
}
function generateRegex(opts) {
var deferred = $q.defer();
options = opts;
try {
var mask = opts['mask'];
var repeat = opts['repeat'];
if (repeat) {
mask = Array((parseInt(repeat)+1)).join(mask);
}
optionalIndexes = OptionalService.getOptionals(mask).fromMaskWithoutOptionals();
options['maskWithoutOptionals'] = maskWithoutOptionals = OptionalService.removeOptionals(mask);
maskWithoutOptionalsLength = maskWithoutOptionals.length;
var cumulativeRegex;
for (var i=0; i<maskWithoutOptionalsLength; i++) {
var charRegex = generateIntermetiateRegex(i);
var elementRegex = charRegex.elementRegex();
var elementOptionalRegex = charRegex.elementOptionalRegex();
var newRegex = cumulativeRegex ? cumulativeRegex.source + elementOptionalRegex.source : elementOptionalRegex.source;
newRegex = new RegExp(newRegex);
cumulativeRegex = cumulativeRegex ? cumulativeRegex.source + elementRegex.source : elementRegex.source;
cumulativeRegex = new RegExp(cumulativeRegex);
regex.push(newRegex);
}
generateOptionalDivisors();
maskWithoutOptionalsAndDivisorsLength = removeDivisors(maskWithoutOptionals).length;
deferred.resolve({
options: options,
divisors: divisors,
divisorElements: divisorElements,
optionalIndexes: optionalIndexes,
optionalDivisors: optionalDivisors,
optionalDivisorsCombinations: optionalDivisorsCombinations
});
} catch (e) {
deferred.reject(e);
throw e;
}
return deferred.promise;
}
function getRegex(index) {
var currentRegex;
try {
currentRegex = regex[index] ? regex[index].source : '';
} catch (e) {
throw e;
}
return (new RegExp('^' + currentRegex + '$'));
}
// DIVISOR
function isOptional(currentPos) {
return UtilService.inArray(currentPos, optionalIndexes);
}
function isDivisor(currentPos) {
return UtilService.inArray(currentPos, divisors);
}
function generateOptionalDivisors() {
function sortNumber(a,b) {
return a - b;
}
var sortedDivisors = divisors.sort(sortNumber);
var sortedOptionals = optionalIndexes.sort(sortNumber);
for (var i = 0; i<sortedDivisors.length; i++) {
var divisor = sortedDivisors[i];
for (var j = 1; j<=sortedOptionals.length; j++) {
var optional = sortedOptionals[(j-1)];
if (optional >= divisor) {
break;
}
if (optionalDivisors[divisor]) {
optionalDivisors[divisor] = optionalDivisors[divisor].concat(divisor-j);
} else {
optionalDivisors[divisor] = [(divisor-j)];
}
// get the original divisor for alternative divisor
divisorElements[(divisor-j)] = divisorElements[divisor];
}
}
}
function removeDivisors(value) {
try {
if (divisors.length > 0 && value) {
var keys = Object.keys(divisorElements);
var elments = [];
for (var i = keys.length - 1; i >= 0; i--) {
var divisor = divisorElements[keys[i]];
if (divisor) {
elments.push(divisor);
}
}
elments = UtilService.uniqueArray(elments);
// remove if it is not pattern
var regex = new RegExp(('[' + '\\' + elments.join('\\') + ']'), 'g');
return value.replace(regex, '');
} else {
return value;
}
} catch (e) {
throw e;
}
}
function insertDivisors(array, combination) {
function insert(array, output) {
var out = output;
for (var i=0; i<array.length; i++) {
var divisor = array[i];
if (divisor < out.length) {
out.splice(divisor, 0, divisorElements[divisor]);
}
}
return out;
}
var output = array;
var divs = divisors.filter(function(it) {
var optionalDivisorsKeys = Object.keys(optionalDivisors).map(function(it){
return parseInt(it);
});
return !UtilService.inArray(it, combination) && !UtilService.inArray(it, optionalDivisorsKeys);
});
if (!angular.isArray(array) || !angular.isArray(combination)) {
return output;
}
// insert not optional divisors
output = insert(divs, output);
// insert optional divisors
output = insert(combination, output);
return output;
}
function tryDivisorConfiguration(value) {
var output = value.split('');
var defaultDivisors = true;
// has optional?
if (optionalIndexes.length > 0) {
var lazyArguments = [];
var optionalDivisorsKeys = Object.keys(optionalDivisors);
// get all optional divisors as array of arrays [[], [], []...]
for (var i=0; i<optionalDivisorsKeys.length; i++) {
var val = optionalDivisors[optionalDivisorsKeys[i]];
lazyArguments.push(val);
}
// generate all possible configurations
if (optionalDivisorsCombinations.length === 0) {
UtilService.lazyProduct(lazyArguments, function() {
// convert arguments to array
optionalDivisorsCombinations.push(Array.prototype.slice.call(arguments));
});
}
for (var i = optionalDivisorsCombinations.length - 1; i >= 0; i--) {
var outputClone = angular.copy(output);
outputClone = insertDivisors(outputClone, optionalDivisorsCombinations[i]);
// try validation
var viewValueWithDivisors = outputClone.join('');
var regex = getRegex(maskWithoutOptionals.length - 1);
if (regex.test(viewValueWithDivisors)) {
defaultDivisors = false;
output = outputClone;
break;
}
}
}
if (defaultDivisors) {
output = insertDivisors(output, divisors);
}
return output.join('');
}
// MASK
function getOptions() {
return options;
}
function getViewValue(value) {
try {
var outputWithoutDivisors = removeDivisors(value);
var output = tryDivisorConfiguration(outputWithoutDivisors);
return {
withDivisors: function(capped) {
if (capped) {
return output.substr(0, maskWithoutOptionalsLength);
} else {
return output;
}
},
withoutDivisors: function(capped) {
if (capped) {
return outputWithoutDivisors.substr(0, maskWithoutOptionalsAndDivisorsLength);
} else {
return outputWithoutDivisors;
}
}
};
} catch (e) {
throw e;
}
}
// SELECTOR
function getWrongPositions(viewValueWithDivisors, onlyFirst) {
var pos = [];
if (!viewValueWithDivisors) {
return 0;
}
for (var i=0; i<viewValueWithDivisors.length; i++){
var pattern = getRegex(i);
var value = viewValueWithDivisors.substr(0, (i+1));
if(pattern && !pattern.test(value)){
pos.push(i);
if (onlyFirst) {
break;
}
}
}
return pos;
}
function getFirstWrongPosition(viewValueWithDivisors) {
return getWrongPositions(viewValueWithDivisors, true)[0];
}
function removeWrongPositions(viewValueWithDivisors) {
var wrongPositions = getWrongPositions(viewValueWithDivisors, false);
var newViewValue = viewValueWithDivisors;
for (var i in wrongPositions) {
var wrongPosition = wrongPositions[i];
var viewValueArray = viewValueWithDivisors.split('');
viewValueArray.splice(wrongPosition, 1);
newViewValue = viewValueArray.join('');
}
return getViewValue(newViewValue);
}
return {
getViewValue: getViewValue,
generateRegex: generateRegex,
getRegex: getRegex,
getOptions: getOptions,
removeDivisors: removeDivisors,
getFirstWrongPosition: getFirstWrongPosition,
removeWrongPositions: removeWrongPositions
}
}
return {
create: create
}
}]);
})();
(function() {
'use strict';
angular.module('ngMask')
.factory('OptionalService', [function() {
function getOptionalsIndexes(mask) {
var indexes = [];
try {
var regexp = /\?/g;
var match = [];
while ((match = regexp.exec(mask)) != null) {
// Save the optional char
indexes.push((match.index - 1));
}
} catch (e) {
throw e;
}
return {
fromMask: function() {
return indexes;
},
fromMaskWithoutOptionals: function() {
return getOptionalsRelativeMaskWithoutOptionals(indexes);
}
};
}
function getOptionalsRelativeMaskWithoutOptionals(optionals) {
var indexes = [];
for (var i=0; i<optionals.length; i++) {
indexes.push(optionals[i]-i);
}
return indexes;
}
function removeOptionals(mask) {
var newMask;
try {
newMask = mask.replace(/\?/g, '');
} catch (e) {
throw e;
}
return newMask;
}
return {
removeOptionals: removeOptionals,
getOptionals: getOptionalsIndexes
}
}]);
})();(function() {
'use strict';
angular.module('ngMask')
.factory('UtilService', [function() {
// sets: an array of arrays
// f: your callback function
// context: [optional] the `this` to use for your callback
// http://phrogz.net/lazy-cartesian-product
function lazyProduct(sets, f, context){
if (!context){
context=this;
}
var p = [];
var max = sets.length-1;
var lens = [];
for (var i=sets.length;i--;) {
lens[i] = sets[i].length;
}
function dive(d){
var a = sets[d];
var len = lens[d];
if (d === max) {
for (var i=0;i<len;++i) {
p[d] = a[i];
f.apply(context, p);
}
} else {
for (var i=0;i<len;++i) {
p[d]=a[i];
dive(d+1);
}
}
p.pop();
}
dive(0);
}
function inArray(i, array) {
var output;
try {
output = array.indexOf(i) > -1;
} catch (e) {
throw e;
}
return output;
}
function uniqueArray(array) {
var u = {};
var a = [];
for (var i = 0, l = array.length; i < l; ++i) {
if(u.hasOwnProperty(array[i])) {
continue;
}
a.push(array[i]);
u[array[i]] = 1;
}
return a;
}
return {
lazyProduct: lazyProduct,
inArray: inArray,
uniqueArray: uniqueArray
}
}]);
})();