PageSourceSearch

https://mutation.ndita.org/PortalManagement/AngularScript/mask.js

js ndita.org collected 2026-10-02 13:23:42 UTC 45,656 bytes, 783 lines download raw bytes

1/*!
2 * angular-ui-mask
3 * https://github.com/angular-ui/ui-mask
4 * Version: 1.8.7 - 2016-07-26T15:59:07.992Z
5 * License: MIT
6 */
7
8
9(function () { 
10'use strict';
11/*
12 Attaches input mask onto input element
13 */
14angular.module('ui.mask', [])
15        .value('uiMaskConfig', {
16            maskDefinitions: {
17                '9': /\d/,
18                'A': /[a-zA-Z]/,
19                '*': /[a-zA-Z0-9]/
20            },
21            clearOnBlur: true,
22            clearOnBlurPlaceholder: false,
23            escChar: '\\',
24            eventsToHandle: ['input', 'keyup', 'click', 'focus'],
25            addDefaultPlaceholder: true,
26            allowInvalidValue: false
27        })
28        .provider('uiMask.Config', function() {
29            var options = {};
30
31            this.maskDefinitions = function(maskDefinitions) {
32                return options.maskDefinitions = maskDefinitions;
33            };
34            this.clearOnBlur = function(clearOnBlur) {
35                return options.clearOnBlur = clearOnBlur;
36            };
37            this.clearOnBlurPlaceholder = function(clearOnBlurPlaceholder) {
38                return options.clearOnBlurPlaceholder = clearOnBlurPlaceholder;
39            };
40            this.eventsToHandle = function(eventsToHandle) {
41                return options.eventsToHandle = eventsToHandle;
42            };
43            this.addDefaultPlaceholder = function(addDefaultPlaceholder) {
44                return options.addDefaultPlaceholder = addDefaultPlaceholder;
45            };
46            this.allowInvalidValue = function(allowInvalidValue) {
47                return options.allowInvalidValue = allowInvalidValue;
48            };
49            this.$get = ['uiMaskConfig', function(uiMaskConfig) {
50                var tempOptions = uiMaskConfig;
51                for(var prop in options) {
52                    if (angular.isObject(options[prop]) && !angular.isArray(options[prop])) {
53                        angular.extend(tempOptions[prop], options[prop]);
54                    } else {
55                        tempOptions[prop] = options[prop];
56                    }
57                }
58
59                return tempOptions;
60            }];
61        })
62        .directive('uiMask', ['uiMask.Config', function(maskConfig) {
63                function isFocused (elem) {
64                  return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
65                }
66
67                return {
68                    priority: 100,
69                    require: 'ngModel',
70                    restrict: 'A',
71                    compile: function uiMaskCompilingFunction() {
72                        var options = angular.copy(maskConfig);
73
74                        return function uiMaskLinkingFunction(scope, iElement, iAttrs, controller) {
75                            var maskProcessed = false, eventsBound = false,
76                                    maskCaretMap, maskPatterns, maskPlaceholder, maskComponents,
77                                    // Minimum required length of the value to be considered valid
78                                    minRequiredLength,
79                                    value, valueMasked, isValid,
80                                    // Vars for initializing/uninitializing
81                                    originalPlaceholder = iAttrs.placeholder,
82                                    originalMaxlength = iAttrs.maxlength,
83                                    // Vars used exclusively in eventHandler()
84                                    oldValue, oldValueUnmasked, oldCaretPosition, oldSelectionLength,
85                                    // Used for communicating if a backspace operation should be allowed between
86                                    // keydownHandler and eventHandler
87                                    preventBackspace;
88
89                            var originalIsEmpty = controller.$isEmpty;
90                            controller.$isEmpty = function(value) {
91                                if (maskProcessed) {
92                                    return originalIsEmpty(unmaskValue(value || ''));
93                                } else {
94                                    return originalIsEmpty(value);
95                                }
96                            };
97
98                            function initialize(maskAttr) {
99                                if (!angular.isDefined(maskAttr)) {
100                                    return uninitialize();
101                                }
102                                processRawMask(maskAttr);
103                                if (!maskProcessed) {
104                                    return uninitialize();
105                                }
106                                initializeElement();
107                                bindEventListeners();
108                                return true;
109                            }
110
111                            function initPlaceholder(placeholderAttr) {
112                                if ( ! placeholderAttr) {
113                                    return;
114                                }
115
116                                maskPlaceholder = placeholderAttr;
117
118                                // If the mask is processed, then we need to update the value
119                                // but don't set the value if there is nothing entered into the element
120                                // and there is a placeholder attribute on the element because that
121                                // will only set the value as the blank maskPlaceholder
122                                // and override the placeholder on the element
123                                if (maskProcessed && !(iElement.val().length === 0 && angular.isDefined(iAttrs.placeholder))) {
124                                    iElement.val(maskValue(unmaskValue(iElement.val())));
125                                }
126                            }
127
128                            function initPlaceholderChar() {
129                                return initialize(iAttrs.uiMask);
130                            }
131
132                            var modelViewValue = false;
133                            iAttrs.$observe('modelViewValue', function(val) {
134                                if (val === 'true') {
135                                    modelViewValue = true;
136                                }
137                            });
138
139                            iAttrs.$observe('allowInvalidValue', function(val) {
140                                linkOptions.allowInvalidValue = val === ''
141                                    ? true
142                                    : !!val;
143                                formatter(controller.$modelValue);
144                            });
145
146                            function formatter(fromModelValue) {
147                                if (!maskProcessed) {
148                                    return fromModelValue;
149                                }
150                                value = unmaskValue(fromModelValue || '');
151                                isValid = validateValue(value);
152                                controller.$setValidity('mask', isValid);
153
154                                if (!value.length) return undefined;
155                                if (isValid || linkOptions.allowInvalidValue) {
156                                    return maskValue(value);
157                                } else {
158                                    return undefined;
159                                }
160                            }
161
162                            function parser(fromViewValue) {
163                                if (!maskProcessed) {
164                                    return fromViewValue;
165                                }
166                                value = unmaskValue(fromViewValue || '');
167                                isValid = validateValue(value);
168                                // We have to set viewValue manually as the reformatting of the input
169                                // value performed by eventHandler() doesn't happen until after
170                                // this parser is called, which causes what the user sees in the input
171                                // to be out-of-sync with what the controller's $viewValue is set to.
172                                controller.$viewValue = value.length ? maskValue(value) : '';
173                                controller.$setValidity('mask', isValid);
174
175                                if (isValid || linkOptions.allowInvalidValue) {
176                                    return modelViewValue ? controller.$viewValue : value;
177                                }
178                            }
179
180                            var linkOptions = {};
181
182                            if (iAttrs.uiOptions) {
183                                linkOptions = scope.$eval('[' + iAttrs.uiOptions + ']');
184                                if (angular.isObject(linkOptions[0])) {
185                                    // we can't use angular.copy nor angular.extend, they lack the power to do a deep merge
186                                    linkOptions = (function(original, current) {
187                                        for (var i in original) {
188                                            if (Object.prototype.hasOwnProperty.call(original, i)) {
189                                                if (current[i] === undefined) {
190                                                    current[i] = angular.copy(original[i]);
191                                                } else {
192                                                    if (angular.isObject(current[i]) && !angular.isArray(current[i])) {
193                                                        current[i] = angular.extend({}, original[i], current[i]);
194                                                    }
195                                                }
196                                            }
197                                        }
198                                        return current;
199                                    })(options, linkOptions[0]);
200                                } else {
201                                    linkOptions = options;  //gotta be a better way to do this..
202                                }
203                            } else {
204                                linkOptions = options;
205                            }
206
207                            iAttrs.$observe('uiMask', initialize);
208                            if (angular.isDefined(iAttrs.uiMaskPlaceholder)) {
209                                iAttrs.$observe('uiMaskPlaceholder', initPlaceholder);
210                            }
211                            else {
212                                iAttrs.$observe('placeholder', initPlaceholder);
213                            }
214                            if (angular.isDefined(iAttrs.uiMaskPlaceholderChar)) {
215                                iAttrs.$observe('uiMaskPlaceholderChar', initPlaceholderChar);
216                            }
217
218                            controller.$formatters.unshift(formatter);
219                            controller.$parsers.unshift(parser);
220
221                            function uninitialize() {
222                                maskProcessed = false;
223                                unbindEventListeners();
224
225                                if (angular.isDefined(originalPlaceholder)) {
226                                    iElement.attr('placeholder', originalPlaceholder);
227                                } else {
228                                    iElement.removeAttr('placeholder');
229                                }
230
231                                if (angular.isDefined(originalMaxlength)) {
232                                    iElement.attr('maxlength', originalMaxlength);
233                                } else {
234                                    iElement.removeAttr('maxlength');
235                                }
236
237                                iElement.val(controller.$modelValue);
238                                controller.$viewValue = controller.$modelValue;
239                                return false;
240                            }
241
242                            function initializeElement() {
243                                value = oldValueUnmasked = unmaskValue(controller.$modelValue || '');
244                                valueMasked = oldValue = maskValue(value);
245                                isValid = validateValue(value);
246                                if (iAttrs.maxlength) { // Double maxlength to allow pasting new val at end of mask
247                                    iElement.attr('maxlength', maskCaretMap[maskCaretMap.length - 1] * 2);
248                                }
249                                if ( ! originalPlaceholder && linkOptions.addDefaultPlaceholder) {
250                                    iElement.attr('placeholder', maskPlaceholder);
251                                }
252                                var viewValue = controller.$modelValue;
253                                var idx = controller.$formatters.length;
254                                while(idx--) {
255                                    viewValue = controller.$formatters[idx](viewValue);
256                                }
257                                controller.$viewValue = viewValue || '';
258                                controller.$render();
259                                // Not using $setViewValue so we don't clobber the model value and dirty the form
260                                // without any kind of user interaction.
261                            }
262
263                            function bindEventListeners() {
264                                if (eventsBound) {
265                                    return;
266                                }
267                                iElement.bind('blur', blurHandler);
268                                iElement.bind('mousedown mouseup', mouseDownUpHandler);
269                                iElement.bind('keydown', keydownHandler);
270                                iElement.bind(linkOptions.eventsToHandle.join(' '), eventHandler);
271                                eventsBound = true;
272                            }
273
274                            function unbindEventListeners() {
275                                if (!eventsBound) {
276                                    return;
277                                }
278                                iElement.unbind('blur', blurHandler);
279                                iElement.unbind('mousedown', mouseDownUpHandler);
280                                iElement.unbind('mouseup', mouseDownUpHandler);
281                                iElement.unbind('keydown', keydownHandler);
282                                iElement.unbind('input', eventHandler);
283                                iElement.unbind('keyup', eventHandler);
284                                iElement.unbind('click', eventHandler);
285                                iElement.unbind('focus', eventHandler);
286                                eventsBound = false;
287                            }
288
289                            function validateValue(value) {
290                                // Zero-length value validity is ngRequired's determination
291                                return value.length ? value.length >= minRequiredLength : true;
292                            }
293
294                            function unmaskValue(value) {
295                                var valueUnmasked = '',
296                                    input = iElement[0],
297                                    maskPatternsCopy = maskPatterns.slice(),
298                                    selectionStart = oldCaretPosition,
299                                    selectionEnd = selectionStart + getSelectionLength(input),
300                                    valueOffset, valueDelta, tempValue = '';
301                                // Preprocess by stripping mask components from value
302                                value = value.toString();
303                                valueOffset = 0;
304                                valueDelta = value.length - maskPlaceholder.length;
305                                angular.forEach(maskComponents, function(component) {
306                                    var position = component.position;
307                                    //Only try and replace the component if the component position is not within the selected range
308                                    //If component was in selected range then it was removed with the user input so no need to try and remove that component
309                                    if (!(position >= selectionStart && position < selectionEnd)) {
310                                        if (position >= selectionStart) {
311                                            position += valueDelta;
312                                        }
313                                        if (value.substring(position, position + component.value.length) === component.value) {
314                                            tempValue += value.slice(valueOffset, position);// + value.slice(position + component.value.length);
315                                            valueOffset = position + component.value.length;
316                                        }
317                                    }
318                                });
319                                value = tempValue + value.slice(valueOffset);
320                                angular.forEach(value.split(''), function(chr) {
321                                    if (maskPatternsCopy.length && maskPatternsCopy[0].test(chr)) {
322                                        valueUnmasked += chr;
323                                        maskPatternsCopy.shift();
324                                    }
325                                });
326
327                                return valueUnmasked;
328                            }
329
330                            function maskValue(unmaskedValue) {
331                                var valueMasked = '',
332                                        maskCaretMapCopy = maskCaretMap.slice();
333
334                                angular.forEach(maskPlaceholder.split(''), function(chr, i) {
335                                    if (unmaskedValue.length && i === maskCaretMapCopy[0]) {
336                                        valueMasked += unmaskedValue.charAt(0) || '_';
337                                        unmaskedValue = unmaskedValue.substr(1);
338                                        maskCaretMapCopy.shift();
339                                    }
340                                    else {
341                                        valueMasked += chr;
342                                    }
343                                });
344                                return valueMasked;
345                            }
346
347                            function getPlaceholderChar(i) {
348                                var placeholder = angular.isDefined(iAttrs.uiMaskPlaceholder) ? iAttrs.uiMaskPlaceholder : iAttrs.placeholder,
349                                    defaultPlaceholderChar;
350
351                                if (angular.isDefined(placeholder) && placeholder[i]) {
352                                    return placeholder[i];
353                                } else {
354                                    defaultPlaceholderChar = angular.isDefined(iAttrs.uiMaskPlaceholderChar) && iAttrs.uiMaskPlaceholderChar ? iAttrs.uiMaskPlaceholderChar : '_';
355                                    return (defaultPlaceholderChar.toLowerCase() === 'space') ? ' ' : defaultPlaceholderChar[0];
356                                }
357                            }
358
359                            // Generate array of mask components that will be stripped from a masked value
360                            // before processing to prevent mask components from being added to the unmasked value.
361                            // E.g., a mask pattern of '+7 9999' won't have the 7 bleed into the unmasked value.
362                            function getMaskComponents() {
363                                var maskPlaceholderChars = maskPlaceholder.split(''),
364                                        maskPlaceholderCopy, components;
365
366                                //maskCaretMap can have bad values if the input has the ui-mask attribute implemented as an obversable property, e.g. the demo page
367                                if (maskCaretMap && !isNaN(maskCaretMap[0])) {
368                                    //Instead of trying to manipulate the RegEx based on the placeholder characters
369                                    //we can simply replace the placeholder characters based on the already built
370                                    //maskCaretMap to underscores and leave the original working RegEx to get the proper
371                                    //mask components
372                                    angular.forEach(maskCaretMap, function(value) {
373                                        maskPlaceholderChars[value] = '_';
374                                    });
375                                }
376                                maskPlaceholderCopy = maskPlaceholderChars.join('');
377                                components = maskPlaceholderCopy.replace(/[_]+/g, '_').split('_');
378                                components = components.filter(function(s) {
379                                    return s !== '';
380                                });
381
382                                // need a string search offset in cases where the mask contains multiple identical components
383                                // E.g., a mask of 99.99.99-999.99
384                                var offset = 0;
385                                return components.map(function(c) {
386                                    var componentPosition = maskPlaceholderCopy.indexOf(c, offset);
387                                    offset = componentPosition + 1;
388                                    return {
389                                        value: c,
390                                        position: componentPosition
391                                    };
392                                });
393                            }
394
395                            function processRawMask(mask) {
396                                var characterCount = 0;
397
398                                maskCaretMap = [];
399                                maskPatterns = [];
400                                maskPlaceholder = '';
401
402                                if (angular.isString(mask)) {
403                                    minRequiredLength = 0;
404
405                                    var isOptional = false,
406                                            numberOfOptionalCharacters = 0,
407                                            splitMask = mask.split('');
408
409                                    var inEscape = false;
410                                    angular.forEach(splitMask, function(chr, i) {
411                                        if (inEscape) {
412                                            inEscape = false;
413                                            maskPlaceholder += chr;
414                                            characterCount++;
415                                        }
416                                        else if (linkOptions.escChar === chr) {
417                                            inEscape = true;
418                                        }
419                                        else if (linkOptions.maskDefinitions[chr]) {
420                                            maskCaretMap.push(characterCount);
421
422                                            maskPlaceholder += getPlaceholderChar(i - numberOfOptionalCharacters);
423                                            maskPatterns.push(linkOptions.maskDefinitions[chr]);
424
425                                            characterCount++;
426                                            if (!isOptional) {
427                                                minRequiredLength++;
428                                            }
429
430                                            isOptional = false;
431                                        }
432                                        else if (chr === '?') {
433                                            isOptional = true;
434                                            numberOfOptionalCharacters++;
435                                        }
436                                        else {
437                                            maskPlaceholder += chr;
438                                            characterCount++;
439                                        }
440                                    });
441                                }
442                                // Caret position immediately following last position is valid.
443                                maskCaretMap.push(maskCaretMap.slice().pop() + 1);
444
445                                maskComponents = getMaskComponents();
446                                maskProcessed = maskCaretMap.length > 1 ? true : false;
447                            }
448
449                            var prevValue = iElement.val();
450                            function blurHandler() {
451                                if (linkOptions.clearOnBlur || ((linkOptions.clearOnBlurPlaceholder) && (value.length === 0) && iAttrs.placeholder)) {
452                                    oldCaretPosition = 0;
453                                    oldSelectionLength = 0;
454                                    if (!isValid || value.length === 0) {
455                                        valueMasked = '';
456                                        iElement.val('');
457                                        scope.$apply(function() {
458                                            //only $setViewValue when not $pristine to avoid changing $pristine state.
459                                            if (!controller.$pristine) {
460                                                controller.$setViewValue('');
461                                            }
462                                        });
463                                    }
464                                }
465                                //Check for different value and trigger change.
466                                //Check for different value and trigger change.
467                                if (value !== prevValue) {
468                                    // #157 Fix the bug from the trigger when backspacing exactly on the first letter (emptying the field)
469                                    // and then blurring out.
470                                    // Angular uses html element and calls setViewValue(element.value.trim()), setting it to the trimmed mask
471                                    // when it should be empty
472                                    var currentVal = iElement.val();
473                                    var isTemporarilyEmpty = value === '' && currentVal && angular.isDefined(iAttrs.uiMaskPlaceholderChar) && iAttrs.uiMaskPlaceholderChar === 'space';
474                                    if(isTemporarilyEmpty) {
475                                        iElement.val('');
476                                    }
477                                    triggerChangeEvent(iElement[0]);
478                                    if(isTemporarilyEmpty) {
479                                        iElement.val(currentVal);
480                                    }
481                                }
482                                prevValue = value;
483                            }
484
485                            function triggerChangeEvent(element) {
486                                var change;
487                                if (angular.isFunction(window.Event) && !element.fireEvent) {
488                                    // modern browsers and Edge
489                                    try {
490                                        change = new Event('change', {
491                                            view: window,
492                                            bubbles: true,
493                                            cancelable: false
494                                        });
495                                    } catch (ex) {
496                                        //this is for certain mobile browsers that have the Event object
497                                        //but don't support the Event constructor #168
498                                        change = document.createEvent('HTMLEvents');
499                                        change.initEvent('change', false, true);
500                                    } finally {
501                                        element.dispatchEvent(change);
502                                    }
503                                } else if ('createEvent' in document) {
504                                    // older browsers
505                                    change = document.createEvent('HTMLEvents');
506                                    change.initEvent('change', false, true);
507                                    element.dispatchEvent(change);
508                                }
509                                else if (element.fireEvent) {
510                                    // IE <= 11
511                                    element.fireEvent('onchange');
512                                }
513                            }
514
515                            function mouseDownUpHandler(e) {
516                                if (e.type === 'mousedown') {
517                                    iElement.bind('mouseout', mouseoutHandler);
518                                } else {
519                                    iElement.unbind('mouseout', mouseoutHandler);
520                                }
521                            }
522
523                            iElement.bind('mousedown mouseup', mouseDownUpHandler);
524
525                            function mouseoutHandler() {
526                                /*jshint validthis: true */
527                                oldSelectionLength = getSelectionLength(this);
528                                iElement.unbind('mouseout', mouseoutHandler);
529                            }
530
531                            function keydownHandler(e) {
532                                /*jshint validthis: true */
533                                var isKeyBackspace = e.which === 8,
534                                caretPos = getCaretPosition(this) - 1 || 0, //value in keydown is pre change so bump caret position back to simulate post change
535                                isCtrlZ = e.which === 90 && e.ctrlKey; //ctrl+z pressed
536
537                                if (isKeyBackspace) {
538                                    while(caretPos >= 0) {
539                                        if (isValidCaretPosition(caretPos)) {
540                                            //re-adjust the caret position.
541                                            //Increment to account for the initial decrement to simulate post change caret position
542                                            setCaretPosition(this, caretPos + 1);
543                                            break;
544                                        }
545                                        caretPos--;
546                                    }
547                                    preventBackspace = caretPos === -1;
548                                }
549
550                                if (isCtrlZ) {
551                                    // prevent IE bug - value should be returned to initial state
552                                    iElement.val('');
553                                    e.preventDefault();
554                                }
555                            }
556
557                            function eventHandler(e) {
558                                /*jshint validthis: true */
559                                e = e || {};
560                                // Allows more efficient minification
561                                var eventWhich = e.which,
562                                        eventType = e.type;
563
564                                // Prevent shift and ctrl from mucking with old values
565                                if (eventWhich === 16 || eventWhich === 91) {
566                                    return;
567                                }
568
569                                var val = iElement.val(),
570                                        valOld = oldValue,
571                                        valMasked,
572                                        valAltered = false,
573                                        valUnmasked = unmaskValue(val),
574                                        valUnmaskedOld = oldValueUnmasked,
575                                        caretPos = getCaretPosition(this) || 0,
576                                        caretPosOld = oldCaretPosition || 0,
577                                        caretPosDelta = caretPos - caretPosOld,
578                                        caretPosMin = maskCaretMap[0],
579                                        caretPosMax = maskCaretMap[valUnmasked.length] || maskCaretMap.slice().shift(),
580                                        selectionLenOld = oldSelectionLength || 0,
581                                        isSelected = getSelectionLength(this) > 0,
582                                        wasSelected = selectionLenOld > 0,
583                                        // Case: Typing a character to overwrite a selection
584                                        isAddition = (val.length > valOld.length) || (selectionLenOld && val.length > valOld.length - selectionLenOld),
585                                        // Case: Delete and backspace behave identically on a selection
586                                        isDeletion = (val.length < valOld.length) || (selectionLenOld && val.length === valOld.length - selectionLenOld),
587                                        isSelection = (eventWhich >= 37 && eventWhich <= 40) && e.shiftKey, // Arrow key codes
588
589                                        isKeyLeftArrow = eventWhich === 37,
590                                        // Necessary due to "input" event not providing a key code
591                                        isKeyBackspace = eventWhich === 8 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === -1)),
592                                        isKeyDelete = eventWhich === 46 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === 0) && !wasSelected),
593                                        // Handles cases where caret is moved and placed in front of invalid maskCaretMap position. Logic below
594                                        // ensures that, on click or leftward caret placement, caret is moved leftward until directly right of
595                                        // non-mask character. Also applied to click since users are (arguably) more likely to backspace
596                                        // a character when clicking within a filled input.
597                                        caretBumpBack = (isKeyLeftArrow || isKeyBackspace || eventType === 'click') && caretPos > caretPosMin;
598
599                                oldSelectionLength = getSelectionLength(this);
600
601                                // These events don't require any action
602                                if (isSelection || (isSelected && (eventType === 'click' || eventType === 'keyup' || eventType === 'focus'))) {
603                                    return;
604                                }
605
606                                if (isKeyBackspace && preventBackspace) {
607                                    iElement.val(maskPlaceholder);
608                                    // This shouldn't be needed but for some reason after aggressive backspacing the controller $viewValue is incorrect.
609                                    // This keeps the $viewValue updated and correct.
610                                    scope.$apply(function () {
611                                        controller.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
612                                    });
613                                    setCaretPosition(this, caretPosOld);
614                                    return;
615                                }
616
617                                // Value Handling
618                                // ==============
619
620                                // User attempted to delete but raw value was unaffected--correct this grievous offense
621                                if ((eventType === 'input') && isDeletion && !wasSelected && valUnmasked === valUnmaskedOld) {
622                                    while (isKeyBackspace && caretPos > caretPosMin && !isValidCaretPosition(caretPos)) {
623                                        caretPos--;
624                                    }
625                                    while (isKeyDelete && caretPos < caretPosMax && maskCaretMap.indexOf(caretPos) === -1) {
626                                        caretPos++;
627                                    }
628                                    var charIndex = maskCaretMap.indexOf(caretPos);
629                                    // Strip out non-mask character that user would have deleted if mask hadn't been in the way.
630                                    valUnmasked = valUnmasked.substring(0, charIndex) + valUnmasked.substring(charIndex + 1);
631
632                                    // If value has not changed, don't want to call $setViewValue, may be caused by IE raising input event due to placeholder
633                                    if (valUnmasked !== valUnmaskedOld)
634                                        valAltered = true;
635                                }
636
637                                // Update values
638                                valMasked = maskValue(valUnmasked);
639
640                                oldValue = valMasked;
641                                oldValueUnmasked = valUnmasked;
642
643                                //additional check to fix the problem where the viewValue is out of sync with the value of the element.
644                                //better fix for commit 2a83b5fb8312e71d220a497545f999fc82503bd9 (I think)
645                                if (!valAltered && val.length > valMasked.length)
646                                    valAltered = true;
647
648                                iElement.val(valMasked);
649
650                                //we need this check.  What could happen if you don't have it is that you'll set the model value without the user
651                                //actually doing anything.  Meaning, things like pristine and touched will be set.
652                                if (valAltered) {
653                                    scope.$apply(function () {
654                                        controller.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
655                                    });
656                                }
657
658                                // Caret Repositioning
659                                // ===================
660
661                                // Ensure that typing always places caret ahead of typed character in cases where the first char of
662                                // the input is a mask char and the caret is placed at the 0 position.
663                                if (isAddition && (caretPos <= caretPosMin)) {
664                                    caretPos = caretPosMin + 1;
665                                }
666
667                                if (caretBumpBack) {
668                                    caretPos--;
669                                }
670
671                                // Make sure caret is within min and max position limits
672                                caretPos = caretPos > caretPosMax ? caretPosMax : caretPos < caretPosMin ? caretPosMin : caretPos;
673
674                                // Scoot the caret back or forth until it's in a non-mask position and within min/max position limits
675                                while (!isValidCaretPosition(caretPos) && caretPos > caretPosMin && caretPos < caretPosMax) {
676                                    caretPos += caretBumpBack ? -1 : 1;
677                                }
678
679                                if ((caretBumpBack && caretPos < caretPosMax) || (isAddition && !isValidCaretPosition(caretPosOld))) {
680                                    caretPos++;
681                                }
682                                oldCaretPosition = caretPos;
683                                setCaretPosition(this, caretPos);
684                            }
685
686                            function isValidCaretPosition(pos) {
687                                return maskCaretMap.indexOf(pos) > -1;
688                            }
689
690                            function getCaretPosition(input) {
691                                if (!input)
692                                    return 0;
693                                if (input.selectionStart !== undefined) {
694                                    return input.selectionStart;
695                                } else if (document.selection) {
696                                    if (isFocused(iElement[0])) {
697                                        // Curse you IE
698                                        input.focus();
699                                        var selection = document.selection.createRange();
700                                        selection.moveStart('character', input.value ? -input.value.length : 0);
701                                        return selection.text.length;
702                                    }
703                                }
704                                return 0;
705                            }
706
707                            function setCaretPosition(input, pos) {
708                                if (!input)
709                                    return 0;
710                                if (input.offsetWidth === 0 || input.offsetHeight === 0) {
711                                    return; // Input's hidden
712                                }
713                                if (input.setSelectionRange) {
714                                    if (isFocused(iElement[0])) {
715                                        input.focus();
716                                        input.setSelectionRange(pos, pos);
717                                    }
718                                }
719                                else if (input.createTextRange) {
720                                    // Curse you IE
721                                    var range = input.createTextRange();
722                                    range.collapse(true);
723                                    range.moveEnd('character', pos);
724                                    range.moveStart('character', pos);
725                                    range.select();
726                                }
727                            }
728
729                            function getSelectionLength(input) {
730                                if (!input)
731                                    return 0;
732                                if (input.selectionStart !== undefined) {
733                                    return (input.selectionEnd - input.selectionStart);
734                                }
735                                if (window.getSelection) {
736                                    return (window.getSelection().toString().length);
737                                }
738                                if (document.selection) {
739                                    return (document.selection.createRange().text.length);
740                                }
741                                return 0;
742                            }
743
744                            // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf
745                            if (!Array.prototype.indexOf) {
746                                Array.prototype.indexOf = function(searchElement /*, fromIndex */) {
747                                    if (this === null) {
748                                        throw new TypeError();
749                                    }
750                                    var t = Object(this);
751                                    var len = t.length >>> 0;
752                                    if (len === 0) {
753                                        return -1;
754                                    }
755                                    var n = 0;
756                                    if (arguments.length > 1) {
757                                        n = Number(arguments[1]);
758                                        if (n !== n) { // shortcut for verifying if it's 
758NaN
759                                            n = 0;
760                                        } else if (n !== 0 && n !== Infinity && n !== -Infinity) {
761                                            n = (n > 0 || -1) * Math.floor(Math.abs(n));
762                                        }
763                                    }
764                                    if (n >= len) {
765                                        return -1;
766                                    }
767                                    var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
768                                    for (; k < len; k++) {
769                                        if (k in t && t[k] === searchElement) {
770                                            return k;
771                                        }
772                                    }
773                                    return -1;
774                                };
775                            }
776
777                        };
778                    }
779                };
780            }
781        ]);
782
783}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.