Merge branch 'redesign'
This commit is contained in:
Коммит
c6517950d8
|
@ -97,10 +97,34 @@
|
|||
"message": "Username",
|
||||
"description": "Username"
|
||||
},
|
||||
"email": {
|
||||
"message": "E-mail",
|
||||
"description": "E-mail"
|
||||
},
|
||||
"add": {
|
||||
"message": "Add",
|
||||
"description": "Add"
|
||||
},
|
||||
"credential_created": {
|
||||
"message": "Credential created",
|
||||
"description": "Credential created"
|
||||
},
|
||||
"url": {
|
||||
"message": "URL",
|
||||
"description": "URL"
|
||||
},
|
||||
"delete": {
|
||||
"message": "Delete",
|
||||
"description": "Delete"
|
||||
},
|
||||
"password": {
|
||||
"message": "Password",
|
||||
"description": "Password"
|
||||
},
|
||||
"password_repeat": {
|
||||
"message": "Password (repeat)",
|
||||
"description": "Password"
|
||||
},
|
||||
"detected_new_login": {
|
||||
"message": "Detected new login",
|
||||
"description": "Detected new login"
|
||||
|
@ -247,6 +271,26 @@
|
|||
"message": "Done",
|
||||
"description": "Done"
|
||||
},
|
||||
"list": {
|
||||
"message": "List",
|
||||
"description": "List"
|
||||
},
|
||||
"edit": {
|
||||
"message": "Edit",
|
||||
"description": "Edit"
|
||||
},
|
||||
"server_settings": {
|
||||
"message": "Server settings",
|
||||
"description": "Server Settings"
|
||||
},
|
||||
"vault_settings": {
|
||||
"message": "Vault settings",
|
||||
"description": "Vault settings"
|
||||
},
|
||||
"master_password": {
|
||||
"message": "Set Master password",
|
||||
"description": "Set a master password"
|
||||
},
|
||||
"finish": {
|
||||
"message": "Finish",
|
||||
"description": "Finish"
|
||||
|
@ -263,10 +307,26 @@
|
|||
"message": "Prev",
|
||||
"description": "Previous"
|
||||
},
|
||||
"back": {
|
||||
"message": "Back",
|
||||
"description": "Back"
|
||||
},
|
||||
"copy": {
|
||||
"message": "Copy",
|
||||
"description": "Copy"
|
||||
},
|
||||
"next": {
|
||||
"message": "Next",
|
||||
"description": "Next"
|
||||
},
|
||||
"continue": {
|
||||
"message": "Continue",
|
||||
"description": "Continue"
|
||||
},
|
||||
"donate": {
|
||||
"message": "Donate",
|
||||
"description": "Donate"
|
||||
},
|
||||
"nextcloud_settings": {
|
||||
"message": "Nextcloud / ownCloud server settings",
|
||||
"description": "Nextcloud / ownCloud server settings"
|
||||
|
@ -287,6 +347,18 @@
|
|||
"message": "Vault password",
|
||||
"description": "Vault password"
|
||||
},
|
||||
"label_required": {
|
||||
"message": "Please fill in a label",
|
||||
"description": "When comparing passwords"
|
||||
},
|
||||
"invalid_host": {
|
||||
"message": "Invalid server url",
|
||||
"description": "Invalid server url"
|
||||
},
|
||||
"no_password_match": {
|
||||
"message": "Passwords don't match",
|
||||
"description": "When comparing passwords"
|
||||
},
|
||||
"invalid_vault_password": {
|
||||
"message": "Invalid vault key!",
|
||||
"description": "Vault password"
|
||||
|
@ -300,7 +372,7 @@
|
|||
"description": "One time password"
|
||||
},
|
||||
"settings": {
|
||||
"message": "settings",
|
||||
"message": "Settings",
|
||||
"description": "Settings"
|
||||
},
|
||||
"search": {
|
||||
|
|
12
css/main.css
12
css/main.css
|
@ -150,13 +150,13 @@ input[type="password"], input[type="text"] {
|
|||
left: 10px;
|
||||
top: 6px;
|
||||
position: relative;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: #d6dadc;
|
||||
border-radius: 50%; }
|
||||
.radial-progress .circle .mask, .radial-progress .circle .fill {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
-webkit-transition: -webkit-transform 0.5s;
|
||||
|
@ -165,9 +165,9 @@ input[type="password"], input[type="text"] {
|
|||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden; }
|
||||
.radial-progress .circle .mask {
|
||||
clip: rect(0px, 25px, 25px, 12.5px); }
|
||||
clip: rect(0px, 10px, 10px, 5px); }
|
||||
.radial-progress .circle .mask .fill {
|
||||
clip: rect(0px, 12.5px, 25px, 0px);
|
||||
clip: rect(0px, 5px, 10px, 0px);
|
||||
background-color: #97a71d; }
|
||||
|
||||
.ng-hide {
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
body, html {
|
||||
min-width: 450px; }
|
||||
|
||||
.server_settings md-input-container {
|
||||
margin-bottom: 0; }
|
||||
|
||||
.pointer {
|
||||
cursor: pointer; }
|
||||
|
||||
.password-list {
|
||||
min-height: 250px; }
|
||||
|
||||
.md-content {
|
||||
height: 100%;
|
||||
max-height: auto; }
|
||||
|
||||
.addFab {
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
bottom: 10px; }
|
||||
|
||||
.menu {
|
||||
padding: 0; }
|
||||
.menu ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%; }
|
||||
.menu ul li {
|
||||
list-style-type: none;
|
||||
width: 100%; }
|
||||
.menu ul li md-icon {
|
||||
margin-right: 16px;
|
||||
min-width: 40px;
|
||||
width: 40px; }
|
||||
.menu ul li a {
|
||||
color: #000;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: block;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding: 0;
|
||||
padding-left: 16px;
|
||||
padding-right: 56px;
|
||||
text-decoration: none;
|
||||
clear: both;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
-webkit-transition: all 0.2s ease-in-out;
|
||||
-o-transition: all 0.2s ease-in-out;
|
||||
transition: all 0.2s ease-in-out; }
|
||||
.menu ul li a:hover {
|
||||
background-color: #e0e0e0; }
|
||||
|
||||
.password-list {
|
||||
padding: 0; }
|
||||
|
||||
/*/*/
|
||||
.radial-progress {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #d6dadc;
|
||||
border-radius: 50%; }
|
||||
.radial-progress .circle .mask, .radial-progress .circle .fill {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
-webkit-transition: -webkit-transform 0.5s;
|
||||
-moz-transition: -moz-transform 0.5s;
|
||||
transition: transform 0.5s;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden; }
|
||||
.radial-progress .circle .mask {
|
||||
clip: rect(0px, 12px, 12px, 6px); }
|
||||
.radial-progress .circle .mask .fill {
|
||||
clip: rect(0px, 6px, 12px, 0px);
|
||||
background-color: #0277bd; }
|
||||
|
||||
.ng-hide {
|
||||
display: none; }
|
||||
|
||||
.unlock-container {
|
||||
height: 350px; }
|
||||
.unlock-container .unlock {
|
||||
padding: 20px;
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); }
|
||||
.unlock-container .unlock md-input-container {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0; }
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,587 +0,0 @@
|
|||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
.btn-default,
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
.btn-info,
|
||||
.btn-warning,
|
||||
.btn-danger {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-primary:active,
|
||||
.btn-success:active,
|
||||
.btn-info:active,
|
||||
.btn-warning:active,
|
||||
.btn-danger:active,
|
||||
.btn-default.active,
|
||||
.btn-primary.active,
|
||||
.btn-success.active,
|
||||
.btn-info.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active {
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-primary.disabled,
|
||||
.btn-success.disabled,
|
||||
.btn-info.disabled,
|
||||
.btn-warning.disabled,
|
||||
.btn-danger.disabled,
|
||||
.btn-default[disabled],
|
||||
.btn-primary[disabled],
|
||||
.btn-success[disabled],
|
||||
.btn-info[disabled],
|
||||
.btn-warning[disabled],
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
fieldset[disabled] .btn-primary,
|
||||
fieldset[disabled] .btn-success,
|
||||
fieldset[disabled] .btn-info,
|
||||
fieldset[disabled] .btn-warning,
|
||||
fieldset[disabled] .btn-danger {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-default .badge,
|
||||
.btn-primary .badge,
|
||||
.btn-success .badge,
|
||||
.btn-info .badge,
|
||||
.btn-warning .badge,
|
||||
.btn-danger .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
background-image: none;
|
||||
}
|
||||
.btn-default {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dbdbdb;
|
||||
border-color: #ccc;
|
||||
}
|
||||
.btn-default:hover,
|
||||
.btn-default:focus {
|
||||
background-color: #e0e0e0;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
border-color: #dbdbdb;
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-default[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
.btn-default.disabled:hover,
|
||||
.btn-default[disabled]:hover,
|
||||
fieldset[disabled] .btn-default:hover,
|
||||
.btn-default.disabled:focus,
|
||||
.btn-default[disabled]:focus,
|
||||
fieldset[disabled] .btn-default:focus,
|
||||
.btn-default.disabled.focus,
|
||||
.btn-default[disabled].focus,
|
||||
fieldset[disabled] .btn-default.focus,
|
||||
.btn-default.disabled:active,
|
||||
.btn-default[disabled]:active,
|
||||
fieldset[disabled] .btn-default:active,
|
||||
.btn-default.disabled.active,
|
||||
.btn-default[disabled].active,
|
||||
fieldset[disabled] .btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: #265a88;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-primary:active,
|
||||
.btn-primary.active {
|
||||
background-color: #265a88;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary.disabled,
|
||||
.btn-primary[disabled],
|
||||
fieldset[disabled] .btn-primary,
|
||||
.btn-primary.disabled:hover,
|
||||
.btn-primary[disabled]:hover,
|
||||
fieldset[disabled] .btn-primary:hover,
|
||||
.btn-primary.disabled:focus,
|
||||
.btn-primary[disabled]:focus,
|
||||
fieldset[disabled] .btn-primary:focus,
|
||||
.btn-primary.disabled.focus,
|
||||
.btn-primary[disabled].focus,
|
||||
fieldset[disabled] .btn-primary.focus,
|
||||
.btn-primary.disabled:active,
|
||||
.btn-primary[disabled]:active,
|
||||
fieldset[disabled] .btn-primary:active,
|
||||
.btn-primary.disabled.active,
|
||||
.btn-primary[disabled].active,
|
||||
fieldset[disabled] .btn-primary.active {
|
||||
background-color: #265a88;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success:hover,
|
||||
.btn-success:focus {
|
||||
background-color: #419641;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-success:active,
|
||||
.btn-success.active {
|
||||
background-color: #419641;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success.disabled,
|
||||
.btn-success[disabled],
|
||||
fieldset[disabled] .btn-success,
|
||||
.btn-success.disabled:hover,
|
||||
.btn-success[disabled]:hover,
|
||||
fieldset[disabled] .btn-success:hover,
|
||||
.btn-success.disabled:focus,
|
||||
.btn-success[disabled]:focus,
|
||||
fieldset[disabled] .btn-success:focus,
|
||||
.btn-success.disabled.focus,
|
||||
.btn-success[disabled].focus,
|
||||
fieldset[disabled] .btn-success.focus,
|
||||
.btn-success.disabled:active,
|
||||
.btn-success[disabled]:active,
|
||||
fieldset[disabled] .btn-success:active,
|
||||
.btn-success.disabled.active,
|
||||
.btn-success[disabled].active,
|
||||
fieldset[disabled] .btn-success.active {
|
||||
background-color: #419641;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info:hover,
|
||||
.btn-info:focus {
|
||||
background-color: #2aabd2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-info:active,
|
||||
.btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info.disabled,
|
||||
.btn-info[disabled],
|
||||
fieldset[disabled] .btn-info,
|
||||
.btn-info.disabled:hover,
|
||||
.btn-info[disabled]:hover,
|
||||
fieldset[disabled] .btn-info:hover,
|
||||
.btn-info.disabled:focus,
|
||||
.btn-info[disabled]:focus,
|
||||
fieldset[disabled] .btn-info:focus,
|
||||
.btn-info.disabled.focus,
|
||||
.btn-info[disabled].focus,
|
||||
fieldset[disabled] .btn-info.focus,
|
||||
.btn-info.disabled:active,
|
||||
.btn-info[disabled]:active,
|
||||
fieldset[disabled] .btn-info:active,
|
||||
.btn-info.disabled.active,
|
||||
.btn-info[disabled].active,
|
||||
fieldset[disabled] .btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning:hover,
|
||||
.btn-warning:focus {
|
||||
background-color: #eb9316;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-warning:active,
|
||||
.btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning.disabled,
|
||||
.btn-warning[disabled],
|
||||
fieldset[disabled] .btn-warning,
|
||||
.btn-warning.disabled:hover,
|
||||
.btn-warning[disabled]:hover,
|
||||
fieldset[disabled] .btn-warning:hover,
|
||||
.btn-warning.disabled:focus,
|
||||
.btn-warning[disabled]:focus,
|
||||
fieldset[disabled] .btn-warning:focus,
|
||||
.btn-warning.disabled.focus,
|
||||
.btn-warning[disabled].focus,
|
||||
fieldset[disabled] .btn-warning.focus,
|
||||
.btn-warning.disabled:active,
|
||||
.btn-warning[disabled]:active,
|
||||
fieldset[disabled] .btn-warning:active,
|
||||
.btn-warning.disabled.active,
|
||||
.btn-warning[disabled].active,
|
||||
fieldset[disabled] .btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus {
|
||||
background-color: #c12e2a;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-danger:active,
|
||||
.btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger.disabled,
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-danger,
|
||||
.btn-danger.disabled:hover,
|
||||
.btn-danger[disabled]:hover,
|
||||
fieldset[disabled] .btn-danger:hover,
|
||||
.btn-danger.disabled:focus,
|
||||
.btn-danger[disabled]:focus,
|
||||
fieldset[disabled] .btn-danger:focus,
|
||||
.btn-danger.disabled.focus,
|
||||
.btn-danger[disabled].focus,
|
||||
fieldset[disabled] .btn-danger.focus,
|
||||
.btn-danger.disabled:active,
|
||||
.btn-danger[disabled]:active,
|
||||
fieldset[disabled] .btn-danger:active,
|
||||
.btn-danger.disabled.active,
|
||||
.btn-danger[disabled].active,
|
||||
fieldset[disabled] .btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
background-image: none;
|
||||
}
|
||||
.thumbnail,
|
||||
.img-thumbnail {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-color: #e8e8e8;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-color: #2e6da4;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-default {
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .open > a,
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
|
||||
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-brand,
|
||||
.navbar-nav > li > a {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .open > a,
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
|
||||
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-inverse .navbar-brand,
|
||||
.navbar-inverse .navbar-nav > li > a {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-static-top,
|
||||
.navbar-fixed-top,
|
||||
.navbar-fixed-bottom {
|
||||
border-radius: 0;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||
color: #fff;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.alert-success {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b2dba1;
|
||||
}
|
||||
.alert-info {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #9acfea;
|
||||
}
|
||||
.alert-warning {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #f5e79e;
|
||||
}
|
||||
.alert-danger {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dca7a7;
|
||||
}
|
||||
.progress {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-striped {
|
||||
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
}
|
||||
.list-group {
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #286090;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.list-group-item.active .badge,
|
||||
.list-group-item.active:hover .badge,
|
||||
.list-group-item.active:focus .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.panel-default > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-info > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-warning > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-danger > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.well {
|
||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dcdcdc;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-theme.css.map */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,507 @@
|
|||
md-stepper {
|
||||
display: block;
|
||||
}
|
||||
md-steppers-header,
|
||||
md-steppers-mobile-header,
|
||||
md-step-actions {
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.md-steppers-header-region {
|
||||
z-index: 1;
|
||||
}
|
||||
@media (max-width: 599px) {
|
||||
md-stepper {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
md-stepper > div {
|
||||
min-width: 0px;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
.md-steppers-content {
|
||||
min-height: 0px;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
md-step.md-active {
|
||||
min-height: 0px;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
md-step.md-active .md-stepper {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
min-height: 0px;
|
||||
}
|
||||
md-steppers-scope {
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
md-step-body {
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.md-steppers {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-flow: column;
|
||||
-ms-flex-flow: column;
|
||||
flex-flow: column;
|
||||
}
|
||||
.md-steppers:not(.md-steppers-linear) .md-stepper-indicator:not(.md-active):not(.md-completed):hover,
|
||||
.md-steppers:not(.md-steppers-linear) .md-stepper-indicator.md-editable.md-completed:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.md-steppers:not(.md-steppers-linear) .md-stepper-indicator:not(.md-active):not(.md-completed):hover,
|
||||
.md-steppers:not(.md-steppers-linear) .md-stepper-indicator.md-editable.md-completed:hover,
|
||||
.md-steppers:not(.md-steppers-linear) .md-stepper-indicator:not(.md-active):not(.md-completed):hover .md-stepper-indicator-wrapper,
|
||||
.md-steppers:not(.md-steppers-linear) .md-stepper-indicator.md-editable.md-completed:hover .md-stepper-indicator-wrapper {
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
.md-steppers-header {
|
||||
margin: 0;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-flow: row nowrap;
|
||||
-ms-flex-flow: row nowrap;
|
||||
flex-flow: row nowrap;
|
||||
border-radius: 0;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.md-steppers-horizontal .md-stepper-indicator {
|
||||
min-height: 72px;
|
||||
}
|
||||
.md-steppers-horizontal .md-stepper-indicator:first-child .md-stepper-indicator-wrapper {
|
||||
padding-left: 16px;
|
||||
}
|
||||
.md-steppers-horizontal .md-stepper-indicator:after {
|
||||
width: 999em;
|
||||
height: 1px;
|
||||
margin-top: -1px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background-color: #E8E8E8;
|
||||
content: " ";
|
||||
}
|
||||
.md-stepper-indicator {
|
||||
margin: 0;
|
||||
padding: 0 16px;
|
||||
position: relative;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex: 1 0 auto;
|
||||
-ms-flex: 1 0 auto;
|
||||
flex: 1 0 auto;
|
||||
-webkit-align-content: flex-start;
|
||||
-ms-flex-line-pack: start;
|
||||
align-content: flex-start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start;
|
||||
overflow: hidden;
|
||||
cursor: default;
|
||||
background: none;
|
||||
border: none;
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
}
|
||||
.md-stepper-indicator:focus {
|
||||
outline: none;
|
||||
}
|
||||
.md-stepper-indicator.md-completed .md-stepper-number,
|
||||
.md-stepper-indicator.md-active .md-stepper-number {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
.md-stepper-indicator.md-completed .md-stepper-title,
|
||||
.md-stepper-indicator.md-active .md-stepper-title {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
.md-stepper-indicator.md-active .md-stepper-title,
|
||||
.md-stepper-indicator.md-editable .md-stepper-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
.md-stepper-indicator.md-error .md-stepper-title {
|
||||
color: #f44336;
|
||||
}
|
||||
.md-stepper-indicator:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
.md-stepper-indicator:last-child {
|
||||
padding-right: 0;
|
||||
-webkit-justify-content: flex-end;
|
||||
-ms-flex-pack: end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.md-stepper-indicator:last-child .md-stepper-indicator-wrapper {
|
||||
padding-right: 24px;
|
||||
}
|
||||
.md-stepper-indicator .md-ripple-container {
|
||||
z-index: 3;
|
||||
}
|
||||
.md-stepper-indicator-wrapper {
|
||||
padding: 0 8px 0 0;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-align-content: center;
|
||||
-ms-flex-line-pack: center;
|
||||
align-content: center;
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
}
|
||||
.md-stepper-number {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0 8px;
|
||||
border-radius: 24px;
|
||||
background-color: #BDBDBD;
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.md-stepper-number.ng-hide {
|
||||
transition: none;
|
||||
}
|
||||
.md-error .md-stepper-error-indicator {
|
||||
margin: 0 8px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.md-error .md-stepper-error-indicator md-icon {
|
||||
color: #f44336;
|
||||
}
|
||||
.md-error .md-stepper-error-message {
|
||||
color: #f44336;
|
||||
font-weight: 400;
|
||||
}
|
||||
.md-stepper-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.md-stepper-icon.md-stepper-icon-edit {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.md-stepper-title {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-flow: column nowrap;
|
||||
-ms-flex-flow: column nowrap;
|
||||
flex-flow: column nowrap;
|
||||
position: relative;
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.md-steppers-content {
|
||||
position: relative;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
.md-stepper {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.md-stepper.md-active {
|
||||
position: relative;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.md-steppers-actions {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.md-stepper-optional .md-stepper-title {
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.md-stepper-optional .md-stepper-title small:not(.md-stepper-error-message) {
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
.md-stepper-optional .md-stepper-title small {
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
font-size: 12px;
|
||||
line-height: 1em;
|
||||
}
|
||||
.md-steppers-linear .md-stepper-indicator {
|
||||
cursor: default;
|
||||
}
|
||||
.md-steppers-linear .md-stepper-indicator.md-editable.md-completed:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.md-steppers-linear .md-stepper-indicator.md-editable.md-completed:hover,
|
||||
.md-steppers-linear .md-stepper-indicator.md-editable.md-completed:hover .md-stepper-indicator-wrapper {
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
.md-steppers-linear .md-stepper-indicator.md-editable.md-completed:hover .md-stepper-number:before,
|
||||
.md-steppers-linear .md-stepper-indicator.md-editable.md-completed:hover .md-stepper-number:after {
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
.md-steppers-linear .md-stepper-title {
|
||||
color: rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
.md-steppers-linear .md-stepper-title small:not(.md-stepper-error-message) {
|
||||
color: rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
.md-steppers-alternative:not(.md-steppers-linear) .md-stepper-indicator:not(.md-active):not(.md-completed):hover .md-stepper-indicator-wrapper {
|
||||
background: none;
|
||||
}
|
||||
.md-steppers-alternative:not(.md-steppers-linear) .md-stepper-indicator:not(.md-active):not(.md-completed):hover .md-stepper-number:before,
|
||||
.md-steppers-alternative:not(.md-steppers-linear) .md-stepper-indicator:not(.md-active):not(.md-completed):hover .md-stepper-number:after {
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-indicator {
|
||||
min-height: 104px;
|
||||
padding: 24px 16px;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-indicator .md-stepper-indicator-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-indicator:first-child {
|
||||
padding-left: 24px;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-indicator:first-child:after {
|
||||
left: 50%;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-indicator:last-child {
|
||||
padding-right: 24px;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-indicator:last-child:after {
|
||||
right: 50%;
|
||||
left: auto;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-indicator:after {
|
||||
margin-top: 0;
|
||||
top: 36px;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-indicator-wrapper {
|
||||
padding: 0 16px;
|
||||
min-height: 50px;
|
||||
-webkit-flex-flow: column nowrap;
|
||||
-ms-flex-flow: column nowrap;
|
||||
flex-flow: column nowrap;
|
||||
background: none;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-number,
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-error-indicator {
|
||||
position: relative;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-number:before,
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-error-indicator:before,
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-number:after,
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-error-indicator:after {
|
||||
width: 8px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #fff;
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
content: " ";
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-number:before,
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-error-indicator:before {
|
||||
left: -8px;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-number:after,
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-error-indicator:after {
|
||||
right: -8px;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-title {
|
||||
margin-top: 16px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
.md-steppers-alternative .md-steppers-horizontal .md-stepper-title small {
|
||||
text-align: center;
|
||||
}
|
||||
.md-steppers:not(.md-steppers-vertical) .md-steppers-header.md-steppers-vertical {
|
||||
display: none;
|
||||
}
|
||||
.md-steppers-vertical .md-stepper {
|
||||
padding: 8px 24px;
|
||||
}
|
||||
.md-steppers-vertical .md-steppers-scope {
|
||||
margin-left: 20px;
|
||||
border-left: 1px solid #E8E8E8;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.md-steppers-vertical .md-steppers-header.md-steppers-horizontal {
|
||||
display: none;
|
||||
}
|
||||
.md-steppers-mobile-header,
|
||||
.md-stepper-feedback-message {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 599px) {
|
||||
.md-steppers-mobile-step-text:not(.md-steppers-vertical) .md-stepper {
|
||||
padding: 0;
|
||||
}
|
||||
.md-steppers-mobile-step-text:not(.md-steppers-vertical) .md-steppers-mobile-header {
|
||||
display: block;
|
||||
}
|
||||
.md-steppers-mobile-step-text:not(.md-steppers-vertical) .md-stepper-feedback-message {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.md-steppers-mobile-step-text:not(.md-steppers-vertical) .md-steppers-header.md-steppers-horizontal {
|
||||
display: none;
|
||||
}
|
||||
.md-steppers-mobile-step-text:not(.md-steppers-vertical) md-step-actions {
|
||||
background: #f6f6f6 !important;
|
||||
color: #202020 !important;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-content: stretch;
|
||||
-ms-flex-line-pack: stretch;
|
||||
align-content: stretch;
|
||||
-webkit-align-items: stretch;
|
||||
-ms-flex-align: stretch;
|
||||
align-items: stretch;
|
||||
-webkit-justify-content: space-between;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 3px 0px, rgba(0, 0, 0, 0.137255) 0px 1px 1px 0px, rgba(0, 0, 0, 0.117647) 0px 2px 1px -1px;
|
||||
}
|
||||
}
|
||||
.md-steppers-header-region {
|
||||
position: relative;
|
||||
}
|
||||
.md-stepper-feedback-message {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
z-index: 2;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.md-steppers {
|
||||
position: relative;
|
||||
}
|
||||
md-step-body {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
.md-step-body-loading {
|
||||
display: none;
|
||||
}
|
||||
.md-steppers-has-feedback .md-stepper-feedback-message {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.md-steppers-has-feedback .md-steppers-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
.md-steppers-has-feedback .md-step-body-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 11;
|
||||
opacity: 0.75;
|
||||
background-color: white;
|
||||
}
|
||||
.md-steppers-has-feedback .md-step-body-loading {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 12;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-align-content: center;
|
||||
-ms-flex-line-pack: center;
|
||||
align-content: center;
|
||||
max-width: 100%;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
}
|
|
@ -1,56 +1,134 @@
|
|||
<!doctype html>
|
||||
<html ng-app="passmanExtension" ng-csp>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Passman rules</title>
|
||||
<script src="/js/vendor/jquery/jquery.js"></script>
|
||||
<script src="/js/vendor/bootstrap/bootstrap.js"></script>
|
||||
<script src="/js/vendor/angular/framework.js"></script>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Passman rules</title>
|
||||
<script src="/js/vendor/jquery/jquery.js"></script>
|
||||
<script src="/js/vendor/angular/framework.js"></script>
|
||||
|
||||
<script src="/js/lib/promise.js"></script>
|
||||
<script src="/js/lib/API/base.js"></script>
|
||||
<script src="/js/lib/API/storage.js"></script>
|
||||
<script src="/js/lib/API/runtime.js"></script>
|
||||
<script src="/js/lib/API/tabs.js"></script>
|
||||
<script src="/js/lib/API/cookies.js"></script>
|
||||
<script src="/js/lib/API/extension.js"></script>
|
||||
<script src="/js/lib/API/i18n.js"></script>
|
||||
<script src="/js/lib/otp.js"></script>
|
||||
<script src="/js/lib/font-awesome.js"></script>
|
||||
<script src="/js/vendor/sjcl/sjcl.js"></script>
|
||||
<script src="/js/vendor/angular-resource/angular-resource.js"></script>
|
||||
<script src="/js/lib/promise.js"></script>
|
||||
<script src="/js/lib/API/base.js"></script>
|
||||
<script src="/js/lib/API/storage.js"></script>
|
||||
<script src="/js/lib/API/runtime.js"></script>
|
||||
<script src="/js/lib/API/tabs.js"></script>
|
||||
<script src="/js/lib/API/cookies.js"></script>
|
||||
<script src="/js/lib/API/extension.js"></script>
|
||||
<script src="/js/lib/API/i18n.js"></script>
|
||||
<script src="/js/lib/otp.js"></script>
|
||||
<script src="/js/lib/font-awesome.js"></script>
|
||||
<script src="/js/lib/passwordgen.js"></script>
|
||||
<script src="/js/vendor/sjcl/sjcl.js"></script>
|
||||
<script src="/js/vendor/angular-resource/angular-resource.js"></script>
|
||||
|
||||
<script src="/js/vendor/angular-route/angular-route.js"></script>
|
||||
<script src="/js/vendor/angular-sanitize/angular-sanitize.js"></script>
|
||||
<script src="/js/vendor/angular-translate/angular-translate.js"></script>
|
||||
<script src="/js/vendor/angular-steps/angular-steps.js"></script>
|
||||
<script src="/js/vendor/sha/sha.js"></script>
|
||||
<script src="/js/vendor/angular-route/angular-route.js"></script>
|
||||
<script src="/js/vendor/angular-aria/angular-aria.js"></script>
|
||||
<script src="/js/vendor/angular-messages/angular-messages.js"></script>
|
||||
<script src="/js/vendor/angular-animate/angular-animate.js"></script>
|
||||
<script src="/js/vendor/angular-sanitize/angular-sanitize.js"></script>
|
||||
<script src="/js/vendor/angular-translate/angular-translate.js"></script>
|
||||
<script src="/js/vendor/angular-material/angular-material.min.js"></script>
|
||||
<script src="/js/vendor/sha/sha.js"></script>
|
||||
<script src="/js/vendor/material-steppers/material-steppers.js"></script>
|
||||
|
||||
<script src="/js/lib/api.js"></script>
|
||||
|
||||
<script src="/js/ui/popup/app.js"></script>
|
||||
|
||||
<script src="/js/ui/popup/factories/storage.js"></script>
|
||||
<script src="/js/ui/popup/factories/debounce.js"></script>
|
||||
|
||||
<script src="/js/ui/popup/controllers/main.js"></script>
|
||||
<script src="/js/ui/popup/controllers/settings.js"></script>
|
||||
<script src="/js/ui/popup/controllers/password_prompt.js"></script>
|
||||
<script src="/js/ui/popup/controllers/search.js"></script>
|
||||
<script src="/js/ui/popup/controllers/setup.js"></script>
|
||||
<script src="/js/ui/popup/directives/otp.js"></script>
|
||||
<script src="/js/ui/popup/directives/ngenter.js"></script>
|
||||
<script src="/js/ui/popup/filters/translate.js"></script>
|
||||
<script src="/js/lib/api.js"></script>
|
||||
|
||||
|
||||
<link href="/css/vendor/bootstrap.css" media="all" rel="stylesheet" />
|
||||
<link href="/css/vendor/bootstrap-theme.css" media="all" rel="stylesheet" />
|
||||
<link href="/css/vendor/font-awesome.css" media="all" rel="stylesheet" />
|
||||
<link href="/css/main.css" media="all" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mainPopup" ng-view>
|
||||
<script src="/js/ui/popup/app.js"></script>
|
||||
|
||||
<script src="/js/ui/popup/factories/storage.js"></script>
|
||||
<script src="/js/ui/popup/factories/debounce.js"></script>
|
||||
|
||||
<script src="/js/ui/popup/controllers/main.js"></script>
|
||||
<script src="/js/ui/popup/controllers/list.js"></script>
|
||||
<script src="/js/ui/popup/controllers/edit.js"></script>
|
||||
<script src="/js/ui/popup/controllers/settings.js"></script>
|
||||
<script src="/js/ui/popup/controllers/password_prompt.js"></script>
|
||||
<script src="/js/ui/popup/controllers/search.js"></script>
|
||||
<script src="/js/ui/popup/controllers/setup.js"></script>
|
||||
<script src="/js/ui/popup/directives/otp.js"></script>
|
||||
<script src="/js/ui/popup/directives/copyText.js"></script>
|
||||
<script src="/js/ui/popup/directives/ngenter.js"></script>
|
||||
<script src="/js/ui/popup/filters/translate.js"></script>
|
||||
|
||||
|
||||
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link href="/css/vendor/font-awesome.css" media="all" rel="stylesheet"/>
|
||||
<link href="/css/vendor/angular-material.min.css" media="all" rel="stylesheet"/>
|
||||
<link href="/css/vendor/material-steppers.css" media="all" rel="stylesheet"/>
|
||||
<link href="/css/material.css" media="all" rel="stylesheet"/>
|
||||
<!--<link href="/css/main.css" media="all" rel="stylesheet" /> -->
|
||||
</head>
|
||||
<body ng-controller="MainCtrl">
|
||||
<section layout="column" flex="">
|
||||
<md-toolbar class="md-hue-2" ng-if="toolbarShown">
|
||||
<div class="md-toolbar-tools">
|
||||
<md-button class="md-icon-button" aria-label="menu" ng-click="toggleLeft()">
|
||||
<md-icon>menu</md-icon>
|
||||
</md-button>
|
||||
<h2>
|
||||
<span>Passman</span>
|
||||
</h2>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button" aria-label="Lock" ng-click="lockExtension()">
|
||||
<md-tooltip md-direction="bottom">{{'lock_extension' | translate}}</md-tooltip>
|
||||
<md-icon>lock</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</body>
|
||||
</md-toolbar>
|
||||
|
||||
<md-sidenav class="md-sidenav-left" md-component-id="left" md-is-locked-open="$mdMedia('gt-md')" ng-if="toolbarShown"
|
||||
md-disable-backdrop="" md-whiteframe="4">
|
||||
|
||||
<md-toolbar class="md-hue-2">
|
||||
<div class="md-toolbar-tools">
|
||||
<h2>
|
||||
<span>Menu</span>
|
||||
</h2>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button" aria-label="Close" ng-click="toggleLeft()">
|
||||
<md-icon>close</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-content class="menu" layout-padding="">
|
||||
<ul>
|
||||
<li>
|
||||
<a ng-click="goto_list()">
|
||||
<md-icon>list</md-icon>
|
||||
{{'list' | translate}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="goto_search()">
|
||||
<md-icon>search</md-icon>
|
||||
{{'search' | translate}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="goto_settings()">
|
||||
<md-icon>settings</md-icon>
|
||||
{{'settings' | translate}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6YS8F97PETVU2" target="_blank">
|
||||
<md-icon>payment</md-icon>
|
||||
Donate
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</md-content>
|
||||
|
||||
</md-sidenav>
|
||||
|
||||
|
||||
<md-content layout="column" class="container" layout-fill>
|
||||
<div ng-view></div>
|
||||
</md-content>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,121 @@
|
|||
<md-content layout="column" class="container" layout-fill>
|
||||
<md-tabs md-dynamic-height md-border-bottom>
|
||||
<md-tab label="General">
|
||||
<md-content class="md-padding" ng-init="pwFieldShown = false;">
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<label>{{'label' | translate}}</label>
|
||||
<input ng-model="credential.label" required>
|
||||
<copy-text text="credential.label"></copy-text>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'username' | translate}}</label>
|
||||
<input ng-model="credential.username"/>
|
||||
<copy-text text="credential.username"></copy-text>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'email' | translate}}</label>
|
||||
<input ng-model="credential.email"/>
|
||||
<copy-text text="credential.email"></copy-text>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block" ng-if="!pwFieldShown">
|
||||
<label>{{'password' | translate}}</label>
|
||||
<input type="password" ng-model="credential.password" style="width: 325px;" >
|
||||
<md-icon class="pointer" ng-click="generatePassword()" style="left: initial; right: 35px;">refresh</md-icon>
|
||||
<md-icon class="pointer" style="left: initial; right: 70px;" ng-click="togglePwField()">remove_red_eye</md-icon>
|
||||
<copy-text text="credential.password"></copy-text>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block" ng-if="pwFieldShown">
|
||||
<label>{{'password' | translate}}</label>
|
||||
<input type="text" ng-model="credential.password" style="width: 325px;" >
|
||||
<md-icon class="pointer" ng-click="generatePassword()" style="left: initial; right: 35px;">refresh</md-icon>
|
||||
<md-icon class="pointer" style="left: initial; right: 70px;" ng-click="togglePwField()">remove_red_eye</md-icon>
|
||||
<copy-text text="credential.password"></copy-text>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'password_repeat' | translate}}</label>
|
||||
<input type="password" ng-model="credential.password_repeat">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block" style="margin-bottom: -5px;">
|
||||
<label>{{'url' | translate}}</label>
|
||||
<input ng-model="credential.url"/>
|
||||
<copy-text text="credential.url"></copy-text>
|
||||
</md-input-container>
|
||||
</md-content>
|
||||
</md-tab>
|
||||
<md-tab label="Custom fields">
|
||||
<md-content class="md-padding" style="overflow-x: hidden;">
|
||||
|
||||
<div layout="row" layout-align="space-around" ng-repeat="custom_field in credential.custom_fields" ng-if="custom_field.field_type !== 'file'">
|
||||
<md-input-container flex="40">
|
||||
<label>Label</label>
|
||||
<input required ng-model="custom_field.label">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="40" ng-if="!custom_field.secret">
|
||||
<label>Value</label>
|
||||
<input required ng-model="custom_field.value">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="40" ng-if="custom_field.secret">
|
||||
<label>Value</label>
|
||||
<input type="password" ng-model="custom_field.value">
|
||||
</md-input-container>
|
||||
<md-input-container flex="10">
|
||||
<md-button class="md-secondary md-icon-button" ng-click="deleteCustomField(custom_field)"
|
||||
aria-label="call">
|
||||
<md-tooltip md-direction="left">{{'delete' | translate}}</md-tooltip>
|
||||
<md-icon>delete</md-icon>
|
||||
</md-button>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<div layout="row">
|
||||
Add custom field
|
||||
</div>
|
||||
<div layout="row" layout-align="space-around">
|
||||
|
||||
<md-input-container flex="25">
|
||||
<label>Label</label>
|
||||
<input ng-model="new_custom_field.label">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="25" ng-if="new_custom_field.field_type === 'text'">
|
||||
<label>Value</label>
|
||||
<input ng-model="new_custom_field.value">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="25" ng-if="new_custom_field.field_type === 'password'">
|
||||
<label>Value</label>
|
||||
<input type="password" ng-model="new_custom_field.value">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="25">
|
||||
<label>Type</label>
|
||||
<md-select name="type" ng-model="new_custom_field.field_type">
|
||||
<md-option value="text">Text</md-option>
|
||||
<md-option value="password">Password</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<md-input-container flex="10">
|
||||
<md-button class="md-secondary md-icon-button" ng-click="addCustomField(new_custom_field)">
|
||||
<md-icon>add_box</md-icon>
|
||||
<md-tooltip md-direction="left">{{'add' | translate}}</md-tooltip>
|
||||
</md-button>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</md-content>
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
|
||||
<div layout="row" layout-sm="column" layout-align="space-around">
|
||||
<md-button class="md-raised md-primary" ng-click="saveCredential()" ng-disabled="saving"></span>{{'save' |
|
||||
translate}}
|
||||
</md-button>
|
||||
<md-button class="md-raised md-hue-1" ng-click="cancel()">{{'cancel' | translate}}</md-button>
|
||||
</div>
|
||||
</md-content>
|
|
@ -1,32 +1,39 @@
|
|||
<div class="col-xs-12 nopadding pwcontainer">
|
||||
<div ng-show="found_credentials.length === 0">
|
||||
{{ 'no_credentials_found_for_site' | translate}}
|
||||
</div>
|
||||
<div ng-show="found_credentials.length > 0">{{'credentials_found_for_site' | translate:found_credentials.length.toString() }}</div>
|
||||
<div class="credential" ng-repeat="credential in found_credentials">
|
||||
<div class="col-xs-7 nopadding">{{credential.label}}<br/>
|
||||
<small>{{credential.username}}</small>
|
||||
<md-list class="password-list" flex>
|
||||
<md-list-item class="md-3-line" ng-repeat="credential in found_credentials">
|
||||
<!--<img ng-src="{{item.face}}?{{$index}}" class="md-avatar" alt="{{item.who}}"/>-->
|
||||
<div class="md-list-item-text" layout="column">
|
||||
<h3>{{credential.label}}</h3>
|
||||
<h4>{{credential.username}}</h4>
|
||||
<p ng-if="credential.otp.secret">OTP: <span otp-generator secret="credential.otp.secret"></span></p>
|
||||
</div>
|
||||
<div class="col-xs-5 OTP" ng-if="credential.otp.secret">
|
||||
{{'one_time_password' | translate}}: <div otp-generator secret="credential.otp.secret"></div>
|
||||
<md-button class="md-secondary md-icon-button" ng-click="edit(credential)" aria-label="call">
|
||||
<md-tooltip md-direction="left">{{'edit' | translate}}</md-tooltip>
|
||||
<md-icon>edit</md-icon>
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item ng-if="found_credentials.length === 0">
|
||||
<div class="md-list-item-text" layout="column">
|
||||
<h3>{{ 'no_credentials_found_for_site' | translate}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div style="position: absolute; left: 50%; margin-top: -7px; width: 110px;">
|
||||
<div style="position: relative; left: -50%; margin-top: 1.3em;">
|
||||
<small>{{'credentials_in_db' | translate:credential_amount}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottomBtn" ng-click="goto_settings()" title="{{'settings' | translate}}"><i class="fa fa-gears"></i></div>
|
||||
<div class="bottomBtn" ng-click="goto_search()" title="{{'search' | translate}}"><i class="fa fa-search"></i></div>
|
||||
<div class="bottomBtn" ng-click="refresh()" title="{{'refresh_credential_list' | translate}}"><i class="fa fa-refresh" ng-class="{'fa-spin': refreshing_credentials}"></i></div>
|
||||
<div class="bottomBtn pull-right"> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6YS8F97PETVU2" target="_blank"
|
||||
title="{{'donate_button_title' | translate}}"
|
||||
rel="nofollow noopener noreferrer"
|
||||
><i class="fa fa-paypal"></i> </a>
|
||||
</div>
|
||||
<div class="bottomBtn pull-right" ng-click="lockExtension()" title="{{'lock_extension' | translate}}"><i class="fa fa-lock"></i></div>
|
||||
<!-- <a class="btn btn-success pull-right"
|
||||
>Donate</a> -->
|
||||
</div>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
|
||||
<!--<div class="footer">-->
|
||||
<!--<div style="position: absolute; left: 50%; margin-top: -7px; width: 110px;">-->
|
||||
<!--<div style="position: relative; left: -50%; margin-top: 1.3em;">-->
|
||||
<!--<small>{{'credentials_in_db' | translate:credential_amount}}</small>-->
|
||||
<!--</div>-->
|
||||
<!--</div>-->
|
||||
<!--<!–-->
|
||||
<!--<div class="bottomBtn" ng-click="goto_settings()" title="{{'settings' | translate}}"><i class="fa fa-gears"></i></div>-->
|
||||
<!--<div class="bottomBtn" ng-click="goto_search()" title="{{'search' | translate}}"><i class="fa fa-search"></i></div>-->
|
||||
<!--<div class="bottomBtn" ng-click="refresh()" title="{{'refresh_credential_list' | translate}}"><i class="fa fa-refresh" ng-class="{'fa-spin': refreshing_credentials}"></i></div>-->
|
||||
<!--<div class="bottomBtn pull-right"> <a -->
|
||||
<!--title="{{'donate_button_title' | translate}}"-->
|
||||
<!--rel="nofollow noopener noreferrer"-->
|
||||
<!--><i class="fa fa-paypal"></i> </a>-->
|
||||
<!--</div>-->
|
||||
<!--<div class="bottomBtn pull-right" ng-click="lockExtension()" title="{{'lock_extension' | translate}}"><i class="fa fa-lock"></i></div>-->
|
||||
<!--<!– <a class="btn btn-success pull-right"-->
|
||||
<!-->Donate</a> –>-->
|
||||
<!--</div>-->
|
|
@ -1,15 +1,43 @@
|
|||
<div class="content unlock" style="max-height: 160px; overflow: auto">
|
||||
<div>
|
||||
<i class="fa fa-lock fa-5x"></i>
|
||||
<div>{{'extension_locked'| translate}}:</div>
|
||||
<input class="form-control" type="password" ng-enter="apply_settings()" ng-model="master_password" />
|
||||
<label><input type="checkbox" ng-model="master_password_remember">Remember master password</label>
|
||||
</div>
|
||||
<div layout="row" layout-align="center center" class="unlock-container">
|
||||
|
||||
<div class="unlock">
|
||||
<div style="width: 100%; text-align: center; margin-bottom: 15px;">
|
||||
<img src="/icons/Passman_Color_Horizontal.png" height="40"/>
|
||||
</div>
|
||||
<div>
|
||||
<span >{{'extension_locked'| translate}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<md-input-container style="width: 100%;">
|
||||
|
||||
<input class="form-control" aria-label="password" type="password" ng-enter="apply_settings()" ng-model="master_password" style="width: 100%;"/>
|
||||
</md-input-container>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-show="inValidPassword" class="error">
|
||||
{{'invalid_master_password' | translate}}
|
||||
{{'invalid_master_password' | translate}}
|
||||
</div>
|
||||
<button class="btn btn-default stepNext" ng-click="apply_settings()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i><i ng-show="!saving" class="fa fa-unlock"></i> {{'unlock' | translate}}</button>
|
||||
</div>
|
||||
|
||||
<div layout="row" layout-sm="column" layout-align="space-around">
|
||||
<md-button class="md-raised md-primary" ng-click="apply_settings()" ng-disabled="saving"></span>
|
||||
<i ng-show="saving"
|
||||
ng-class="{'fa-spinner fa-spin': saving}"
|
||||
class="fa"></i><i
|
||||
ng-show="!saving" class="fa fa-unlock"></i>
|
||||
{{'unlock' | translate}}
|
||||
</md-button>
|
||||
|
||||
</div>
|
||||
<br />
|
||||
<div layout="row" layout-sm="column" layout-align="space-around">
|
||||
<md-input-container>
|
||||
<md-checkbox class="md-primary" md-no-ink aria-label=" {{'remember_master_password'| translate}}" ng-model="master_password_remember">
|
||||
{{'remember_master_password'| translate}}
|
||||
</md-checkbox>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,38 +1,25 @@
|
|||
<div class="col-xs-12 nopadding pwcontainer">
|
||||
<div class="searchContainer">
|
||||
<input type="text" ng-model="searchText" placeholder="{{'search_for' | translate }}..." class="form-control" ng-enter="search()"/>
|
||||
</div>
|
||||
<div ng-show="found_credentials.length === 0">
|
||||
{{'no_credentials_found' | translate}}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="credential" ng-repeat="credential in found_credentials">
|
||||
<div class="col-xs-7 nopadding">{{credential.label}}<br/>
|
||||
<small>{{credential.username}}</small>
|
||||
<small>{{credential.email}}</small>
|
||||
<div layout-padding>
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'search_for' | translate }}</label>
|
||||
<input ng-model="searchText" ng-enter="search()">
|
||||
</md-input-container>
|
||||
<md-list class="password-list" ng-show="found_credentials.length > 0">
|
||||
<md-list-item class="md-3-line" ng-repeat="credential in found_credentials">
|
||||
<!--<img ng-src="{{item.face}}?{{$index}}" class="md-avatar" alt="{{item.who}}"/>-->
|
||||
<div class="md-list-item-text" layout="column">
|
||||
<h3>{{credential.label}}</h3>
|
||||
<h4>{{credential.username}}</h4>
|
||||
<p ng-if="credential.otp.secret">OTP: <span otp-generator secret="credential.otp.secret"></span></p>
|
||||
</div>
|
||||
<md-button class="md-secondary md-icon-button" ng-click="edit(credential)" aria-label="call">
|
||||
<md-tooltip md-direction="left">{{'edit' | translate}}</md-tooltip>
|
||||
<md-icon>edit</md-icon>
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
<md-list-item ng-if="found_credentials.length === 0">
|
||||
<div class="md-list-item-text" layout="column">
|
||||
<h3>{{ 'no_credentials_found' | translate}}</h3>
|
||||
</div>
|
||||
<div class="col-xs-5 OTP" ng-if="credential.otp.secret">
|
||||
{{'one_time_password' | translate}}: <div otp-generator secret="credential.otp.secret"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div style="position: absolute; left: 50%; margin-top: -7px; width: 110px;">
|
||||
<div style="position: relative; left: -50%; margin-top: 1.3em;">
|
||||
<small>{{'credentials_in_db' | translate:credential_amount}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottomBtn" ng-click="goto_settings()" title="{{'settings' | translate}}"><i class="fa fa-gears"></i></div>
|
||||
<div class="bottomBtn" ng-click="goto_search()" title="{{'search' | translate}}"><i class="fa fa-search"></i></div>
|
||||
<div class="bottomBtn" ng-click="refresh()" title="{{'refresh_credential_list' | translate}}"><i class="fa fa-refresh" ng-class="{'fa-spin': refreshing_credentials}"></i></div>
|
||||
<div class="bottomBtn pull-right"> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6YS8F97PETVU2" target="_blank"
|
||||
title="{{'donate_button_title' | translate}}"
|
||||
rel="nofollow noopener noreferrer"
|
||||
><i class="fa fa-paypal"></i> </a>
|
||||
</div>
|
||||
<div class="bottomBtn pull-right" ng-click="lockExtension()" title="{{'lock_extension' | translate}}"><i class="fa fa-lock"></i></div>
|
||||
<!-- <a class="btn btn-success pull-right"
|
||||
>Donate</a> -->
|
||||
</md-list-item>
|
||||
</div>
|
|
@ -1,10 +1,80 @@
|
|||
|
||||
<h4 align="center">{{'please_enter_nextcloud_credentials' | translate}}:</h4>
|
||||
<div class="alerts alert alert-warning" ng-if="errors.length > 0">
|
||||
<li ng-repeat="error in errors">{{error}}</li>
|
||||
</div>
|
||||
<div style="max-height: 350px; overflow-y: auto;">
|
||||
<table class="table">
|
||||
|
||||
<h4 align="center">{{'please_enter_nextcloud_credentials' | translate}}:</h4>
|
||||
<md-content layout-padding>
|
||||
<div layout-gt-sm="row">
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<label>{{'server_url' | translate}}</label>
|
||||
<input ng-model="settings.nextcloud_host" required ng-debounce="1000">
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'username' | translate}}</label>
|
||||
<input ng-model="settings.nextcloud_username" required ng-debounce="1000">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'password' | translate}}</label>
|
||||
<input type="password" ng-model="settings.nextcloud_password" required ng-debounce="1000">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block" ng-if="vaults">
|
||||
<label>{{'select_default_vault' | translate}}</label>
|
||||
<md-select ng-model="settings.default_vault.guid">
|
||||
<md-option ng-repeat="vault in vaults" value="{{vault.guid}}"
|
||||
ng-selected="settings.default_vault.vault_id === vault.vault_id">
|
||||
{{vault.name}}
|
||||
</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" ng-show="settings.default_vault != ''">
|
||||
<label>{{'vault_password' | translate}}</label>
|
||||
<input type="password" ng-model="settings.vault_password" required>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'refresh_timer' | translate}}</label>
|
||||
<input type="text" ng-model="settings.refreshTime" required>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<md-switch class="md-primary" name="special" ng-model="settings.ignoreProtocol">
|
||||
{{'ignore_protocol' | translate}}
|
||||
</md-switch>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<md-switch class="md-primary" name="special" ng-model="settings.ignoreSubdomain">
|
||||
{{'ignore_subdomain' | translate}}
|
||||
</md-switch>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<md-switch class="md-primary" name="special" ng-model="settings.ignorePort">
|
||||
{{'ignore_port' | translate}}
|
||||
</md-switch>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<md-switch class="md-primary" name="special" ng-model="settings.disableAutoFill">
|
||||
{{'disable_autofill' | translate}}
|
||||
</md-switch>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<md-switch class="md-primary" name="special" ng-model="settings.disablePasswordPicker">
|
||||
{{'disable_password_picker' | translate}}
|
||||
</md-switch>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<md-switch class="md-primary" name="special" ng-model="settings.debug">
|
||||
{{'enable_debug' | translate}}
|
||||
</md-switch>
|
||||
</md-input-container>
|
||||
<div layout="row" layout-sm="column" layout-align="space-around">
|
||||
<md-button class="md-raised md-primary" ng-click="saveSettings()" ng-disabled="saving"></span>{{'save' |
|
||||
translate}}
|
||||
</md-button>
|
||||
<md-button class="md-raised" ng-click="cancel()">{{'cancel' | translate}}</md-button>
|
||||
</div>
|
||||
</div>
|
||||
</md-content>
|
||||
<!-- <table class="table">
|
||||
<tr>
|
||||
<td><small>{{'server_url' | translate}}:</small></td>
|
||||
<td><input type="url" class="form-control input-sm" id="host" ng-model="settings.nextcloud_host" required ng-debounce="1000">
|
||||
|
@ -74,8 +144,8 @@
|
|||
<button class="btn btn-warning" ng-click="cancel()">{{'cancel' | translate}}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="version">
|
||||
{{extension}}
|
||||
</div>
|
||||
</table> -->
|
||||
<div class="version">
|
||||
{{extension}}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,52 +1,102 @@
|
|||
<div class="alerts alert alert-warning" ng-if="errors.length > 0">
|
||||
<li ng-repeat="error in errors">{{error}}</li>
|
||||
</div>
|
||||
<steps on-finish="finished()" class="setup">
|
||||
<step class="step1" name="welcome">
|
||||
<h2>{{'welcome_to_passman' | translate}}</h2>
|
||||
{{'intro_text' | translate}}
|
||||
<button class="btn btn-default stepNext" ng-click="next()">{{'begin' | translate}}</button>
|
||||
</step>
|
||||
<step class="step2" name="server">
|
||||
<h2>{{'nextcloud_settings' | translate}}</h2>
|
||||
<label>{{'server_url' | translate}}</label>
|
||||
<input type="text" ng-model="settings.nextcloud_host"/>
|
||||
<label>{{'username' | translate}}</label>
|
||||
<input type="text" ng-model="settings.nextcloud_username"/>
|
||||
<label>{{'password' | translate}}</label>
|
||||
<input type="password" ng-model="settings.nextcloud_password" />
|
||||
<div layout-padding style="height: 520px; width: 500px;">
|
||||
<md-stepper id="setup-stepper" md-linear="false" md-vertical="true">
|
||||
<md-step md-label="{{'welcome_to_passman' | translate}}">
|
||||
<md-step-body>
|
||||
<p>{{'intro_text' | translate}}</p>
|
||||
</md-step-body>
|
||||
<md-step-actions>
|
||||
<md-button class="md-primary md-raised" ng-click="nextStep();">
|
||||
<i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i>
|
||||
Continue
|
||||
</md-button>
|
||||
</md-step-actions>
|
||||
</md-step>
|
||||
<md-step md-label="{{ 'server_settings' | translate}}" class="server_settings">
|
||||
<md-step-body style="overflow: hidden;">
|
||||
<md-input-container>
|
||||
<label>{{'server_url' | translate}}</label>
|
||||
<input ng-model="settings.nextcloud_host" required>
|
||||
</md-input-container>
|
||||
<md-input-container>
|
||||
<label>{{'username' | translate}}</label>
|
||||
<input ng-model="settings.nextcloud_username" required>
|
||||
</md-input-container>
|
||||
|
||||
<button step-previous class="btn btn-default">{{'prev' | translate}}</button>
|
||||
<md-input-container>
|
||||
<label>{{'password' | translate}}</label>
|
||||
<input type="password" ng-model="settings.nextcloud_password" required>
|
||||
</md-input-container>
|
||||
</md-step-body>
|
||||
<md-step-actions>
|
||||
<md-button class="md-primary md-raised" ng-click="nextStep('server');">
|
||||
<i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i>
|
||||
{{ 'continue' | translate}}
|
||||
</md-button>
|
||||
<md-button class="md-primary" ng-click="previousStep();">{{ 'back' | translate}}</md-button>
|
||||
</md-step-actions>
|
||||
</md-step>
|
||||
<md-step md-label="{{'vault_settings' | translate}}">
|
||||
<md-step-body style="overflow: hidden;">
|
||||
<md-input-container class="md-block" ng-if="vaults">
|
||||
<label>{{'select_default_vault' | translate}}</label>
|
||||
<md-select ng-model="settings.default_vault.guid">
|
||||
<md-option ng-repeat="vault in vaults" value="{{vault.guid}}"
|
||||
ng-selected="settings.default_vault.vault_id === vault.vault_id">
|
||||
{{vault.name}}
|
||||
</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'vault_password' | translate}}</label>
|
||||
<input type="password" ng-model="settings.vault_password" required>
|
||||
</md-input-container>
|
||||
</md-step-body>
|
||||
<md-step-actions>
|
||||
<md-button class="md-primary md-raised" ng-click="nextStep('vault');">
|
||||
<i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i>
|
||||
{{ 'continue' | translate}}
|
||||
</md-button>
|
||||
<md-button class="md-primary" ng-click="previousStep();">{{ 'back' | translate}}</md-button>
|
||||
</md-step-actions>
|
||||
</md-step>
|
||||
<md-step md-label="{{ 'master_password' | translate}}">
|
||||
<md-step-body>
|
||||
<p>
|
||||
{{'enter_master_password_desc' | translate}}
|
||||
</p>
|
||||
<md-input-container class="md-block">
|
||||
<input type="password" ng-model="settings.master_password" aria-label="Master password" />
|
||||
</md-input-container>
|
||||
<md-input-container>
|
||||
<md-checkbox class="md-primary" md-no-ink aria-label=" {{'remember_master_password'| translate}}" ng-model="settings.master_password_remember">
|
||||
{{'remember_master_password'| translate}}
|
||||
</md-checkbox>
|
||||
</md-input-container>
|
||||
</md-step-body>
|
||||
<md-step-actions>
|
||||
<md-button class="md-primary md-raised" ng-click="nextStep('master');">
|
||||
<i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i>
|
||||
{{ 'continue' | translate}}
|
||||
</md-button>
|
||||
<md-button class="md-primary" ng-click="previousStep();">{{ 'back' | translate}}</md-button>
|
||||
</md-step-actions>
|
||||
</md-step>
|
||||
|
||||
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'next' | translate}}</button>
|
||||
</step>
|
||||
<step class="step3" name="vault">
|
||||
<h2>{{'default_vault' | translate}}</h2>
|
||||
<label>{{'default_vault_desc' | translate}}</label>
|
||||
<select id="defaultVault" class="form-control input-sm" ng-model="settings.default_vault" required ng-options="vault.name for vault in vaults track by vault.guid">
|
||||
</select>
|
||||
|
||||
<label>{{'vault_password' | translate}}</label>
|
||||
<input type="password" ng-change="" ng-model="settings.vault_password" />
|
||||
|
||||
<button step-previous class="btn btn-default">{{'prev' | translate}}</button>
|
||||
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'next' | translate}}</button>
|
||||
</step>
|
||||
<step class="step4" name="master">
|
||||
<h2>{{'enter_master_password' | translate}}</h2>
|
||||
<div>{{'enter_master_password_desc' | translate}}</div>
|
||||
<br />
|
||||
<input type="password" ng-model="settings.master_password" />
|
||||
<br />
|
||||
<label><input type="checkbox" ng-model="settings.master_password_remember">{{'remember_master_password' | translate}}</label>
|
||||
|
||||
<button step-previous class="btn btn-default">{{'prev' | translate}}</button>
|
||||
<button class="btn btn-default stepNext" ng-click="next()" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'next' | translate}}</button>
|
||||
</step>
|
||||
<step class="step5" name="last">
|
||||
<h1>{{'done' | translate}}!</h1>
|
||||
<div>{{'done_donate' | translate}}</div>
|
||||
|
||||
<button step-finish class="btn btn-default" ng-disabled="saving"><i ng-show="saving" ng-class="{'fa-spinner fa-spin': saving}" class="fa"></i> {{'finish' | translate}}</button>
|
||||
</step>
|
||||
</steps>
|
||||
<md-step md-label="{{'done' | translate}}">
|
||||
<md-step-body>
|
||||
<p>
|
||||
{{'done_donate' | translate}}
|
||||
</p>
|
||||
</md-step-body>
|
||||
<md-step-actions>
|
||||
<md-button class="md-primary md-raised" ng-click="finished();">{{'finish' | translate}}</md-button>
|
||||
<md-button class="md-primary" ng-click="previousStep();">{{ 'back' | translate}}</md-button>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6YS8F97PETVU2" target="_blank"><md-button class="md-primary md-raised">{{'donate' | translate}}</md-button></a>
|
||||
</md-step-actions>
|
||||
</md-step>
|
||||
<!-- Other steps if needed ... -->
|
||||
</md-stepper>
|
||||
</div>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 15 KiB |
|
@ -6,7 +6,6 @@ var background = (function () {
|
|||
var _window = {};
|
||||
|
||||
|
||||
|
||||
API.runtime.onConnect.addListener(function (port) {
|
||||
|
||||
port.onMessage.addListener(function (msg) {
|
||||
|
@ -90,7 +89,7 @@ var background = (function () {
|
|||
|
||||
_self.settings = _settings;
|
||||
|
||||
if(!_self.settings.hasOwnProperty('ignored_sites')){
|
||||
if (!_self.settings.hasOwnProperty('ignored_sites')) {
|
||||
_self.settings.ignored_sites = [];
|
||||
}
|
||||
|
||||
|
@ -139,7 +138,7 @@ var background = (function () {
|
|||
PAPI.username = settings.nextcloud_username;
|
||||
PAPI.password = settings.nextcloud_password;
|
||||
|
||||
if(!settings.hasOwnProperty('ignored_sites')){
|
||||
if (!settings.hasOwnProperty('ignored_sites')) {
|
||||
settings.ignored_sites = [];
|
||||
}
|
||||
|
||||
|
@ -147,7 +146,6 @@ var background = (function () {
|
|||
_self.settings = settings;
|
||||
|
||||
|
||||
|
||||
storage.set('settings', settings).then(function () {
|
||||
getSettings();
|
||||
});
|
||||
|
@ -192,6 +190,17 @@ var background = (function () {
|
|||
|
||||
_self.getCredentials = getCredentials;
|
||||
|
||||
function getCredentialByGuid(guid) {
|
||||
for (var i = 0; i < local_credentials.length; i++) {
|
||||
var credential = local_credentials[i];
|
||||
if (credential.guid === guid) {
|
||||
return credential;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_self.getCredentialByGuid = getCredentialByGuid;
|
||||
|
||||
function getCredentialsByUrl(_url, sender) {
|
||||
if (!master_password) {
|
||||
return [];
|
||||
|
@ -259,11 +268,14 @@ var background = (function () {
|
|||
//console.log('Fecthing mined data for tab id', sender.tab.id)
|
||||
var senderUrl = sender.tab.url;
|
||||
var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
|
||||
if(!_self.settings.hasOwnProperty('ignored_sites')){
|
||||
return mined_data[sender.tab.id];
|
||||
}
|
||||
var matches = _self.settings.ignored_sites.filter(function (item) {
|
||||
return typeof item === 'string' && site.indexOf(item) > -1;
|
||||
});
|
||||
|
||||
if(matches.length !== 0){
|
||||
if (matches.length !== 0) {
|
||||
return null;
|
||||
}
|
||||
return mined_data[sender.tab.id];
|
||||
|
@ -411,7 +423,31 @@ var background = (function () {
|
|||
});
|
||||
}
|
||||
|
||||
self.injectCreateCredential = injectCreateCredential;
|
||||
_self.injectCreateCredential = injectCreateCredential;
|
||||
|
||||
function saveCredential(credential) {
|
||||
if (!credential.credential_id) {
|
||||
PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
|
||||
local_credentials.push(createdCredential);
|
||||
});
|
||||
} else {
|
||||
var credential_index;
|
||||
for (var i = 0; i < local_credentials.length; i++) {
|
||||
if (local_credentials[i].guid === credential.guid) {
|
||||
credential_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
|
||||
if (credential_index) {
|
||||
local_credentials[credential_index] = updatedCredential;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_self.saveCredential = saveCredential;
|
||||
|
||||
function isVaultKeySet() {
|
||||
return (_self.settings.vault_password !== null);
|
||||
|
@ -429,14 +465,17 @@ var background = (function () {
|
|||
_self.isAutoFillEnabled = isAutoFillEnabled;
|
||||
|
||||
var doorhangerData = null;
|
||||
|
||||
function setDoorhangerData(data) {
|
||||
doorhangerData = data;
|
||||
}
|
||||
|
||||
_self.setDoorhangerData = setDoorhangerData;
|
||||
|
||||
function getDoorhangerData() {
|
||||
return doorhangerData;
|
||||
}
|
||||
|
||||
_self.getDoorhangerData = getDoorhangerData;
|
||||
|
||||
API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
|
||||
|
|
|
@ -108,8 +108,44 @@ window.PAPI = (function () {
|
|||
callback(credential);
|
||||
});
|
||||
},
|
||||
updateCredential: function (credential, _key, callback) {
|
||||
credential = this.encryptCredential(credential, _key);
|
||||
encryptSharedCredential: function (credential, sharedKey, origKey) {
|
||||
var _credential = credential;
|
||||
_credential.shared_key = this.encryptString(sharedKey, origKey);
|
||||
var encrypted_fields = _encryptedFields;
|
||||
for (var i = 0; i < encrypted_fields.length; i++) {
|
||||
var field = encrypted_fields[i];
|
||||
var fieldValue = credential[field];
|
||||
_credential[field] = this.encryptString(JSON.stringify(fieldValue), sharedKey);
|
||||
}
|
||||
return _credential;
|
||||
},
|
||||
|
||||
updateCredential: function (credential, key, callback) {
|
||||
var origKey = key;
|
||||
var _credential, _key;
|
||||
if (!credential.hasOwnProperty('acl') && credential.hasOwnProperty('shared_key')) {
|
||||
if (credential.shared_key) {
|
||||
_key = this.decryptString(credential.shared_key);
|
||||
}
|
||||
}
|
||||
|
||||
if (credential.hasOwnProperty('acl')) {
|
||||
_key = this.decryptString(credential.acl.shared_key);
|
||||
}
|
||||
|
||||
if (_key) {
|
||||
_credential = this.encryptSharedCredential(credential, _key, origKey);
|
||||
} else {
|
||||
_credential = credential;
|
||||
}
|
||||
delete _credential.shared_key;
|
||||
var regex = /(<([^>]+)>)/ig;
|
||||
if(_credential.description && _credential.description !== "") {
|
||||
_credential.description = _credential.description.replace(regex, "");
|
||||
}
|
||||
|
||||
|
||||
credential = this.encryptCredential(_credential, key);
|
||||
credential.expire_time = new Date(credential.expire_time).getTime() / 1000;
|
||||
api_request('/api/v2/credentials/' + credential.guid, 'PATCH', credential, function () {
|
||||
callback(credential);
|
||||
|
|
|
@ -37,13 +37,14 @@
|
|||
'ngRoute',
|
||||
'ngSanitize',
|
||||
'pascalprecht.translate',
|
||||
'angular-steps'
|
||||
'ngMaterial',
|
||||
'mdSteppers'
|
||||
])
|
||||
.config(function ($routeProvider, $locationProvider) {
|
||||
$routeProvider
|
||||
.when('/', {
|
||||
templateUrl: 'views/main.html',
|
||||
controller: 'MainCtrl'
|
||||
controller: 'ListCtrl'
|
||||
})
|
||||
.when('/search', {
|
||||
templateUrl: 'views/search.html',
|
||||
|
@ -53,6 +54,10 @@
|
|||
templateUrl: 'views/settings.html',
|
||||
controller: 'SettingsCtrl'
|
||||
})
|
||||
.when('/edit/:guid', {
|
||||
templateUrl: 'views/edit_credential.html',
|
||||
controller: 'EditCtrl'
|
||||
})
|
||||
.when('/locked', {
|
||||
templateUrl: 'views/password_prompt.html',
|
||||
controller: 'PasswordPromptCtrl'
|
||||
|
@ -79,6 +84,9 @@
|
|||
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|moz-extension):/);
|
||||
// Angular before v1.2 uses $compileProvider.urlSanitizationWhitelist(...)
|
||||
}
|
||||
]);
|
||||
]).config(function($mdThemingProvider) {
|
||||
$mdThemingProvider.theme('default')
|
||||
.primaryPalette('blue');
|
||||
});
|
||||
|
||||
}());
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/* global API */
|
||||
|
||||
/**
|
||||
* Nextcloud - passman
|
||||
*
|
||||
* @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
|
||||
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name passmanApp.controller:MainCtrl
|
||||
* @description
|
||||
* # MainCtrl
|
||||
* Controller of the passmanApp
|
||||
*/
|
||||
angular.module('passmanExtension')
|
||||
.controller('EditCtrl', ['$scope', '$routeParams', '$mdToast','$timeout', function ($scope, $routeParams, $mdToast, $timeout) {
|
||||
API.runtime.sendMessage(API.runtime.id, {
|
||||
method: "getCredentialByGuid",
|
||||
args: $routeParams.guid
|
||||
}).then(function (credential) {
|
||||
$scope.credential = credential;
|
||||
$scope.credential.password_repeat = angular.copy(credential.password);
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
var storage = new API.Storage();
|
||||
|
||||
function genPwd(settings) {
|
||||
/* jshint ignore:start */
|
||||
var password = generatePassword(settings['length'],
|
||||
settings.useUppercase,
|
||||
settings.useLowercase,
|
||||
settings.useDigits,
|
||||
settings.useSpecialChars,
|
||||
settings.minimumDigitCount,
|
||||
settings.avoidAmbiguousCharacters,
|
||||
settings.requireEveryCharType);
|
||||
/* jshint ignore:end */
|
||||
return password;
|
||||
}
|
||||
|
||||
$scope.pw_settings = null;
|
||||
function getPasswordGenerationSettings(cb) {
|
||||
var default_settings = {
|
||||
'length': 12,
|
||||
'useUppercase': true,
|
||||
'useLowercase': true,
|
||||
'useDigits': true,
|
||||
'useSpecialChars': true,
|
||||
'minimumDigitCount': 3,
|
||||
'avoidAmbiguousCharacters': false,
|
||||
'requireEveryCharType': true
|
||||
};
|
||||
storage.get('password_generator_settings').then(function (_settings) {
|
||||
if (!_settings) {
|
||||
_settings = default_settings;
|
||||
}
|
||||
|
||||
$scope.pw_settings = _settings;
|
||||
}).error(function () {
|
||||
$scope.pw_settings = default_settings;
|
||||
});
|
||||
}
|
||||
|
||||
getPasswordGenerationSettings();
|
||||
|
||||
var custom_field = {
|
||||
label: '',
|
||||
value: '',
|
||||
field_type: 'text',
|
||||
secret: false
|
||||
};
|
||||
|
||||
$scope.new_custom_field = angular.copy(custom_field);
|
||||
|
||||
$scope.addCustomField = function (_field) {
|
||||
var field = angular.copy(_field);
|
||||
if (!field.label || !field.value) {
|
||||
return;
|
||||
}
|
||||
$scope.credential.custom_fields.push(field);
|
||||
$scope.new_custom_field = angular.copy(custom_field);
|
||||
};
|
||||
|
||||
$scope.deleteCustomField = function (field) {
|
||||
var idx = $scope.credential.custom_fields.indexOf(field);
|
||||
$scope.credential.custom_fields.splice(idx, 1);
|
||||
};
|
||||
|
||||
$scope.pwFieldShown = false;
|
||||
|
||||
$scope.togglePwField = function () {
|
||||
$scope.pwFieldShown = !$scope.pwFieldShown;
|
||||
};
|
||||
|
||||
|
||||
function generate_pass(inputId) {
|
||||
|
||||
}
|
||||
var round = 0;
|
||||
$scope.generatePassword = function () {
|
||||
var new_password = genPwd($scope.pw_settings);
|
||||
$scope.credential.password = new_password;
|
||||
$scope.credential.password_repeat = new_password;
|
||||
$timeout(function () {
|
||||
if (round < 10) {
|
||||
$scope.generatePassword();
|
||||
round++;
|
||||
} else {
|
||||
round = 0;
|
||||
}
|
||||
}, 10);
|
||||
};
|
||||
|
||||
$scope.saveCredential = function () {
|
||||
if (!$scope.credential.label) {
|
||||
$mdToast.showSimple(API.i18n.getMessage('label_required'));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.credential.password !== $scope.credential.password_repeat) {
|
||||
$mdToast.showSimple(API.i18n.getMessage('no_password_match'));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.new_custom_field.label && $scope.new_custom_field.value) {
|
||||
$scope.credential.custom_fields.push(angular.copy($scope.new_custom_field));
|
||||
}
|
||||
delete $scope.credential.password_repeat;
|
||||
|
||||
API.runtime.sendMessage(API.runtime.id, {
|
||||
method: "saveCredential",
|
||||
args: $scope.credential
|
||||
}).then(function (credential) {
|
||||
if (!$scope.credential.credential_id) {
|
||||
$mdToast.showSimple(API.i18n.getMessage('credential_created'));
|
||||
} else {
|
||||
$mdToast.showSimple(API.i18n.getMessage('credential_updated'));
|
||||
}
|
||||
window.location = '#!/';
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
window.location = '#!/';
|
||||
};
|
||||
|
||||
|
||||
}]);
|
||||
}());
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/* global API */
|
||||
|
||||
/**
|
||||
* Nextcloud - passman
|
||||
*
|
||||
* @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
|
||||
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name passmanApp.controller:MainCtrl
|
||||
* @description
|
||||
* # MainCtrl
|
||||
* Controller of the passmanApp
|
||||
*/
|
||||
angular.module('passmanExtension')
|
||||
.controller('ListCtrl', ['$scope', 'Settings', '$location', '$rootScope', function ($scope, Settings, $window, $rootScope) {
|
||||
$scope.app = 'passman';
|
||||
var port = API.runtime.connect(null, {
|
||||
name: "PassmanCommunication"
|
||||
});
|
||||
|
||||
var messageParser = function (message) {
|
||||
var e = message.split(':');
|
||||
|
||||
switch (e[0]) {
|
||||
case "credential_amount":
|
||||
$scope.credential_amount = e[1];
|
||||
$scope.refreshing_credentials = false;
|
||||
}
|
||||
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to the background service
|
||||
*/
|
||||
var initApp = function () {
|
||||
port.onMessage.addListener(messageParser);
|
||||
API.runtime.sendMessage(API.runtime.id, {method: "getMasterPasswordSet"}).then(function (isPasswordSet) {
|
||||
getActiveTab();
|
||||
$scope.refreshing_credentials = true;
|
||||
setTimeout(function () {
|
||||
port.postMessage("credential_amount");
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.refreshing_credentials = false;
|
||||
$scope.refresh = function () {
|
||||
$scope.refreshing_credentials = true;
|
||||
API.runtime.sendMessage(API.runtime.id, {method: "getCredentials"}).then(function () {
|
||||
setTimeout(function () {
|
||||
port.postMessage("credential_amount");
|
||||
}, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
var getActiveTab = function (cb) {
|
||||
API.tabs.query({currentWindow: true, active: true}).then(function (tab) {
|
||||
API.runtime.sendMessage(API.runtime.id, {
|
||||
method: "getCredentialsByUrl",
|
||||
args: [tab[0].url]
|
||||
}).then(function (_logins) {
|
||||
//var url = backgroundPage.processURL(tab.url, $rootScope.app_settings.ignoreProtocol, $rootScope.app_settings.ignoreSubdomain, $rootScope.app_settings.ignorePath);
|
||||
$scope.found_credentials = _logins;
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
initApp();
|
||||
|
||||
$scope.edit = function (credential) {
|
||||
window.location = '#!/edit/' + credential.guid;
|
||||
};
|
||||
}]);
|
||||
}());
|
||||
|
|
@ -33,100 +33,56 @@
|
|||
* Controller of the passmanApp
|
||||
*/
|
||||
angular.module('passmanExtension')
|
||||
.controller('MainCtrl', ['$scope', 'Settings', '$location', '$rootScope', function ($scope, Settings, $window, $rootScope) {
|
||||
$scope.app = 'passman';
|
||||
var port = API.runtime.connect(null, {
|
||||
name: "PassmanCommunication"
|
||||
.controller('MainCtrl', ['$scope', '$mdSidenav', '$rootScope', function ($scope, $mdSidenav, $rootScope) {
|
||||
function buildToggler(navID) {
|
||||
return function() {
|
||||
// Component lookup should always be available since we are not using `ng-if`
|
||||
$mdSidenav(navID)
|
||||
.toggle();
|
||||
};
|
||||
}
|
||||
|
||||
$scope.toolbarShown = true;
|
||||
|
||||
$rootScope.$on('lockExtension', function () {
|
||||
$scope.lockExtension();
|
||||
});
|
||||
|
||||
var messageParser = function (message) {
|
||||
var e = message.split(':');
|
||||
$rootScope.$on('unlocked', function () {
|
||||
$scope.toolbarShown = true;
|
||||
});
|
||||
|
||||
switch (e[0]) {
|
||||
case "credential_amount":
|
||||
$scope.credential_amount = e[1];
|
||||
$scope.refreshing_credentials = false;
|
||||
}
|
||||
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to the background service
|
||||
*/
|
||||
var initApp = function () {
|
||||
port.onMessage.addListener(messageParser);
|
||||
API.runtime.sendMessage(API.runtime.id, {method: "getMasterPasswordSet"}).then(function (isPasswordSet) {
|
||||
function redirectToPrompt() {
|
||||
window.location = '#!/locked';
|
||||
return;
|
||||
}
|
||||
|
||||
//First check attributes
|
||||
if (!isPasswordSet) {
|
||||
redirectToPrompt();
|
||||
return;
|
||||
}
|
||||
|
||||
getActiveTab();
|
||||
$scope.refreshing_credentials = true;
|
||||
setTimeout(function () {
|
||||
port.postMessage("credential_amount");
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.refreshing_credentials = false;
|
||||
$scope.refresh = function () {
|
||||
$scope.refreshing_credentials = true;
|
||||
API.runtime.sendMessage(API.runtime.id, {method: "getCredentials"}).then(function () {
|
||||
setTimeout(function () {
|
||||
port.postMessage("credential_amount");
|
||||
}, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
var getActiveTab = function (cb) {
|
||||
API.tabs.query({currentWindow: true, active: true}).then(function (tab) {
|
||||
API.runtime.sendMessage(API.runtime.id, {
|
||||
method: "getCredentialsByUrl",
|
||||
args: [tab[0].url]
|
||||
}).then(function (_logins) {
|
||||
//var url = backgroundPage.processURL(tab.url, $rootScope.app_settings.ignoreProtocol, $rootScope.app_settings.ignoreSubdomain, $rootScope.app_settings.ignorePath);
|
||||
$scope.found_credentials = _logins;
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
};
|
||||
$scope.toggleLeft = buildToggler('left');
|
||||
|
||||
$scope.lockExtension = function () {
|
||||
$scope.toolbarShown = false;
|
||||
API.runtime.sendMessage(API.runtime.id, {method: "setMasterPassword", args: {password: null}}).then(function () {
|
||||
window.location = '#!/locked';
|
||||
});
|
||||
};
|
||||
|
||||
API.runtime.sendMessage(API.runtime.id, {'method': 'getRuntimeSettings'}).then(function (settings) {
|
||||
|
||||
$rootScope.app_settings = settings;
|
||||
if (!settings || Object.keys(settings).length === 0) {
|
||||
window.location = '#!/setup';
|
||||
} else if (settings.hasOwnProperty('isInstalled')) {
|
||||
window.location = '#!/locked';
|
||||
} else {
|
||||
initApp();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
$scope.goto_list = function () {
|
||||
window.location = '#!/';
|
||||
$mdSidenav('left').close();
|
||||
};
|
||||
$scope.goto_settings = function () {
|
||||
window.location = '#!/settings';
|
||||
$mdSidenav('left').close();
|
||||
};
|
||||
|
||||
$scope.goto_search = function () {
|
||||
window.location = '#!/search';
|
||||
$mdSidenav('left').close();
|
||||
};
|
||||
|
||||
API.runtime.sendMessage(API.runtime.id, {'method': 'getRuntimeSettings'}).then(function (settings) {
|
||||
$rootScope.app_settings = settings;
|
||||
if (!settings || Object.keys(settings).length === 0) {
|
||||
window.location = '#!/setup';
|
||||
} else if (settings.hasOwnProperty('isInstalled')) {
|
||||
$scope.lockExtension();
|
||||
}
|
||||
});
|
||||
|
||||
}]);
|
||||
}());
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
API.runtime.sendMessage(API.runtime.id, {method: "getMasterPasswordSet"}).then(function(isSet) {
|
||||
$scope.masterPwSet = isSet;
|
||||
$rootScope.$broadcast('lockExtension');
|
||||
$scope.$apply();
|
||||
});
|
||||
API.runtime.sendMessage(API.runtime.id, {method: "getSettings"});
|
||||
|
@ -51,6 +52,7 @@
|
|||
API.runtime.sendMessage(API.runtime.id, {method: "setMasterPassword", args: {password: $scope.master_password, savePassword: $scope.master_password_remember} }).then(function () {
|
||||
setTimeout(function () {
|
||||
window.location = '#!/';
|
||||
$rootScope.$emit('unlocked');
|
||||
$scope.saving = false;
|
||||
$scope.saving = false;
|
||||
},1500);
|
||||
|
|
|
@ -54,11 +54,8 @@
|
|||
port.onMessage.addListener(messageParser);
|
||||
port.postMessage("credential_amount");
|
||||
|
||||
|
||||
$scope.lockExtension = function () {
|
||||
API.runtime.sendMessage(API.runtime.id, {method: "setMasterPassword", args: {password: null}}).then(function () {
|
||||
window.location = '#!/locked';
|
||||
});
|
||||
$scope.edit = function (credential) {
|
||||
window.location = '#!/edit/' + credential.guid;
|
||||
};
|
||||
|
||||
$scope.found_credentials = false;
|
||||
|
@ -71,15 +68,6 @@
|
|||
};
|
||||
|
||||
|
||||
$scope.goto_settings = function () {
|
||||
window.location = '#!/settings';
|
||||
};
|
||||
|
||||
$scope.goto_search = function () {
|
||||
window.location = '#!/search';
|
||||
};
|
||||
|
||||
|
||||
}]);
|
||||
}());
|
||||
|
||||
|
|
|
@ -102,6 +102,14 @@
|
|||
$scope.saveSettings = function () {
|
||||
$scope.errors = [];
|
||||
var settings = angular.copy($scope.settings);
|
||||
for (var i =0; i < $scope.vaults.length; i++){
|
||||
var vault = $scope.vaults[i];
|
||||
if(vault.guid === settings.default_vault.guid){
|
||||
settings.default_vault = angular.copy(vault);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
/** global: PAPI */
|
||||
PAPI.decryptString(settings.default_vault.challenge_password, settings.vault_password);
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
* Controller of the passmanApp
|
||||
*/
|
||||
angular.module('passmanExtension')
|
||||
.controller('SetupCtrl', ['$scope', '$timeout', '$location', '$rootScope', 'StepsService', function ($scope, $timeout, $location, $rootScope, StepsService) {
|
||||
.controller('SetupCtrl', ['$scope', '$timeout', '$location', '$rootScope', '$mdStepper', '$mdToast',
|
||||
function ($scope, $timeout, $location, $rootScope, $mdStepper, $mdToast) {
|
||||
$scope.settings = {
|
||||
nextcloud_host: '',
|
||||
nextcloud_username: '',
|
||||
|
@ -54,20 +55,54 @@
|
|||
};
|
||||
$scope.vaults = [];
|
||||
|
||||
$scope.gogo = function (to) {
|
||||
StepsService.steps().goTo(to);
|
||||
$scope.nextStep = function (stepName) {
|
||||
var steppers = $mdStepper('setup-stepper');
|
||||
|
||||
$scope.saving = true;
|
||||
$scope.errors = [];
|
||||
$timeout(function () {
|
||||
var step = stepName;
|
||||
var check = $scope.check[step];
|
||||
if (typeof check === "function") {
|
||||
check(function (result) {
|
||||
$scope.saving = false;
|
||||
if (result) {
|
||||
$scope.errors = [];
|
||||
$scope.$apply();
|
||||
steppers.next();
|
||||
}
|
||||
$timeout(function () {
|
||||
$scope.errors = [];
|
||||
$scope.$apply();
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.saving = false;
|
||||
steppers.next();
|
||||
}
|
||||
}, 10);
|
||||
|
||||
|
||||
};
|
||||
$scope.previousStep = function () {
|
||||
var steppers = $mdStepper('setup-stepper');
|
||||
steppers.back();
|
||||
};
|
||||
|
||||
$scope.check = {
|
||||
server: function (callback) {
|
||||
if(!$scope.settings.nextcloud_host){
|
||||
$mdToast.showSimple(API.i18n.getMessage('invalid_host'));
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
PAPI.host = $scope.settings.nextcloud_host;
|
||||
PAPI.username = $scope.settings.nextcloud_username;
|
||||
PAPI.password = $scope.settings.nextcloud_password;
|
||||
PAPI.getVaults(function (vaults) {
|
||||
if (vaults.hasOwnProperty('error')) {
|
||||
var errors = API.i18n.getMessage('invalid_response_from_server', [vaults.result.status, vaults.result.statusText]);
|
||||
$scope.errors.push(errors);
|
||||
|
||||
$mdToast.showSimple(API.i18n.getMessage('invalid_response_from_server', [vaults.result.status, vaults.result.statusText]));
|
||||
callback(false);
|
||||
}
|
||||
else {
|
||||
|
@ -78,12 +113,19 @@
|
|||
});
|
||||
},
|
||||
vault: function (callback) {
|
||||
for (var i = 0; i < $scope.vaults.length; i++) {
|
||||
var vault = $scope.vaults[i];
|
||||
if (vault.guid === $scope.settings.default_vault.guid) {
|
||||
$scope.settings.default_vault = angular.copy(vault);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
PAPI.decryptString($scope.settings.default_vault.challenge_password, $scope.settings.vault_password);
|
||||
callback(true);
|
||||
}
|
||||
catch (e) {
|
||||
$scope.errors.push(API.i18n.getMessage('invalid_vault_password'));
|
||||
$mdToast.showSimple(API.i18n.getMessage('invalid_vault_password'));
|
||||
callback(false);
|
||||
}
|
||||
},
|
||||
|
@ -91,38 +133,13 @@
|
|||
if ($scope.settings.master_password.trim() !== '') {
|
||||
callback(true);
|
||||
} else {
|
||||
$scope.errors.push(API.i18n.getMessage('empty_master_key'));
|
||||
$mdToast.showSimple(API.i18n.getMessage('empty_master_key'));
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
$scope.saving = false;
|
||||
$scope.next = function () {
|
||||
$scope.saving = true;
|
||||
$scope.errors = [];
|
||||
$timeout(function () {
|
||||
var step = StepsService.getCurrent().name;
|
||||
var check = $scope.check[step];
|
||||
if (typeof check === "function") {
|
||||
check(function (result) {
|
||||
$scope.saving = false;
|
||||
if (result) {
|
||||
$scope.errors = [];
|
||||
$scope.$apply();
|
||||
StepsService.steps().next();
|
||||
}
|
||||
$timeout(function () {
|
||||
$scope.errors = [];
|
||||
$scope.$apply();
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.saving = false;
|
||||
StepsService.steps().next();
|
||||
}
|
||||
}, 10);
|
||||
};
|
||||
|
||||
|
||||
$scope.finished = function () {
|
||||
var settings = angular.copy($scope.settings);
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* Nextcloud - passman
|
||||
*
|
||||
* @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
|
||||
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name passmanApp.directive:passwordGen
|
||||
* @description
|
||||
* # passwordGen
|
||||
*/
|
||||
angular.module('passmanExtension')
|
||||
.directive('copyText', ['$compile', '$timeout',
|
||||
function ($compile, $timeout) {
|
||||
var strCopy = API.i18n.getMessage('copy');
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope:{
|
||||
text: '='
|
||||
},
|
||||
template: '<md-icon class="pointer" ng-click="copyText()">content_copy</md-icon>',
|
||||
replace: true,
|
||||
link: function (scope, el) {
|
||||
scope.copyText = function () {
|
||||
var txtToCopy = document.createElement('input');
|
||||
txtToCopy.value = scope.text;
|
||||
document.body.appendChild(txtToCopy);
|
||||
txtToCopy.select();
|
||||
document.execCommand('copy');
|
||||
txtToCopy.parentNode.removeChild(txtToCopy);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
}());
|
|
@ -68,10 +68,12 @@
|
|||
return str;
|
||||
}
|
||||
|
||||
var strCopy = API.i18n.getMessage('copy');
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
// template: '<span class="otp_generator">{{otp}} <span ng-bind="timeleft"></span></span>',
|
||||
template: '<span class="otp_generator"><span class="code">{{otp}}</span> <div class="radial-progress"><div class="circle"><div class="mask full"><div class="fill"></div></div><div class="mask half"><div class="fill"></div><div class="fill fix"></div></div></div></div></span>',
|
||||
//template: '<span class="otp_generator">{{otp}} <i class="fa fa-copy" ng-click="copyOTP(otp)"><md-tooltip md-direction="top">'+ strCopy +'</md-tooltip></i></md-button> </span>',
|
||||
template: '<span class="otp_generator"><span class="code">{{otp}}</span> <i class="fa fa-copy" ng-click="copyOTP(otp)"><md-tooltip md-direction="top">'+ strCopy +'</md-tooltip></i></md-button> <div class="radial-progress"><div class="circle"><div class="mask full"><div class="fill"></div></div><div class="mask half"><div class="fill"></div><div class="fill fix"></div></div></div></div></span>',
|
||||
transclude: false,
|
||||
scope: {
|
||||
secret: '='
|
||||
|
@ -127,6 +129,16 @@
|
|||
$timeout.cancel(scope.timer);
|
||||
}
|
||||
);
|
||||
|
||||
scope.copyOTP = function (otp) {
|
||||
var txtToCopy = document.createElement('input');
|
||||
|
||||
txtToCopy.value = otp;
|
||||
document.body.appendChild(txtToCopy);
|
||||
txtToCopy.select();
|
||||
document.execCommand('copy');
|
||||
txtToCopy.parentNode.removeChild(txtToCopy);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,402 @@
|
|||
/**
|
||||
* @license AngularJS v1.6.1
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngAria
|
||||
* @description
|
||||
*
|
||||
* The `ngAria` module provides support for common
|
||||
* [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](http://www.w3.org/TR/wai-aria/)
|
||||
* attributes that convey state or semantic information about the application for users
|
||||
* of assistive technologies, such as screen readers.
|
||||
*
|
||||
* <div doc-module-components="ngAria"></div>
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
|
||||
* directives are supported:
|
||||
* `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
|
||||
* `ngDblClick`, and `ngMessages`.
|
||||
*
|
||||
* Below is a more detailed breakdown of the attributes handled by ngAria:
|
||||
*
|
||||
* | Directive | Supported Attributes |
|
||||
* |---------------------------------------------|-----------------------------------------------------------------------------------------------------|
|
||||
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
|
||||
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
|
||||
* | {@link ng.directive:ngRequired ngRequired} | aria-required |
|
||||
* | {@link ng.directive:ngChecked ngChecked} | aria-checked |
|
||||
* | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly |
|
||||
* | {@link ng.directive:ngValue ngValue} | aria-checked |
|
||||
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
|
||||
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
|
||||
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
|
||||
* | {@link module:ngMessages ngMessages} | aria-live |
|
||||
* | {@link ng.directive:ngClick ngClick} | tabindex, keydown event, button role |
|
||||
*
|
||||
* Find out more information about each directive by reading the
|
||||
* {@link guide/accessibility ngAria Developer Guide}.
|
||||
*
|
||||
* ## Example
|
||||
* Using ngDisabled with ngAria:
|
||||
* ```html
|
||||
* <md-checkbox ng-disabled="disabled">
|
||||
* ```
|
||||
* Becomes:
|
||||
* ```html
|
||||
* <md-checkbox ng-disabled="disabled" aria-disabled="true">
|
||||
* ```
|
||||
*
|
||||
* ## Disabling Attributes
|
||||
* It's possible to disable individual attributes added by ngAria with the
|
||||
* {@link ngAria.$ariaProvider#config config} method. For more details, see the
|
||||
* {@link guide/accessibility Developer Guide}.
|
||||
*/
|
||||
var ngAriaModule = angular.module('ngAria', ['ng']).
|
||||
provider('$aria', $AriaProvider);
|
||||
|
||||
/**
|
||||
* Internal Utilities
|
||||
*/
|
||||
var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY'];
|
||||
|
||||
var isNodeOneOf = function(elem, nodeTypeArray) {
|
||||
if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $ariaProvider
|
||||
* @this
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Used for configuring the ARIA attributes injected and managed by ngAria.
|
||||
*
|
||||
* ```js
|
||||
* angular.module('myApp', ['ngAria'], function config($ariaProvider) {
|
||||
* $ariaProvider.config({
|
||||
* ariaValue: true,
|
||||
* tabindex: false
|
||||
* });
|
||||
* });
|
||||
*```
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngAria} module to be installed.
|
||||
*
|
||||
*/
|
||||
function $AriaProvider() {
|
||||
var config = {
|
||||
ariaHidden: true,
|
||||
ariaChecked: true,
|
||||
ariaReadonly: true,
|
||||
ariaDisabled: true,
|
||||
ariaRequired: true,
|
||||
ariaInvalid: true,
|
||||
ariaValue: true,
|
||||
tabindex: true,
|
||||
bindKeydown: true,
|
||||
bindRoleForClick: true
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ariaProvider#config
|
||||
*
|
||||
* @param {object} config object to enable/disable specific ARIA attributes
|
||||
*
|
||||
* - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags
|
||||
* - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags
|
||||
* - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags
|
||||
* - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
|
||||
* - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
|
||||
* - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
|
||||
* - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and
|
||||
* aria-valuenow tags
|
||||
* - **tabindex** – `{boolean}` – Enables/disables tabindex tags
|
||||
* - **bindKeydown** – `{boolean}` – Enables/disables keyboard event binding on non-interactive
|
||||
* elements (such as `div` or `li`) using ng-click, making them more accessible to users of
|
||||
* assistive technologies
|
||||
* - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements (such as
|
||||
* `div` or `li`) using ng-click, making them more accessible to users of assistive
|
||||
* technologies
|
||||
*
|
||||
* @description
|
||||
* Enables/disables various ARIA attributes
|
||||
*/
|
||||
this.config = function(newConfig) {
|
||||
config = angular.extend(config, newConfig);
|
||||
};
|
||||
|
||||
function watchExpr(attrName, ariaAttr, nodeBlackList, negate) {
|
||||
return function(scope, elem, attr) {
|
||||
var ariaCamelName = attr.$normalize(ariaAttr);
|
||||
if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) {
|
||||
scope.$watch(attr[attrName], function(boolVal) {
|
||||
// ensure boolean value
|
||||
boolVal = negate ? !boolVal : !!boolVal;
|
||||
elem.attr(ariaAttr, boolVal);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $aria
|
||||
*
|
||||
* @description
|
||||
* @priority 200
|
||||
*
|
||||
* The $aria service contains helper methods for applying common
|
||||
* [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
|
||||
*
|
||||
* ngAria injects common accessibility attributes that tell assistive technologies when HTML
|
||||
* elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
|
||||
* let's review a code snippet from ngAria itself:
|
||||
*
|
||||
*```js
|
||||
* ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
|
||||
* return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
|
||||
* }])
|
||||
*```
|
||||
* Shown above, the ngAria module creates a directive with the same signature as the
|
||||
* traditional `ng-disabled` directive. But this ngAria version is dedicated to
|
||||
* solely managing accessibility attributes on custom elements. The internal `$aria` service is
|
||||
* used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
|
||||
* developer, `aria-disabled` is injected as an attribute with its value synchronized to the
|
||||
* value in `ngDisabled`.
|
||||
*
|
||||
* Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
|
||||
* anything to enable this feature. The `aria-disabled` attribute is automatically managed
|
||||
* simply as a silent side-effect of using `ng-disabled` with the ngAria module.
|
||||
*
|
||||
* The full list of directives that interface with ngAria:
|
||||
* * **ngModel**
|
||||
* * **ngChecked**
|
||||
* * **ngReadonly**
|
||||
* * **ngRequired**
|
||||
* * **ngDisabled**
|
||||
* * **ngValue**
|
||||
* * **ngShow**
|
||||
* * **ngHide**
|
||||
* * **ngClick**
|
||||
* * **ngDblclick**
|
||||
* * **ngMessages**
|
||||
*
|
||||
* Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
|
||||
* directive.
|
||||
*
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngAria} module to be installed.
|
||||
*/
|
||||
this.$get = function() {
|
||||
return {
|
||||
config: function(key) {
|
||||
return config[key];
|
||||
},
|
||||
$$watchExpr: watchExpr
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
ngAriaModule.directive('ngShow', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true);
|
||||
}])
|
||||
.directive('ngHide', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false);
|
||||
}])
|
||||
.directive('ngValue', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngChecked', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngReadonly', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngRequired', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngModel', ['$aria', function($aria) {
|
||||
|
||||
function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) {
|
||||
return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList));
|
||||
}
|
||||
|
||||
function shouldAttachRole(role, elem) {
|
||||
// if element does not have role attribute
|
||||
// AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
|
||||
// AND element is not in nodeBlackList
|
||||
return !elem.attr('role') && (elem.attr('type') === role) && !isNodeOneOf(elem, nodeBlackList);
|
||||
}
|
||||
|
||||
function getShape(attr, elem) {
|
||||
var type = attr.type,
|
||||
role = attr.role;
|
||||
|
||||
return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
|
||||
((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' :
|
||||
(type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : '';
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value
|
||||
compile: function(elem, attr) {
|
||||
var shape = getShape(attr, elem);
|
||||
|
||||
return {
|
||||
post: function(scope, elem, attr, ngModel) {
|
||||
var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false);
|
||||
|
||||
function ngAriaWatchModelValue() {
|
||||
return ngModel.$modelValue;
|
||||
}
|
||||
|
||||
function getRadioReaction(newVal) {
|
||||
// Strict comparison would cause a BC
|
||||
// eslint-disable-next-line eqeqeq
|
||||
var boolVal = (attr.value == ngModel.$viewValue);
|
||||
elem.attr('aria-checked', boolVal);
|
||||
}
|
||||
|
||||
function getCheckboxReaction() {
|
||||
elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue));
|
||||
}
|
||||
|
||||
switch (shape) {
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
if (shouldAttachRole(shape, elem)) {
|
||||
elem.attr('role', shape);
|
||||
}
|
||||
if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) {
|
||||
scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
|
||||
getRadioReaction : getCheckboxReaction);
|
||||
}
|
||||
if (needsTabIndex) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
break;
|
||||
case 'range':
|
||||
if (shouldAttachRole(shape, elem)) {
|
||||
elem.attr('role', 'slider');
|
||||
}
|
||||
if ($aria.config('ariaValue')) {
|
||||
var needsAriaValuemin = !elem.attr('aria-valuemin') &&
|
||||
(attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
|
||||
var needsAriaValuemax = !elem.attr('aria-valuemax') &&
|
||||
(attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
|
||||
var needsAriaValuenow = !elem.attr('aria-valuenow');
|
||||
|
||||
if (needsAriaValuemin) {
|
||||
attr.$observe('min', function ngAriaValueMinReaction(newVal) {
|
||||
elem.attr('aria-valuemin', newVal);
|
||||
});
|
||||
}
|
||||
if (needsAriaValuemax) {
|
||||
attr.$observe('max', function ngAriaValueMinReaction(newVal) {
|
||||
elem.attr('aria-valuemax', newVal);
|
||||
});
|
||||
}
|
||||
if (needsAriaValuenow) {
|
||||
scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
|
||||
elem.attr('aria-valuenow', newVal);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (needsTabIndex) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required
|
||||
&& shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) {
|
||||
// ngModel.$error.required is undefined on custom controls
|
||||
attr.$observe('required', function() {
|
||||
elem.attr('aria-required', !!attr['required']);
|
||||
});
|
||||
}
|
||||
|
||||
if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) {
|
||||
scope.$watch(function ngAriaInvalidWatch() {
|
||||
return ngModel.$invalid;
|
||||
}, function ngAriaInvalidReaction(newVal) {
|
||||
elem.attr('aria-invalid', !!newVal);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
.directive('ngDisabled', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngMessages', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?ngMessages',
|
||||
link: function(scope, elem, attr, ngMessages) {
|
||||
if (!elem.attr('aria-live')) {
|
||||
elem.attr('aria-live', 'assertive');
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive('ngClick',['$aria', '$parse', function($aria, $parse) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
compile: function(elem, attr) {
|
||||
var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
|
||||
return function(scope, elem, attr) {
|
||||
|
||||
if (!isNodeOneOf(elem, nodeBlackList)) {
|
||||
|
||||
if ($aria.config('bindRoleForClick') && !elem.attr('role')) {
|
||||
elem.attr('role', 'button');
|
||||
}
|
||||
|
||||
if ($aria.config('tabindex') && !elem.attr('tabindex')) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
|
||||
if ($aria.config('bindKeydown') && !attr.ngKeydown && !attr.ngKeypress && !attr.ngKeyup) {
|
||||
elem.on('keydown', function(event) {
|
||||
var keyCode = event.which || event.keyCode;
|
||||
if (keyCode === 32 || keyCode === 13) {
|
||||
scope.$apply(callback);
|
||||
}
|
||||
|
||||
function callback() {
|
||||
fn(scope, { $event: event });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
.directive('ngDblclick', ['$aria', function($aria) {
|
||||
return function(scope, elem, attr) {
|
||||
if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,739 @@
|
|||
/**
|
||||
* @license AngularJS v1.6.1
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {'use strict';
|
||||
|
||||
var forEach;
|
||||
var isArray;
|
||||
var isString;
|
||||
var jqLite;
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngMessages
|
||||
* @description
|
||||
*
|
||||
* The `ngMessages` module provides enhanced support for displaying messages within templates
|
||||
* (typically within forms or when rendering message objects that return key/value data).
|
||||
* Instead of relying on JavaScript code and/or complex ng-if statements within your form template to
|
||||
* show and hide error messages specific to the state of an input field, the `ngMessages` and
|
||||
* `ngMessage` directives are designed to handle the complexity, inheritance and priority
|
||||
* sequencing based on the order of how the messages are defined in the template.
|
||||
*
|
||||
* Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
|
||||
* `ngMessage` and `ngMessageExp` directives.
|
||||
*
|
||||
* # Usage
|
||||
* The `ngMessages` directive allows keys in a key/value collection to be associated with a child element
|
||||
* (or 'message') that will show or hide based on the truthiness of that key's value in the collection. A common use
|
||||
* case for `ngMessages` is to display error messages for inputs using the `$error` object exposed by the
|
||||
* {@link ngModel ngModel} directive.
|
||||
*
|
||||
* The child elements of the `ngMessages` directive are matched to the collection keys by a `ngMessage` or
|
||||
* `ngMessageExp` directive. The value of these attributes must match a key in the collection that is provided by
|
||||
* the `ngMessages` directive.
|
||||
*
|
||||
* Consider the following example, which illustrates a typical use case of `ngMessages`. Within the form `myForm` we
|
||||
* have a text input named `myField` which is bound to the scope variable `field` using the {@link ngModel ngModel}
|
||||
* directive.
|
||||
*
|
||||
* The `myField` field is a required input of type `email` with a maximum length of 15 characters.
|
||||
*
|
||||
* ```html
|
||||
* <form name="myForm">
|
||||
* <label>
|
||||
* Enter text:
|
||||
* <input type="email" ng-model="field" name="myField" required maxlength="15" />
|
||||
* </label>
|
||||
* <div ng-messages="myForm.myField.$error" role="alert">
|
||||
* <div ng-message="required">Please enter a value for this field.</div>
|
||||
* <div ng-message="email">This field must be a valid email address.</div>
|
||||
* <div ng-message="maxlength">This field can be at most 15 characters long.</div>
|
||||
* </div>
|
||||
* </form>
|
||||
* ```
|
||||
*
|
||||
* In order to show error messages corresponding to `myField` we first create an element with an `ngMessages` attribute
|
||||
* set to the `$error` object owned by the `myField` input in our `myForm` form.
|
||||
*
|
||||
* Within this element we then create separate elements for each of the possible errors that `myField` could have.
|
||||
* The `ngMessage` attribute is used to declare which element(s) will appear for which error - for example,
|
||||
* setting `ng-message="required"` specifies that this particular element should be displayed when there
|
||||
* is no value present for the required field `myField` (because the key `required` will be `true` in the object
|
||||
* `myForm.myField.$error`).
|
||||
*
|
||||
* ### Message order
|
||||
*
|
||||
* By default, `ngMessages` will only display one message for a particular key/value collection at any time. If more
|
||||
* than one message (or error) key is currently true, then which message is shown is determined by the order of messages
|
||||
* in the HTML template code (messages declared first are prioritised). This mechanism means the developer does not have
|
||||
* to prioritize messages using custom JavaScript code.
|
||||
*
|
||||
* Given the following error object for our example (which informs us that the field `myField` currently has both the
|
||||
* `required` and `email` errors):
|
||||
*
|
||||
* ```javascript
|
||||
* <!-- keep in mind that ngModel automatically sets these error flags -->
|
||||
* myField.$error = { required : true, email: true, maxlength: false };
|
||||
* ```
|
||||
* The `required` message will be displayed to the user since it appears before the `email` message in the DOM.
|
||||
* Once the user types a single character, the `required` message will disappear (since the field now has a value)
|
||||
* but the `email` message will be visible because it is still applicable.
|
||||
*
|
||||
* ### Displaying multiple messages at the same time
|
||||
*
|
||||
* While `ngMessages` will by default only display one error element at a time, the `ng-messages-multiple` attribute can
|
||||
* be applied to the `ngMessages` container element to cause it to display all applicable error messages at once:
|
||||
*
|
||||
* ```html
|
||||
* <!-- attribute-style usage -->
|
||||
* <div ng-messages="myForm.myField.$error" ng-messages-multiple>...</div>
|
||||
*
|
||||
* <!-- element-style usage -->
|
||||
* <ng-messages for="myForm.myField.$error" multiple>...</ng-messages>
|
||||
* ```
|
||||
*
|
||||
* ## Reusing and Overriding Messages
|
||||
* In addition to prioritization, ngMessages also allows for including messages from a remote or an inline
|
||||
* template. This allows for generic collection of messages to be reused across multiple parts of an
|
||||
* application.
|
||||
*
|
||||
* ```html
|
||||
* <script type="text/ng-template" id="error-messages">
|
||||
* <div ng-message="required">This field is required</div>
|
||||
* <div ng-message="minlength">This field is too short</div>
|
||||
* </script>
|
||||
*
|
||||
* <div ng-messages="myForm.myField.$error" role="alert">
|
||||
* <div ng-messages-include="error-messages"></div>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* However, including generic messages may not be useful enough to match all input fields, therefore,
|
||||
* `ngMessages` provides the ability to override messages defined in the remote template by redefining
|
||||
* them within the directive container.
|
||||
*
|
||||
* ```html
|
||||
* <!-- a generic template of error messages known as "my-custom-messages" -->
|
||||
* <script type="text/ng-template" id="my-custom-messages">
|
||||
* <div ng-message="required">This field is required</div>
|
||||
* <div ng-message="minlength">This field is too short</div>
|
||||
* </script>
|
||||
*
|
||||
* <form name="myForm">
|
||||
* <label>
|
||||
* Email address
|
||||
* <input type="email"
|
||||
* id="email"
|
||||
* name="myEmail"
|
||||
* ng-model="email"
|
||||
* minlength="5"
|
||||
* required />
|
||||
* </label>
|
||||
* <!-- any ng-message elements that appear BEFORE the ng-messages-include will
|
||||
* override the messages present in the ng-messages-include template -->
|
||||
* <div ng-messages="myForm.myEmail.$error" role="alert">
|
||||
* <!-- this required message has overridden the template message -->
|
||||
* <div ng-message="required">You did not enter your email address</div>
|
||||
*
|
||||
* <!-- this is a brand new message and will appear last in the prioritization -->
|
||||
* <div ng-message="email">Your email address is invalid</div>
|
||||
*
|
||||
* <!-- and here are the generic error messages -->
|
||||
* <div ng-messages-include="my-custom-messages"></div>
|
||||
* </div>
|
||||
* </form>
|
||||
* ```
|
||||
*
|
||||
* In the example HTML code above the message that is set on required will override the corresponding
|
||||
* required message defined within the remote template. Therefore, with particular input fields (such
|
||||
* email addresses, date fields, autocomplete inputs, etc...), specialized error messages can be applied
|
||||
* while more generic messages can be used to handle other, more general input errors.
|
||||
*
|
||||
* ## Dynamic Messaging
|
||||
* ngMessages also supports using expressions to dynamically change key values. Using arrays and
|
||||
* repeaters to list messages is also supported. This means that the code below will be able to
|
||||
* fully adapt itself and display the appropriate message when any of the expression data changes:
|
||||
*
|
||||
* ```html
|
||||
* <form name="myForm">
|
||||
* <label>
|
||||
* Email address
|
||||
* <input type="email"
|
||||
* name="myEmail"
|
||||
* ng-model="email"
|
||||
* minlength="5"
|
||||
* required />
|
||||
* </label>
|
||||
* <div ng-messages="myForm.myEmail.$error" role="alert">
|
||||
* <div ng-message="required">You did not enter your email address</div>
|
||||
* <div ng-repeat="errorMessage in errorMessages">
|
||||
* <!-- use ng-message-exp for a message whose key is given by an expression -->
|
||||
* <div ng-message-exp="errorMessage.type">{{ errorMessage.text }}</div>
|
||||
* </div>
|
||||
* </div>
|
||||
* </form>
|
||||
* ```
|
||||
*
|
||||
* The `errorMessage.type` expression can be a string value or it can be an array so
|
||||
* that multiple errors can be associated with a single error message:
|
||||
*
|
||||
* ```html
|
||||
* <label>
|
||||
* Email address
|
||||
* <input type="email"
|
||||
* ng-model="data.email"
|
||||
* name="myEmail"
|
||||
* ng-minlength="5"
|
||||
* ng-maxlength="100"
|
||||
* required />
|
||||
* </label>
|
||||
* <div ng-messages="myForm.myEmail.$error" role="alert">
|
||||
* <div ng-message-exp="'required'">You did not enter your email address</div>
|
||||
* <div ng-message-exp="['minlength', 'maxlength']">
|
||||
* Your email must be between 5 and 100 characters long
|
||||
* </div>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* Feel free to use other structural directives such as ng-if and ng-switch to further control
|
||||
* what messages are active and when. Be careful, if you place ng-message on the same element
|
||||
* as these structural directives, Angular may not be able to determine if a message is active
|
||||
* or not. Therefore it is best to place the ng-message on a child element of the structural
|
||||
* directive.
|
||||
*
|
||||
* ```html
|
||||
* <div ng-messages="myForm.myEmail.$error" role="alert">
|
||||
* <div ng-if="showRequiredError">
|
||||
* <div ng-message="required">Please enter something</div>
|
||||
* </div>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* ## Animations
|
||||
* If the `ngAnimate` module is active within the application then the `ngMessages`, `ngMessage` and
|
||||
* `ngMessageExp` directives will trigger animations whenever any messages are added and removed from
|
||||
* the DOM by the `ngMessages` directive.
|
||||
*
|
||||
* Whenever the `ngMessages` directive contains one or more visible messages then the `.ng-active` CSS
|
||||
* class will be added to the element. The `.ng-inactive` CSS class will be applied when there are no
|
||||
* messages present. Therefore, CSS transitions and keyframes as well as JavaScript animations can
|
||||
* hook into the animations whenever these classes are added/removed.
|
||||
*
|
||||
* Let's say that our HTML code for our messages container looks like so:
|
||||
*
|
||||
* ```html
|
||||
* <div ng-messages="myMessages" class="my-messages" role="alert">
|
||||
* <div ng-message="alert" class="some-message">...</div>
|
||||
* <div ng-message="fail" class="some-message">...</div>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* Then the CSS animation code for the message container looks like so:
|
||||
*
|
||||
* ```css
|
||||
* .my-messages {
|
||||
* transition:1s linear all;
|
||||
* }
|
||||
* .my-messages.ng-active {
|
||||
* // messages are visible
|
||||
* }
|
||||
* .my-messages.ng-inactive {
|
||||
* // messages are hidden
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Whenever an inner message is attached (becomes visible) or removed (becomes hidden) then the enter
|
||||
* and leave animation is triggered for each particular element bound to the `ngMessage` directive.
|
||||
*
|
||||
* Therefore, the CSS code for the inner messages looks like so:
|
||||
*
|
||||
* ```css
|
||||
* .some-message {
|
||||
* transition:1s linear all;
|
||||
* }
|
||||
*
|
||||
* .some-message.ng-enter {}
|
||||
* .some-message.ng-enter.ng-enter-active {}
|
||||
*
|
||||
* .some-message.ng-leave {}
|
||||
* .some-message.ng-leave.ng-leave-active {}
|
||||
* ```
|
||||
*
|
||||
* {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
|
||||
*/
|
||||
angular.module('ngMessages', [], function initAngularHelpers() {
|
||||
// Access helpers from angular core.
|
||||
// Do it inside a `config` block to ensure `window.angular` is available.
|
||||
forEach = angular.forEach;
|
||||
isArray = angular.isArray;
|
||||
isString = angular.isString;
|
||||
jqLite = angular.element;
|
||||
})
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @module ngMessages
|
||||
* @name ngMessages
|
||||
* @restrict AE
|
||||
*
|
||||
* @description
|
||||
* `ngMessages` is a directive that is designed to show and hide messages based on the state
|
||||
* of a key/value object that it listens on. The directive itself complements error message
|
||||
* reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
|
||||
*
|
||||
* `ngMessages` manages the state of internal messages within its container element. The internal
|
||||
* messages use the `ngMessage` directive and will be inserted/removed from the page depending
|
||||
* on if they're present within the key/value object. By default, only one message will be displayed
|
||||
* at a time and this depends on the prioritization of the messages within the template. (This can
|
||||
* be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
|
||||
*
|
||||
* A remote template can also be used to promote message reusability and messages can also be
|
||||
* overridden.
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {string} ngMessages an angular expression evaluating to a key/value object
|
||||
* (this is typically the $error object on an ngModel instance).
|
||||
* @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
|
||||
*
|
||||
* @example
|
||||
* <example name="ngMessages-directive" module="ngMessagesExample"
|
||||
* deps="angular-messages.js"
|
||||
* animations="true" fixBase="true">
|
||||
* <file name="index.html">
|
||||
* <form name="myForm">
|
||||
* <label>
|
||||
* Enter your name:
|
||||
* <input type="text"
|
||||
* name="myName"
|
||||
* ng-model="name"
|
||||
* ng-minlength="5"
|
||||
* ng-maxlength="20"
|
||||
* required />
|
||||
* </label>
|
||||
* <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
|
||||
*
|
||||
* <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
|
||||
* <div ng-message="required">You did not enter a field</div>
|
||||
* <div ng-message="minlength">Your field is too short</div>
|
||||
* <div ng-message="maxlength">Your field is too long</div>
|
||||
* </div>
|
||||
* </form>
|
||||
* </file>
|
||||
* <file name="script.js">
|
||||
* angular.module('ngMessagesExample', ['ngMessages']);
|
||||
* </file>
|
||||
* </example>
|
||||
*/
|
||||
.directive('ngMessages', ['$animate', function($animate) {
|
||||
var ACTIVE_CLASS = 'ng-active';
|
||||
var INACTIVE_CLASS = 'ng-inactive';
|
||||
|
||||
return {
|
||||
require: 'ngMessages',
|
||||
restrict: 'AE',
|
||||
controller: ['$element', '$scope', '$attrs', function NgMessagesCtrl($element, $scope, $attrs) {
|
||||
var ctrl = this;
|
||||
var latestKey = 0;
|
||||
var nextAttachId = 0;
|
||||
|
||||
this.getAttachId = function getAttachId() { return nextAttachId++; };
|
||||
|
||||
var messages = this.messages = {};
|
||||
var renderLater, cachedCollection;
|
||||
|
||||
this.render = function(collection) {
|
||||
collection = collection || {};
|
||||
|
||||
renderLater = false;
|
||||
cachedCollection = collection;
|
||||
|
||||
// this is true if the attribute is empty or if the attribute value is truthy
|
||||
var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
|
||||
isAttrTruthy($scope, $attrs.multiple);
|
||||
|
||||
var unmatchedMessages = [];
|
||||
var matchedKeys = {};
|
||||
var messageItem = ctrl.head;
|
||||
var messageFound = false;
|
||||
var totalMessages = 0;
|
||||
|
||||
// we use != instead of !== to allow for both undefined and null values
|
||||
while (messageItem != null) {
|
||||
totalMessages++;
|
||||
var messageCtrl = messageItem.message;
|
||||
|
||||
var messageUsed = false;
|
||||
if (!messageFound) {
|
||||
forEach(collection, function(value, key) {
|
||||
if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
|
||||
// this is to prevent the same error name from showing up twice
|
||||
if (matchedKeys[key]) return;
|
||||
matchedKeys[key] = true;
|
||||
|
||||
messageUsed = true;
|
||||
messageCtrl.attach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (messageUsed) {
|
||||
// unless we want to display multiple messages then we should
|
||||
// set a flag here to avoid displaying the next message in the list
|
||||
messageFound = !multiple;
|
||||
} else {
|
||||
unmatchedMessages.push(messageCtrl);
|
||||
}
|
||||
|
||||
messageItem = messageItem.next;
|
||||
}
|
||||
|
||||
forEach(unmatchedMessages, function(messageCtrl) {
|
||||
messageCtrl.detach();
|
||||
});
|
||||
|
||||
if (unmatchedMessages.length !== totalMessages) {
|
||||
$animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS);
|
||||
} else {
|
||||
$animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
|
||||
|
||||
// If the element is destroyed, proactively destroy all the currently visible messages
|
||||
$element.on('$destroy', function() {
|
||||
forEach(messages, function(item) {
|
||||
item.message.detach();
|
||||
});
|
||||
});
|
||||
|
||||
this.reRender = function() {
|
||||
if (!renderLater) {
|
||||
renderLater = true;
|
||||
$scope.$evalAsync(function() {
|
||||
if (renderLater && cachedCollection) {
|
||||
ctrl.render(cachedCollection);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.register = function(comment, messageCtrl) {
|
||||
var nextKey = latestKey.toString();
|
||||
messages[nextKey] = {
|
||||
message: messageCtrl
|
||||
};
|
||||
insertMessageNode($element[0], comment, nextKey);
|
||||
comment.$$ngMessageNode = nextKey;
|
||||
latestKey++;
|
||||
|
||||
ctrl.reRender();
|
||||
};
|
||||
|
||||
this.deregister = function(comment) {
|
||||
var key = comment.$$ngMessageNode;
|
||||
delete comment.$$ngMessageNode;
|
||||
removeMessageNode($element[0], comment, key);
|
||||
delete messages[key];
|
||||
ctrl.reRender();
|
||||
};
|
||||
|
||||
function findPreviousMessage(parent, comment) {
|
||||
var prevNode = comment;
|
||||
var parentLookup = [];
|
||||
|
||||
while (prevNode && prevNode !== parent) {
|
||||
var prevKey = prevNode.$$ngMessageNode;
|
||||
if (prevKey && prevKey.length) {
|
||||
return messages[prevKey];
|
||||
}
|
||||
|
||||
// dive deeper into the DOM and examine its children for any ngMessage
|
||||
// comments that may be in an element that appears deeper in the list
|
||||
if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) === -1) {
|
||||
parentLookup.push(prevNode);
|
||||
prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
|
||||
} else if (prevNode.previousSibling) {
|
||||
prevNode = prevNode.previousSibling;
|
||||
} else {
|
||||
prevNode = prevNode.parentNode;
|
||||
parentLookup.push(prevNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function insertMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
if (!ctrl.head) {
|
||||
ctrl.head = messageNode;
|
||||
} else {
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
messageNode.next = match.next;
|
||||
match.next = messageNode;
|
||||
} else {
|
||||
messageNode.next = ctrl.head;
|
||||
ctrl.head = messageNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
match.next = messageNode.next;
|
||||
} else {
|
||||
ctrl.head = messageNode.next;
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
function isAttrTruthy(scope, attr) {
|
||||
return (isString(attr) && attr.length === 0) || //empty attribute
|
||||
truthy(scope.$eval(attr));
|
||||
}
|
||||
|
||||
function truthy(val) {
|
||||
return isString(val) ? val.length : !!val;
|
||||
}
|
||||
}])
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessagesInclude
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
|
||||
* code from a remote template and place the downloaded template code into the exact spot
|
||||
* that the ngMessagesInclude directive is placed within the ngMessages container. This allows
|
||||
* for a series of pre-defined messages to be reused and also allows for the developer to
|
||||
* determine what messages are overridden due to the placement of the ngMessagesInclude directive.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-messages-include="remoteTplString">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-messages-include src="expressionValue1">...</ng-messages-include>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
|
||||
*/
|
||||
.directive('ngMessagesInclude',
|
||||
['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
|
||||
|
||||
return {
|
||||
restrict: 'AE',
|
||||
require: '^^ngMessages', // we only require this for validation sake
|
||||
link: function($scope, element, attrs) {
|
||||
var src = attrs.ngMessagesInclude || attrs.src;
|
||||
$templateRequest(src).then(function(html) {
|
||||
if ($scope.$$destroyed) return;
|
||||
|
||||
if (isString(html) && !html.trim()) {
|
||||
// Empty template - nothing to compile
|
||||
replaceElementWithMarker(element, src);
|
||||
} else {
|
||||
// Non-empty template - compile and link
|
||||
$compile(html)($scope, function(contents) {
|
||||
element.after(contents);
|
||||
replaceElementWithMarker(element, src);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Helpers
|
||||
function replaceElementWithMarker(element, src) {
|
||||
// A comment marker is placed for debugging purposes
|
||||
var comment = $compile.$$createComment ?
|
||||
$compile.$$createComment('ngMessagesInclude', src) :
|
||||
$document[0].createComment(' ngMessagesInclude: ' + src + ' ');
|
||||
var marker = jqLite(comment);
|
||||
element.after(marker);
|
||||
|
||||
// Don't pollute the DOM anymore by keeping an empty directive element
|
||||
element.remove();
|
||||
}
|
||||
}])
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessage
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessage` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* More information about using `ngMessage` can be found in the
|
||||
* {@link module:ngMessages `ngMessages` module documentation}.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {expression} ngMessage|when a string value corresponding to the message key.
|
||||
*/
|
||||
.directive('ngMessage', ngMessageDirectiveFactory())
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessageExp
|
||||
* @restrict AE
|
||||
* @priority 1
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessageExp` is the same as {@link directive:ngMessage `ngMessage`}, but instead of a static
|
||||
* value, it accepts an expression to be evaluated for the message key.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression">
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression">
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
|
||||
*/
|
||||
.directive('ngMessageExp', ngMessageDirectiveFactory());
|
||||
|
||||
function ngMessageDirectiveFactory() {
|
||||
return ['$animate', function($animate) {
|
||||
return {
|
||||
restrict: 'AE',
|
||||
transclude: 'element',
|
||||
priority: 1, // must run before ngBind, otherwise the text is set on the comment
|
||||
terminal: true,
|
||||
require: '^^ngMessages',
|
||||
link: function(scope, element, attrs, ngMessagesCtrl, $transclude) {
|
||||
var commentNode = element[0];
|
||||
|
||||
var records;
|
||||
var staticExp = attrs.ngMessage || attrs.when;
|
||||
var dynamicExp = attrs.ngMessageExp || attrs.whenExp;
|
||||
var assignRecords = function(items) {
|
||||
records = items
|
||||
? (isArray(items)
|
||||
? items
|
||||
: items.split(/[\s,]+/))
|
||||
: null;
|
||||
ngMessagesCtrl.reRender();
|
||||
};
|
||||
|
||||
if (dynamicExp) {
|
||||
assignRecords(scope.$eval(dynamicExp));
|
||||
scope.$watchCollection(dynamicExp, assignRecords);
|
||||
} else {
|
||||
assignRecords(staticExp);
|
||||
}
|
||||
|
||||
var currentElement, messageCtrl;
|
||||
ngMessagesCtrl.register(commentNode, messageCtrl = {
|
||||
test: function(name) {
|
||||
return contains(records, name);
|
||||
},
|
||||
attach: function() {
|
||||
if (!currentElement) {
|
||||
$transclude(function(elm, newScope) {
|
||||
$animate.enter(elm, null, element);
|
||||
currentElement = elm;
|
||||
|
||||
// Each time we attach this node to a message we get a new id that we can match
|
||||
// when we are destroying the node later.
|
||||
var $$attachId = currentElement.$$attachId = ngMessagesCtrl.getAttachId();
|
||||
|
||||
// in the event that the element or a parent element is destroyed
|
||||
// by another structural directive then it's time
|
||||
// to deregister the message from the controller
|
||||
currentElement.on('$destroy', function() {
|
||||
if (currentElement && currentElement.$$attachId === $$attachId) {
|
||||
ngMessagesCtrl.deregister(commentNode);
|
||||
messageCtrl.detach();
|
||||
}
|
||||
newScope.$destroy();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
detach: function() {
|
||||
if (currentElement) {
|
||||
var elm = currentElement;
|
||||
currentElement = null;
|
||||
$animate.leave(elm);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
function contains(collection, key) {
|
||||
if (collection) {
|
||||
return isArray(collection)
|
||||
? collection.indexOf(key) >= 0
|
||||
: collection.hasOwnProperty(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,193 +0,0 @@
|
|||
(function () {
|
||||
|
||||
angular.module('templates-angular-steps', ['step.html', 'steps.html']);
|
||||
|
||||
angular.module('step.html', []).run(function ($templateCache) {
|
||||
$templateCache.put('step.html', '<div ng-show=\"selected\" class=\"step ng-hide\" ng-transclude></div>');
|
||||
});
|
||||
|
||||
angular.module('steps.html', []).run(function ($templateCache) {
|
||||
$templateCache.put('steps.html',
|
||||
'<div class=\"angular-steps\">\n' +
|
||||
' <div class=\"steps\" ng-transclude></div>\n' +
|
||||
'</div>');
|
||||
});
|
||||
|
||||
angular.module('angular-steps', ['templates-angular-steps']);
|
||||
|
||||
angular.module('angular-steps').directive('step', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
scope: {
|
||||
name: '@'
|
||||
},
|
||||
require: '^steps',
|
||||
templateUrl: function (element, attributes) {
|
||||
return attributes.template || 'step.html';
|
||||
},
|
||||
link: function ($scope, $element, $attrs, steps) {
|
||||
steps.addStep($scope);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
angular.module('angular-steps').directive('steps', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
scope: {
|
||||
currentStep: '=',
|
||||
onFinish: '&',
|
||||
name: '@'
|
||||
},
|
||||
templateUrl: function (element, attributes) {
|
||||
return attributes.template || 'steps.html';
|
||||
},
|
||||
controller: function ($scope, $element, StepsService) {
|
||||
StepsService.addSteps($scope.name || StepsService.defaultName, this);
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
StepsService.removeSteps($scope.name || StepsService.defaultName);
|
||||
});
|
||||
|
||||
this.steps = $scope.steps = [];
|
||||
|
||||
$scope.$watch('currentStep', function (step) {
|
||||
if (!step) return;
|
||||
var stepName = $scope.selectedStep.name;
|
||||
if ($scope.selectedStep && stepName !== $scope.currentStep) {
|
||||
var found = $scope.steps.filter(function (elm) {
|
||||
return elm.name === $scope.currentStep;
|
||||
})[0];
|
||||
$scope.goTo(found);
|
||||
}
|
||||
});
|
||||
|
||||
this.addStep = function (step) {
|
||||
$scope.steps.push(step);
|
||||
if ($scope.steps.length === 1) {
|
||||
$scope.goTo($scope.steps[0]);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.goTo = function (step) {
|
||||
unselectAll();
|
||||
$scope.selectedStep = step;
|
||||
if ($scope.currentStep !== void 0) {
|
||||
$scope.currentStep = step.name;
|
||||
}
|
||||
step.selected = true;
|
||||
};
|
||||
|
||||
function unselectAll() {
|
||||
$scope.steps.forEach(function (step) {
|
||||
step.selected = false;
|
||||
});
|
||||
$scope.selectedStep = null;
|
||||
}
|
||||
|
||||
this.next = function () {
|
||||
var index = $scope.steps.indexOf($scope.selectedStep);
|
||||
if (index === $scope.steps.length - 1) {
|
||||
this.finish();
|
||||
} else {
|
||||
$scope.goTo($scope.steps[index + 1]);
|
||||
}
|
||||
};
|
||||
|
||||
this.previous = function () {
|
||||
var index = $scope.steps.indexOf($scope.selectedStep);
|
||||
if (index === 0) {
|
||||
throw new Error('Already at step 0');
|
||||
} else {
|
||||
$scope.goTo($scope.steps[index - 1]);
|
||||
}
|
||||
};
|
||||
|
||||
this.goTo = function (step) {
|
||||
var stepTo;
|
||||
if (isNaN(step)) {
|
||||
stepTo = $scope.steps.filter(function (elm) {
|
||||
return elm.name === step;
|
||||
})[0];
|
||||
} else {
|
||||
stepTo = $scope.steps[step];
|
||||
}
|
||||
$scope.goTo(stepTo);
|
||||
};
|
||||
|
||||
this.getCurrent = function() {
|
||||
var index = $scope.steps.indexOf($scope.selectedStep);
|
||||
|
||||
return $scope.steps[index];
|
||||
};
|
||||
|
||||
this.finish = function () {
|
||||
if ($scope.onFinish) {
|
||||
$scope.onFinish();
|
||||
}
|
||||
};
|
||||
|
||||
this.cancel = function () {
|
||||
$scope.goTo($scope.steps[0]);
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function stepsButtonDirective(action) {
|
||||
angular.module('angular-steps')
|
||||
.directive(action, function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
replace: false,
|
||||
require: '^steps',
|
||||
link: function ($scope, $element, $attrs, steps) {
|
||||
$element.on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$element.addClass(action);
|
||||
$scope.$apply(function () {
|
||||
$scope.$eval($attrs[action]);
|
||||
steps[action.replace('step', '').toLowerCase()]();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
stepsButtonDirective('stepNext');
|
||||
stepsButtonDirective('stepPrevious');
|
||||
stepsButtonDirective('stepFinish');
|
||||
stepsButtonDirective('stepCancel');
|
||||
|
||||
angular.module('angular-steps').factory('StepsService', function () {
|
||||
var service = {};
|
||||
|
||||
var instances = {};
|
||||
|
||||
service.defaultName = 'default';
|
||||
|
||||
service.addSteps = function (name, steps) {
|
||||
instances[name] = steps;
|
||||
};
|
||||
|
||||
service.removeSteps = function (name) {
|
||||
delete instances[name];
|
||||
};
|
||||
|
||||
service.steps = function (name) {
|
||||
return instances[name || service.defaultName];
|
||||
};
|
||||
|
||||
service.getCurrent = function() {
|
||||
return instances[service.defaultName].getCurrent();
|
||||
};
|
||||
|
||||
return service;
|
||||
});
|
||||
|
||||
})();
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,250 @@
|
|||
var StepperCtrl = (function () {
|
||||
function StepperCtrl($mdComponentRegistry, $attrs, $log) {
|
||||
this.$mdComponentRegistry = $mdComponentRegistry;
|
||||
this.$attrs = $attrs;
|
||||
this.$log = $log;
|
||||
this.labelStep = 'Step';
|
||||
this.labelOf = 'of';
|
||||
/* End of bindings */
|
||||
this.steps = [];
|
||||
this.currentStep = 0;
|
||||
}
|
||||
StepperCtrl.prototype.$onInit = function () {
|
||||
if (this.$attrs.mdMobileStepText === '') {
|
||||
this.mobileStepText = true;
|
||||
}
|
||||
if (this.$attrs.mdLinear === '') {
|
||||
this.linear = true;
|
||||
}
|
||||
if (this.$attrs.mdAlternative === '') {
|
||||
this.alternative = true;
|
||||
}
|
||||
};
|
||||
StepperCtrl.prototype.$postLink = function () {
|
||||
if (!this.$attrs.id) {
|
||||
this.$log.warn('You must set an id attribute to your stepper');
|
||||
}
|
||||
this.registeredStepper = this.$mdComponentRegistry.register(this, this.$attrs.id);
|
||||
};
|
||||
StepperCtrl.prototype.$onDestroy = function () {
|
||||
this.registeredStepper && this.registeredStepper();
|
||||
};
|
||||
/**
|
||||
* Register component step to this stepper.
|
||||
*
|
||||
* @param {StepCtrl} step The step to add.
|
||||
* @returns number - The step number.
|
||||
*/
|
||||
StepperCtrl.prototype.$addStep = function (step) {
|
||||
return this.steps.push(step) - 1;
|
||||
};
|
||||
/**
|
||||
* Complete the current step and move one to the next.
|
||||
* Using this method on editable steps (in linear stepper)
|
||||
* it will search by the next step without "completed" state to move.
|
||||
* When invoked it dispatch the event onstepcomplete to the step element.
|
||||
*
|
||||
* @returns boolean - True if move and false if not move (e.g. On the last step)
|
||||
*/
|
||||
StepperCtrl.prototype.next = function () {
|
||||
if (this.currentStep < this.steps.length) {
|
||||
this.clearError();
|
||||
this.currentStep++;
|
||||
this.clearFeedback();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* Move to the previous step without change the state of current step.
|
||||
* Using this method in linear stepper it will check if previous step is editable to move.
|
||||
*
|
||||
* @returns boolean - True if move and false if not move (e.g. On the first step)
|
||||
*/
|
||||
StepperCtrl.prototype.back = function () {
|
||||
if (this.currentStep > 0) {
|
||||
this.clearError();
|
||||
this.currentStep--;
|
||||
this.clearFeedback();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* Move to the next step without change the state of current step.
|
||||
* This method works only in optional steps.
|
||||
*
|
||||
* @returns boolean - True if move and false if not move (e.g. On non-optional step)
|
||||
*/
|
||||
StepperCtrl.prototype.skip = function () {
|
||||
var step = this.steps[this.currentStep];
|
||||
if (step.optional) {
|
||||
this.currentStep++;
|
||||
this.clearFeedback();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* Defines the current step state to "error" and shows the message parameter on
|
||||
* title message element.When invoked it dispatch the event onsteperror to the step element.
|
||||
*
|
||||
* @param {string} message The error message
|
||||
*/
|
||||
StepperCtrl.prototype.error = function (message) {
|
||||
var step = this.steps[this.currentStep];
|
||||
step.hasError = true;
|
||||
step.message = message;
|
||||
this.clearFeedback();
|
||||
};
|
||||
/**
|
||||
* Defines the current step state to "normal" and removes the message parameter on
|
||||
* title message element.
|
||||
*/
|
||||
StepperCtrl.prototype.clearError = function () {
|
||||
var step = this.steps[this.currentStep];
|
||||
step.hasError = false;
|
||||
};
|
||||
/**
|
||||
* Move "active" to specified step id parameter.
|
||||
* The id used as reference is the integer number shown on the label of each step (e.g. 2).
|
||||
*
|
||||
* @param {number} stepNumber (description)
|
||||
* @returns boolean - True if move and false if not move (e.g. On id not found)
|
||||
*/
|
||||
StepperCtrl.prototype.goto = function (stepNumber) {
|
||||
if (stepNumber < this.steps.length) {
|
||||
this.currentStep = stepNumber;
|
||||
this.clearFeedback();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* Shows a feedback message and a loading indicador.
|
||||
*
|
||||
* @param {string} [message] The feedbackMessage
|
||||
*/
|
||||
StepperCtrl.prototype.showFeedback = function (message) {
|
||||
this.hasFeedback = true;
|
||||
this.feedbackMessage = message;
|
||||
};
|
||||
/**
|
||||
* Removes the feedback.
|
||||
*/
|
||||
StepperCtrl.prototype.clearFeedback = function () {
|
||||
this.hasFeedback = false;
|
||||
};
|
||||
StepperCtrl.prototype.isCompleted = function (stepNumber) {
|
||||
return this.linear && stepNumber < this.currentStep;
|
||||
};
|
||||
;
|
||||
StepperCtrl.prototype.isActiveStep = function (step) {
|
||||
return this.steps.indexOf(step) === this.currentStep;
|
||||
};
|
||||
StepperCtrl.$inject = [
|
||||
'$mdComponentRegistry',
|
||||
'$attrs',
|
||||
'$log'
|
||||
];
|
||||
return StepperCtrl;
|
||||
}());
|
||||
var StepperServiceFactory = ['$mdComponentRegistry',
|
||||
function ($mdComponentRegistry) {
|
||||
return function (handle) {
|
||||
var instance = $mdComponentRegistry.get(handle);
|
||||
if (!instance) {
|
||||
$mdComponentRegistry.notFoundError(handle);
|
||||
}
|
||||
return instance;
|
||||
};
|
||||
}];
|
||||
var StepCtrl = (function () {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function StepCtrl($element, $compile, $scope) {
|
||||
this.$element = $element;
|
||||
this.$compile = $compile;
|
||||
this.$scope = $scope;
|
||||
}
|
||||
StepCtrl.prototype.$postLink = function () {
|
||||
this.stepNumber = this.$stepper.$addStep(this);
|
||||
};
|
||||
StepCtrl.prototype.isActive = function () {
|
||||
var state = this.$stepper.isActiveStep(this);
|
||||
return state;
|
||||
};
|
||||
StepCtrl.$inject = [
|
||||
'$element',
|
||||
'$compile',
|
||||
'$scope'
|
||||
];
|
||||
return StepCtrl;
|
||||
}());
|
||||
angular.module('mdSteppers', ['ngMaterial'])
|
||||
.factory('$mdStepper', StepperServiceFactory)
|
||||
.directive('mdStepper', function () {
|
||||
return {
|
||||
transclude: true,
|
||||
scope: {
|
||||
linear: '<?mdLinear',
|
||||
alternative: '<?mdAlternative',
|
||||
vertical: '<?mdVertical',
|
||||
mobileStepText: '<?mdMobileStepText',
|
||||
labelStep: '@?mdLabelStep',
|
||||
labelOf: '@?mdLabelOf'
|
||||
},
|
||||
bindToController: true,
|
||||
controller: StepperCtrl,
|
||||
controllerAs: 'stepper',
|
||||
templateUrl: 'mdSteppers/mdStepper.tpl.html'
|
||||
};
|
||||
})
|
||||
.directive('mdStep', ['$compile', function ($compile) {
|
||||
return {
|
||||
require: '^^mdStepper',
|
||||
transclude: true,
|
||||
scope: {
|
||||
label: '@mdLabel',
|
||||
optional: '@?mdOptional'
|
||||
},
|
||||
bindToController: true,
|
||||
controller: StepCtrl,
|
||||
controllerAs: '$ctrl',
|
||||
link: function (scope, iElement, iAttrs, stepperCtrl) {
|
||||
function addOverlay() {
|
||||
var hasOverlay = !!iElement.find('.md-step-body-overlay')[0];
|
||||
if (!hasOverlay) {
|
||||
var overlay = angular.element("<div class=\"md-step-body-overlay\"></div>\n <div class=\"md-step-body-loading\">\n <md-progress-circular md-mode=\"indeterminate\"></md-progress-circular>\n </div>");
|
||||
$compile(overlay)(scope);
|
||||
iElement.find('.md-steppers-scope').append(overlay);
|
||||
}
|
||||
}
|
||||
scope.$ctrl.$stepper = stepperCtrl;
|
||||
scope.$watch(function () {
|
||||
return scope.$ctrl.isActive();
|
||||
}, function (isActive) {
|
||||
if (isActive) {
|
||||
iElement.addClass('md-active');
|
||||
addOverlay();
|
||||
}
|
||||
else {
|
||||
iElement.removeClass('md-active');
|
||||
}
|
||||
});
|
||||
},
|
||||
templateUrl: 'mdSteppers/mdStep.tpl.html'
|
||||
};
|
||||
}])
|
||||
.config(['$mdIconProvider', function ($mdIconProvider) {
|
||||
$mdIconProvider.icon('steppers-check', 'mdSteppers/ic_check_24px.svg');
|
||||
$mdIconProvider.icon('steppers-warning', 'mdSteppers/ic_warning_24px.svg');
|
||||
}])
|
||||
.run(["$templateCache", function ($templateCache) {
|
||||
$templateCache.put("mdSteppers/ic_check_24px.svg", "<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\r\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\r\n</svg>");
|
||||
$templateCache.put("mdSteppers/ic_warning_24px.svg", "<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\r\n <path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/>\r\n</svg>");
|
||||
}]);
|
||||
|
||||
angular.module("mdSteppers").run(["$templateCache", function($templateCache) {$templateCache.put("mdSteppers/mdStep.tpl.html","<div class=\"md-stepper\" ng-class=\"{ \'md-active\': $ctrl.isActive() }\">\r\n <md-steppers-header class=\"md-steppers-header md-steppers-vertical\">\r\n <button class=\"md-stepper-indicator\" ng-class=\"{\r\n \'md-active\': $ctrl.stepNumber === $ctrl.$stepper.currentStep,\r\n \'md-completed\': $ctrl.$stepper.isCompleted($ctrl.stepNumber),\r\n \'md-error\': $ctrl.hasError,\r\n \'md-stepper-optional\': $ctrl.optional || $ctrl.hasError\r\n }\" ng-click=\"$ctrl.$stepper.goto($ctrl.stepNumber)\" ng-disabled=\"$ctrl.$stepper.linear || $ctrl.stepNumber === $ctrl.$stepper.currentStep\">\r\n <div class=\"md-stepper-indicator-wrapper\">\r\n <div class=\"md-stepper-number\" ng-hide=\"$ctrl.hasError\">\r\n <span ng-if=\"!$ctrl.$stepper.isCompleted($ctrl.stepNumber)\">{{ ::$ctrl.stepNumber+1 }}</span>\r\n <md-icon md-svg-icon=\"steppers-check\" class=\"md-stepper-icon\" ng-if=\"$ctrl.$stepper.isCompleted($ctrl.stepNumber)\"></md-icon>\r\n </div>\r\n <div class=\"md-stepper-error-indicator\" ng-show=\"$ctrl.hasError\">\r\n <md-icon md-svg-icon=\"steppers-warning\"></md-icon>\r\n </div>\r\n\r\n <div class=\"md-stepper-title\">\r\n <span>{{ $ctrl.label }}</span>\r\n <small ng-if=\"$ctrl.optional && !$ctrl.hasError\">{{ $ctrl.optional }}</small>\r\n <small class=\"md-stepper-error-message\" ng-show=\"$ctrl.hasError\">\r\n {{ $ctrl.message }}\r\n </small>\r\n </div>\r\n </div>\r\n </button>\r\n <div class=\"md-stepper-feedback-message\" ng-show=\"stepper.hasFeedback\">\r\n {{stepper.feedbackMessage}}\r\n </div>\r\n </md-steppers-header>\r\n <md-steppers-scope layout=\"column\" class=\"md-steppers-scope\" ng-if=\"$ctrl.isActive()\" ng-transclude></md-steppers-scope>\r\n</div>");
|
||||
$templateCache.put("mdSteppers/mdStepper.tpl.html","<div flex class=\"md-steppers\" ng-class=\"{ \r\n \'md-steppers-linear\': stepper.linear, \r\n \'md-steppers-alternative\': stepper.alternative,\r\n \'md-steppers-vertical\': stepper.vertical,\r\n \'md-steppers-mobile-step-text\': stepper.mobileStepText,\r\n \'md-steppers-has-feedback\': stepper.hasFeedback\r\n }\">\r\n <div class=\"md-steppers-header-region\">\r\n <md-steppers-header class=\"md-steppers-header md-steppers-horizontal md-whiteframe-1dp\">\r\n <button class=\"md-stepper-indicator\" ng-repeat=\"(stepNumber, $step) in stepper.steps track by $index\" ng-class=\"{\r\n \'md-active\': stepNumber === stepper.currentStep,\r\n \'md-completed\': stepper.isCompleted(stepNumber),\r\n \'md-error\': $step.hasError,\r\n \'md-stepper-optional\': $step.optional || $step.hasError\r\n }\" ng-click=\"stepper.goto(stepNumber)\" ng-disabled=\"stepper.linear || stepNumber === stepper.currentStep\">\r\n <div class=\"md-stepper-indicator-wrapper\">\r\n <div class=\"md-stepper-number\" ng-hide=\"$step.hasError\">\r\n <span ng-if=\"!stepper.isCompleted(stepNumber)\">{{ ::stepNumber+1 }}</span>\r\n <md-icon md-svg-icon=\"steppers-check\" class=\"md-stepper-icon\" ng-if=\"stepper.isCompleted(stepNumber)\"></md-icon>\r\n </div>\r\n\r\n <div class=\"md-stepper-error-indicator\" ng-show=\"$step.hasError\">\r\n <md-icon md-svg-icon=\"steppers-warning\"></md-icon>\r\n </div>\r\n <div class=\"md-stepper-title\">\r\n <span>{{ $step.label }}</span>\r\n <small ng-if=\"$step.optional && !$step.hasError\">{{ $step.optional }}</small>\r\n <small class=\"md-stepper-error-message\" ng-show=\"$step.hasError\">\r\n {{ $step.message }}\r\n </small>\r\n </div>\r\n </div>\r\n </button>\r\n \r\n </md-steppers-header>\r\n <md-steppers-mobile-header class=\"md-steppers-mobile-header\">\r\n <md-toolbar flex=\"none\" class=\"md-whiteframe-1dp\" style=\"background: #f6f6f6 !important; color: #202020 !important;\">\r\n <div class=\"md-toolbar-tools\">\r\n <h3>\r\n <span>{{stepper.labelStep}} {{stepper.currentStep+1}} {{stepper.labelOf}} {{stepper.steps.length}}</span>\r\n </h3>\r\n </div>\r\n </md-toolbar>\r\n </md-steppers-mobile-header>\r\n <div class=\"md-stepper-feedback-message\" ng-show=\"stepper.hasFeedback\">\r\n {{stepper.feedbackMessage}}\r\n </div>\r\n </div>\r\n <md-steppers-content class=\"md-steppers-content\" ng-transclude></md-steppers-content>\r\n <div class=\"md-steppers-overlay\"></div>\r\n</div>");}]);
|
|
@ -187,7 +187,7 @@ input[type="password"], input[type="text"] {
|
|||
|
||||
|
||||
.radial-progress {
|
||||
$circle-size : 25px;
|
||||
$circle-size : 10px;
|
||||
$circle-background : #d6dadc;
|
||||
$circle-color : #97a71d;
|
||||
$transition-length : 0.5s;
|
||||
|
@ -210,7 +210,7 @@ input[type="password"], input[type="text"] {
|
|||
|
||||
@include transition(transform $transition-length);
|
||||
@include backface-visibility(hidden);
|
||||
|
||||
|
||||
}
|
||||
.mask {
|
||||
clip: rect(0px, $circle-size, $circle-size, $circle-size/2);
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
@import "bourbon/bourbon";
|
||||
$height: auto;
|
||||
body,html {
|
||||
min-width: 450px;
|
||||
}
|
||||
.server_settings{
|
||||
md-input-container{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.pointer{
|
||||
cursor: pointer;
|
||||
}
|
||||
.password-list{
|
||||
min-height: 250px;
|
||||
}
|
||||
.md-content {
|
||||
height: 100%;
|
||||
max-height: $height;
|
||||
}
|
||||
|
||||
.addFab{
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
}
|
||||
.menu {
|
||||
padding: 0;
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
li {
|
||||
list-style-type: none;
|
||||
width: 100%;
|
||||
md-icon {
|
||||
margin-right: 16px;
|
||||
min-width: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
a {
|
||||
color: #000;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: block;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding: 0;
|
||||
padding-left: 16px;
|
||||
padding-right: 56px;
|
||||
text-decoration: none;
|
||||
clear: both;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
-webkit-transition: all 0.2s ease-in-out;
|
||||
-o-transition: all 0.2s ease-in-out;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
a:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.password-list {
|
||||
padding: 0;
|
||||
}
|
||||
/*/*/
|
||||
|
||||
|
||||
.radial-progress {
|
||||
$circle-size : 12px;
|
||||
$circle-background : #d6dadc;
|
||||
$circle-color : rgb(2,119,189);
|
||||
$transition-length : 0.5s;
|
||||
display: inline-block;
|
||||
|
||||
position: relative;
|
||||
width: $circle-size;
|
||||
height: $circle-size;
|
||||
|
||||
background-color: $circle-background;
|
||||
border-radius: 50%;
|
||||
.circle {
|
||||
.mask, .fill {
|
||||
width: $circle-size;
|
||||
height: $circle-size;
|
||||
position: absolute;
|
||||
|
||||
border-radius: 50%;
|
||||
|
||||
@include transition(transform $transition-length);
|
||||
@include backface-visibility(hidden);
|
||||
|
||||
}
|
||||
.mask {
|
||||
clip: rect(0px, $circle-size, $circle-size, $circle-size/2);
|
||||
.fill {
|
||||
clip: rect(0px, $circle-size/2, $circle-size, 0px);
|
||||
background-color: $circle-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ng-hide{
|
||||
display: none;
|
||||
}
|
||||
.unlock-container {
|
||||
height: 350px;
|
||||
|
||||
.unlock {
|
||||
padding: 20px;
|
||||
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
|
||||
md-input-container {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче