-
Notifications
You must be signed in to change notification settings - Fork 5
/
angular-chrome-autofill-fix.js
125 lines (111 loc) · 3.93 KB
/
angular-chrome-autofill-fix.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
* Created by Alireza Mirian ([email protected]) on 05/24/2016.
*/
(function(angular){
"use strict";
var MAX_TRIES = 5;
var TRY_INTERVAL = 100;
/**
* @ngdoc module
* @name chrome-autofill-fix
* @module chrome-autofill-fix
* @description
* A tiny fix for chrome problems regarding auto-filled passwords
*
*/
angular.module("chrome-autofill-fix", [])
/**
* @ngdoc directive
* @module chrome-autofill-fix
* @name mdInputContainer
* Prevents floating label collapsing in [angular-material](https://material.angularjs.org/latest/demo/input) inputs
*
*/
.directive('mdInputContainer', ['$interval', mdInputContainerDirective])
/**
* @ngdoc directive
* @module chrome-autofill-fix
* @name required
* Overrides the default `required` validator to take Chrome auto-filling into account
*/
.directive("required", ['$interval', '$log', requiredDirective]);
function requiredDirective($interval, $log) {
return {
priority: 100,
require: "?ngModel",
link: linkFn
};
function linkFn(scope, elem, attrs, ngModel) {
if(!isChrome()){
// ignore other browsers
return;
}
if(!ngModel){
// no ngModel, nothing to do!
return;
}
if(elem[0].tagName !== 'INPUT'){
// ignore non-input components
return;
}
var originalValidator = ngModel.$validators.required;
ngModel.$validators.required = validator;
// try validating until
var tries = 0;
var timer = $interval(function () {
tries++;
if (tries > MAX_TRIES) {
$interval.cancel(timer);
}
ngModel.$validate();
}, TRY_INTERVAL);
function validator(modelValue, viewValue) {
if (isChrome() && elem[0].matches("input[type=password]:-webkit-autofill")) {
$log.info("bypassing required validator because of Chrome auto-filling");
$interval.cancel(timer);
return true;
}
return originalValidator(modelValue, viewValue);
}
}
}
function mdInputContainerDirective($interval) {
return {
restrict: "E",
link: linkFn
};
function linkFn($scope, elem) {
if (isChrome() && elem[0].querySelector('input')) {
var tries = 0;
var timer = $interval(function () {
tries++;
if (tries > MAX_TRIES) {
$interval.cancel(timer);
}
if (elem[0].querySelector('input[type=password]:-webkit-autofill')) {
elem.addClass('md-input-has-value');
$interval.cancel(timer);
}
}, TRY_INTERVAL);
}
}
}
function isChrome(){
return navigator.userAgent.match(/chrome/i) && !navigator.userAgent.match(/edge/i);
}
/**
* element.matches() pollyfill
*/
Element && function(ElementPrototype) {
ElementPrototype.matches = ElementPrototype.matchesSelector ||
ElementPrototype.mozMatchesSelector ||
ElementPrototype.msMatchesSelector ||
ElementPrototype.oMatchesSelector ||
ElementPrototype.webkitMatchesSelector ||
function (selector) {
var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
while (nodes[++i] && nodes[i] != node);
return !!nodes[i];
}
}(Element.prototype);
})(angular);