├── .eslintrc
├── .gitignore
├── views
├── errormessage.hbs
├── notfound.hbs
├── error.hbs
├── login.hbs
├── index.hbs
├── users.hbs
├── users-profile.hbs
├── layout.hbs
├── users-new.hbs
└── users-edit.hbs
├── .prettierrc.js
├── test
├── users.json
├── auth-proxy.service
├── auth-proxy-nginx.conf
└── test-web.js
├── config
├── default.toml
└── backends.json
├── Gruntfile.js
├── public
├── webauth-css
│ └── custom.css
└── webauth-js
│ ├── popper.min.js
│ ├── bootstrap.min.js
│ └── jquery-3.3.1.slim.min.js
├── LICENSE
├── package.json
├── README.md
└── server.js
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["nodemailer", "prettier"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json*
3 | .DS_Store
4 |
5 |
--------------------------------------------------------------------------------
/views/errormessage.hbs:
--------------------------------------------------------------------------------
1 |
{{code}} Error
2 |
3 | {{message}}
4 |
5 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 160,
3 | tabWidth: 4,
4 | singleQuote: true
5 | };
6 |
--------------------------------------------------------------------------------
/test/users.json:
--------------------------------------------------------------------------------
1 | {
2 | "admin": {
3 | "enabled": true,
4 | "tags": ["admin", "test"],
5 | "password": "admin"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/views/notfound.hbs:
--------------------------------------------------------------------------------
1 | 404 Not Found
2 |
3 | Please select something else
4 |
5 |
--------------------------------------------------------------------------------
/views/error.hbs:
--------------------------------------------------------------------------------
1 | 403 Forbidden
2 |
3 | You are not allowed to access this page, please log in first
4 |
5 |
--------------------------------------------------------------------------------
/config/default.toml:
--------------------------------------------------------------------------------
1 | [www]
2 | host = "127.0.0.1"
3 | port = 5050
4 | postsize = "5MB"
5 |
6 | cookieName = "AUTH_PROXY_SESS"
7 | csrfCookie = "AUTH_PROXY_CSRF"
8 |
9 | [users]
10 | # must be readable/writable by application
11 | db="test/users.json"
12 |
13 | [backends]
14 | # @include "backends.json"
15 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (grunt) {
4 |
5 | // Project configuration.
6 | grunt.initConfig({
7 | eslint: {
8 | all: ['Gruntfile.js', 'index.js']
9 | }
10 | });
11 |
12 | // Load the plugin(s)
13 | grunt.loadNpmTasks('grunt-eslint');
14 |
15 | // Tasks
16 | grunt.registerTask('default', ['eslint']);
17 | };
18 |
--------------------------------------------------------------------------------
/config/backends.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Test",
4 | "description": "Proovin lihtsalt niisama",
5 | "url": "/test1/",
6 | "options": {
7 | "target": "http://127.0.0.1:5060/"
8 | }
9 | },
10 |
11 | {
12 | "title": "Test2",
13 | "description": "Proovin lihtsalt niisama",
14 | "url": "/test1/",
15 | "options": {
16 | "target": "http://127.0.0.1:5060/"
17 | }
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/auth-proxy.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Auth Proxy server
3 | Requires=After=network.target
4 |
5 | [Service]
6 | User=auth-proxy
7 | Group=auth-proxy
8 | Environment="NODE_ENV=production"
9 | WorkingDirectory=/opt/auth-proxy
10 | ExecStart=/usr/bin/node server.js --config=/etc/auth-proxy/auth-proxy.toml
11 | ExecReload=/bin/kill -HUP $MAINPID
12 | SyslogIdentifier=auth-proxy
13 | Type=simple
14 | Restart=always
15 | RestartSec=10
16 |
17 | [Install]
18 | WantedBy=multi-user.target
19 |
--------------------------------------------------------------------------------
/public/webauth-css/custom.css:
--------------------------------------------------------------------------------
1 | html {
2 | position: relative;
3 | min-height: 100%;
4 | }
5 |
6 | body {
7 | padding-top: 2rem;
8 | margin-bottom: 60px;
9 | }
10 |
11 | .main-header {
12 | margin-top: 2rem;
13 | }
14 |
15 | .footer {
16 | position: absolute;
17 | bottom: 0;
18 | width: 100%;
19 | height: 60px; /* Set the fixed height of the footer here */
20 | line-height: 60px; /* Vertically center the text there */
21 | background-color: #f5f5f5;
22 | }
23 |
--------------------------------------------------------------------------------
/test/auth-proxy-nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | listen [::]:80;
4 |
5 | server_name proxy.tahvel.info;
6 | access_log /var/log/nginx/auth-proxy.log;
7 |
8 | location / {
9 | proxy_set_header X-Real-IP $remote_addr;
10 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11 | proxy_set_header HOST $http_host;
12 | proxy_set_header X-NginX-Proxy true;
13 |
14 | proxy_pass http://127.0.0.1:5050;
15 | proxy_redirect off;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/test/test-web.js:
--------------------------------------------------------------------------------
1 | /* eslint no-console: 0*/
2 |
3 | 'use strict';
4 | const http = require('http');
5 |
6 | const hostname = '127.0.0.1';
7 | const port = 5060;
8 |
9 | const server = http.createServer((req, res) => {
10 | console.log(req.remoteAddress);
11 | console.log(req.method);
12 | console.log(req.url);
13 | console.log(req.headers);
14 | res.statusCode = 200;
15 | res.setHeader('Content-Type', 'text/plain');
16 | res.end('Hello World\n');
17 | });
18 |
19 | server.listen(port, hostname, () => {
20 | console.log(`Server running at http://${hostname}:${port}/`);
21 | });
22 |
--------------------------------------------------------------------------------
/views/login.hbs:
--------------------------------------------------------------------------------
1 | Log in
2 |
3 |
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Internet Systems Consortium license
2 | ===================================
3 |
4 | Copyright (c) 2018, Andris Reinman
5 |
6 | Permission to use, copy, modify, and/or distribute this software for any purpose
7 | with or without fee is hereby granted, provided that the above copyright notice
8 | and this permission notice appear in all copies.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
12 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
16 | THIS SOFTWARE.
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auth-proxy",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "HTTP proxy that requires authentication before allowing requests through.",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "grunt",
9 | "start": "node server.js"
10 | },
11 | "keywords": [],
12 | "author": "Andris Reinman",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "eslint": "^5.7.0",
16 | "eslint-config-nodemailer": "^1.2.0",
17 | "eslint-config-prettier": "^3.1.0",
18 | "grunt": "^1.0.3",
19 | "grunt-cli": "^1.3.1",
20 | "grunt-eslint": "^21.0.0"
21 | },
22 | "dependencies": {
23 | "body-parser": "^1.18.3",
24 | "express": "^4.16.4",
25 | "hbs": "^4.0.1",
26 | "http-proxy": "^1.17.0",
27 | "joi": "^14.0.1",
28 | "wild-config": "^1.3.6"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/views/index.hbs:
--------------------------------------------------------------------------------
1 | {{#if user}}
2 |
3 | Backends
4 |
5 |
6 | Select backend to continue to
7 |
8 |
9 |
10 | {{#each backends}}
11 |
12 | |
13 | {{index}}
14 | |
15 |
16 | {{title}}
17 | |
18 |
19 | {{#if description}}{{description}}{{else}} {{/if}}
20 | |
21 |
22 | {{/each}}
23 |
24 |
25 |
26 | {{else}}
27 |
28 | Log in
29 |
30 |
42 |
43 | {{/if}}
44 |
--------------------------------------------------------------------------------
/views/users.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | Users
8 |
9 |
10 | Manage service users
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 | |
24 | #
25 | |
26 |
27 | User
28 | |
29 |
30 | Tags
31 | |
32 |
33 |
34 | |
35 |
36 |
37 |
38 | {{#each users}}
39 |
40 | |
41 | {{index}}
42 | |
43 |
44 | {{username}}
45 | |
46 |
47 | {{#each tags}}
48 | {{this}}
49 | {{/each}}
50 | |
51 |
52 | Edit
53 | |
54 |
55 | {{/each}}
56 |
57 |
58 |
--------------------------------------------------------------------------------
/views/users-profile.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | Profile
4 |
5 |
6 | Manage my settings
7 |
8 |
9 |
44 |
--------------------------------------------------------------------------------
/views/layout.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | WebAuth Gateway
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
39 |
40 | {{#if error.message}}
41 |
42 | {{#if error.title}}
43 | {{error.title}}
44 | {{/if}}
45 | {{error.message}}
46 |
47 |
48 | {{/if}}
49 |
50 | {{#if success.message}}
51 |
52 | {{#if success.title}}
53 | {{success.title}}
54 | {{/if}}
55 | {{success.message}}
56 |
57 |
58 | {{/if}}
59 |
60 | {{{body}}}
61 |
62 |
63 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/views/users-new.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | Users
8 |
9 |
10 | Manage service users
11 |
12 |
13 |
21 |
22 |
23 |
24 |
83 |
--------------------------------------------------------------------------------
/views/users-edit.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | Users
8 |
9 |
10 | Manage service users
11 |
12 |
13 |
21 |
22 |
23 |
24 |
89 |
90 |
91 |
92 |
93 |
94 |
100 |
101 | Are you sure you want to delete this user?
102 |
103 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebAuth
2 |
3 | HTTP proxy that requires authentication before allowing requests to reach backends.
4 |
5 | [Inteface screenshots](https://cloudup.com/cU0jWGvAfit)
6 |
7 | ## Installation
8 |
9 | ```
10 | $ npm install --production
11 | ```
12 |
13 | ## Running
14 |
15 | ```
16 | $ node server.js
17 | ```
18 |
19 | ## User accounts
20 |
21 | User accounts are stored in a json file on disk. File location can be set in the config file. This file must be both readable and writable by the application process.
22 |
23 | User account entry includes a password field. By default it can contain a plaintext password. Once user account is updated via web interface, plaintext password is converted to a pbkdf2-sha512 password hash. Users added via web interface also get a hashed password to the user entry. If you forget your password, then just replace the password hash string in the json file with some plaintext password to log in.
24 |
25 | ```json
26 | {
27 | "admin": {
28 | "enabled": true,
29 | "tags": ["admin"],
30 | "password": "admin"
31 | }
32 | }
33 | ```
34 |
35 | Users with the `admin` tag can add and modify other users. Non-admin users can only access hidden backends.
36 |
37 | ## Backends
38 |
39 | Backends are listed in the backends json file (must be readable by the application process).
40 |
41 | ```json
42 | {
43 | "title": "WordPress",
44 | "description": "Hidden WordPress instance",
45 | "url": "/wordpress/",
46 | "options": {
47 | "target": "http://127.0.0.1:5060/"
48 | }
49 | }
50 | ```
51 |
52 | - **title** is the backend title. This field should be set as it is the link text in backend listing
53 | - **description** is an optional description text
54 | - **url** is the URL prefix for that backend, it should start and end with a slash. If the value is "/test/", then the backend is available from `http://proxyhostname/test/`
55 | - **options** is the proxy configuration, see all available options [here](https://www.npmjs.com/package/http-proxy#user-content-options). In most cases you probably want to set these. It should be possible to use all options except `buffer`.
56 |
57 | For example if you want to modify 301 and 302 redirects and also set your own `Host:` header, then the options object could look like this:
58 |
59 | ```json
60 | {
61 | "target": "http://127.0.0.1:5060/",
62 | "headers": {
63 | "host": "wordpress.tahvel.info"
64 | },
65 | "autoRewrite": true
66 | }
67 | ```
68 |
69 | ## Running as systemd service
70 |
71 | Assuming you have this application copied to `/opt/auth-proxy`
72 |
73 | ```
74 | $ cd /opt/auth-proxy
75 | $ npm install --production
76 | ```
77 |
78 | **Step 1.** Create a proxy user
79 |
80 | This would be an unprivileged user
81 |
82 | ```
83 | $ useradd auth-proxy
84 | ```
85 |
86 | **Step 2.** Create a config folder
87 |
88 | ```
89 | $ mkdir /etc/auth-proxy
90 | $ cp config/* /etc/auth-proxy
91 | $ cp test/users.json /etc/auth-proxy
92 | $ mv /etc/auth-proxy/default.toml /etc/auth-proxy/auth-proxy.toml
93 | $ chown auth-proxy:auth-proxy /etc/auth-proxy/users.json
94 | ```
95 |
96 | > **NB!** edit _/etc/auth-proxy/auth-proxy.toml_ and set user db location to `"/etc/auth-proxy/users.json"`
97 |
98 | **Step 3.** Enable nginx virtual host
99 |
100 | ```
101 | $ cp test/auth-proxy-nginx.conf /etc/nginx/sites-available/auth-proxy
102 | $ ln -s /etc/nginx/sites-available/auth-proxy /etc/nginx/sites-enabled/auth-proxy
103 | $ systemctl reload nginx
104 | ```
105 |
106 | > **NB!** change the virtual host server name in _/etc/nginx/sites-enabled/auth-proxy_ before actually using it
107 |
108 | **Step 4.** Set up systemd service
109 |
110 | ```
111 | $ cp test/auth-proxy.service /etc/systemd/system/auth-proxy.service
112 | $ systemctl enable auth-proxy
113 | $ systemctl start auth-proxy
114 | ```
115 |
116 | Next open the hostname of the proxy service in your browser. Default username is "admin" and password is "admin" (or whatever you have set in the users config file).
117 |
118 | ## Update
119 |
120 | Just replace the files in _/opt/auth-proxy_. If you are using configuration from _/etc/auth-proxy_ then replacing application files should not mess with your existing setup.
121 |
122 | Once application files are replaced, restart the service
123 |
124 | ```
125 | $ systemctl restart auth-proxy
126 | ```
127 |
128 | ## Changing user database or backend manually
129 |
130 | If you have changed user database or backends file, then you can force reload of changes
131 |
132 | ```
133 | $ systemctl reload auth-proxy
134 | ```
135 |
136 | Reloading the service instead of restarting it prevents killing all existing user sessions as WebAuth stores session information for logged in users in memory.
137 |
138 | ## License
139 |
140 | **ISC**
141 |
--------------------------------------------------------------------------------
/public/webauth-js/popper.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) Federico Zivolo 2018
3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=J(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!q(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=$(J(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Q(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=z(n);break;case he.COUNTERCLOCKWISE:p=z(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=G(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.right {
64 | let userData = this.db[username];
65 | userData.username = username;
66 | userData.tags = [].concat(userData.tags || []);
67 | return userData;
68 | })
69 | .sort((a, b) =>
70 | a.username
71 | .toLowerCase()
72 | .trim()
73 | .localeCompare(b.username.toLowerCase().trim())
74 | );
75 | },
76 |
77 | authenticate(username, password) {
78 | if (!this.db) {
79 | this.load();
80 | }
81 | if (!this.db.hasOwnProperty(username)) {
82 | return false;
83 | }
84 | let userData = this.db[username];
85 | if (!userData.enabled || !userData.password) {
86 | return false;
87 | }
88 |
89 | if (userData.password.indexOf('$pbkdf$') >= 0) {
90 | let parts = userData.password.split('$');
91 | let salt = Buffer.from(parts[2], 'base64');
92 | let hash = parts[3];
93 |
94 | if (crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('base64') !== hash) {
95 | return false;
96 | }
97 | } else if (userData.password !== password) {
98 | return false;
99 | }
100 |
101 | userData.username = username;
102 |
103 | return userData;
104 | },
105 |
106 | save(db) {
107 | try {
108 | fs.writeFileSync(config.users.db, JSON.stringify(db, false, 4));
109 | } catch (err) {
110 | console.error('Failed to save database file');
111 | console.error(err.message);
112 | throw new Error('Failed to save database file (diagnostics code: ' + err.code + ')');
113 | }
114 | this.db = db;
115 | },
116 |
117 | create(username, userData) {
118 | let db = this.load();
119 | if (userData.username) {
120 | delete userData.username;
121 | }
122 |
123 | let salt = crypto.randomBytes(16);
124 | userData.password = '$pbkdf$' + salt.toString('base64') + '$' + crypto.pbkdf2Sync(userData.password, salt, 100000, 64, 'sha512').toString('base64');
125 |
126 | db[username] = userData;
127 |
128 | this.save(db);
129 | },
130 |
131 | update(username, userData) {
132 | let db = this.load();
133 | if (userData.username) {
134 | delete userData.username;
135 | }
136 |
137 | if (userData.password && userData.password.indexOf('$pbkdf$') !== 0) {
138 | let salt = crypto.randomBytes(16);
139 | userData.password = '$pbkdf$' + salt.toString('base64') + '$' + crypto.pbkdf2Sync(userData.password, salt, 100000, 64, 'sha512').toString('base64');
140 | }
141 |
142 | db[username] = userData;
143 |
144 | this.save(db);
145 | },
146 |
147 | delete(username) {
148 | let db = this.load();
149 | delete db[username];
150 | this.save(db);
151 | }
152 | };
153 |
154 | config.on('reload', () => {
155 | try {
156 | userStorage.load();
157 | console.log('User database reloaded from disk');
158 | } catch (err) {
159 | console.error('Failed to reload user database from disk');
160 | console.error(err);
161 | }
162 | });
163 |
164 | //
165 | // Create a proxy server with custom application logic
166 | //
167 | const proxy = httpProxy.createProxyServer({});
168 |
169 | webapp.use((req, res, next) => {
170 | // no caching!
171 | res.set({
172 | 'cache-control': 'no-cache',
173 | pragma: 'no-cache'
174 | });
175 | res.locals.time = Date.now();
176 | next();
177 | });
178 |
179 | webapp.set('views', __dirname + '/views');
180 | webapp.set('view engine', 'hbs');
181 | webapp.use(express.static(__dirname + '/public'));
182 |
183 | webapp.use((req, res, next) => {
184 | if (!req.cookies[config.www.csrfCookie]) {
185 | let csrfToken = crypto
186 | .randomBytes(16)
187 | .toString('base64')
188 | .replace(/[=]/g, '');
189 | req.cookies[config.www.csrfCookie] = csrfToken;
190 | res.set({
191 | 'set-cookie': encodeURIComponent(config.www.csrfCookie) + '=' + encodeURIComponent(csrfToken) + '; Path=/; HttpOnly'
192 | });
193 | }
194 | req.csrfToken = res.locals._csrf = req.cookies[config.www.csrfCookie];
195 |
196 | if (typeof req.cookies[config.www.cookieName] === 'string') {
197 | let clearCookie = true;
198 | if (sessions.has(req.cookies[config.www.cookieName])) {
199 | let session = sessions.get(req.cookies[config.www.cookieName]);
200 | let userData = userStorage.get(session.username);
201 | if (userData && userData.enabled) {
202 | userData.username = session.username;
203 | userData.tags = [].concat(userData.tags || []);
204 | req.user = res.locals.user = userData;
205 | clearCookie = false;
206 | req.session = session;
207 | }
208 | }
209 | if (clearCookie) {
210 | sessions.delete(req.cookies[config.www.cookieName]);
211 | res.set({
212 | 'set-cookie': encodeURIComponent(config.www.cookieName) + '=deleted; Path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT'
213 | });
214 | }
215 | }
216 |
217 | let menuItems = (res.locals.menuItems = [
218 | {
219 | key: 'home',
220 | title: 'Home',
221 | url: '/'
222 | }
223 | ]);
224 |
225 | if (!req.user) {
226 | menuItems.push({
227 | key: 'login',
228 | title: 'Log in',
229 | url: '/webauth-login'
230 | });
231 | } else {
232 | if (req.user.tags.includes('admin')) {
233 | menuItems.push({
234 | key: 'users',
235 | title: 'Users',
236 | url: '/webauth-users'
237 | });
238 | }
239 | menuItems.push({
240 | key: 'profile',
241 | title: 'Profile',
242 | url: '/webauth-users/profile'
243 | });
244 | menuItems.push({
245 | key: 'logout',
246 | title: 'Log out',
247 | url: '/webauth-logout'
248 | });
249 | }
250 |
251 | req.setActiveMenu = key => {
252 | menuItems.forEach(item => {
253 | item.active = item.key === key;
254 | });
255 | };
256 |
257 | next();
258 | });
259 |
260 | webapp.use(
261 | bodyParser.urlencoded({
262 | extended: true,
263 | limit: config.www.postsize
264 | })
265 | );
266 |
267 | webapp.use((req, res, next) => {
268 | if (req.method === 'POST' && (!req.body._csrf || req.body._csrf !== req.csrfToken)) {
269 | req.showError = {
270 | message: 'Invalid CSRF token, please refresh page and try again',
271 | statusCode: 403
272 | };
273 | }
274 | next();
275 | });
276 |
277 | webapp.use((req, res, next) => {
278 | if (req.showError) {
279 | res.status(req.showError.statusCode || 500);
280 | res.render('errormessage', {
281 | code: req.showError.statusCode || 500,
282 | message: req.showError.message || req.showError
283 | });
284 | return;
285 | }
286 |
287 | req.flash = (level, title, message) => {
288 | if (!req.session) {
289 | return;
290 | }
291 | req.session[level] = {
292 | title,
293 | message
294 | };
295 | };
296 |
297 | if (req.session && req.method === 'GET') {
298 | res.locals.error = req.session.error;
299 | res.locals.success = req.session.success;
300 | req.session.error = {};
301 | req.session.success = {};
302 | }
303 |
304 | next();
305 | });
306 |
307 | webapp.get('/webauth-logout', (req, res) => {
308 | if (sessions.has(req.cookies[config.www.cookieName])) {
309 | sessions.delete(req.cookies[config.www.cookieName]);
310 | res.set({
311 | 'set-cookie': encodeURIComponent(config.www.cookieName) + '=deleted; Path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT'
312 | });
313 | }
314 | res.redirect('/?t=' + Date.now());
315 | });
316 |
317 | webapp.use('/webauth-users/delete', (req, res, next) => {
318 | if (!req.user || !req.user.tags.includes('admin')) {
319 | req.flash('error', 'Invalid permissions.', 'You do not have permissions to access restricted content');
320 | return res.redirect('/?t=' + Date.now());
321 | }
322 |
323 | req.setActiveMenu('users');
324 | next();
325 | });
326 |
327 | webapp.post('/webauth-users/delete', (req, res) => {
328 | const schema = Joi.object().keys({
329 | username: Joi.string()
330 | .trim()
331 | .max(256)
332 | .label('Username')
333 | .required()
334 | });
335 |
336 | const validation = Joi.validate(req.body, schema, {
337 | abortEarly: false,
338 | convert: true,
339 | stripUnknown: true
340 | });
341 |
342 | let sendError = error => {
343 | let userData = userStorage.get(validation.value.username);
344 | if (!userData) {
345 | res.status(404);
346 | res.render('notfound');
347 | return;
348 | }
349 | res.render('users-edit', {
350 | error,
351 | form: userData
352 | });
353 | };
354 |
355 | if (validation.error) {
356 | return sendError({
357 | title: 'Error',
358 | message: validation.error.message
359 | });
360 | }
361 |
362 | if (req.user.username === validation.value.username) {
363 | return sendError({
364 | title: 'Error',
365 | message: 'Can not delete self'
366 | });
367 | }
368 |
369 | userStorage.delete(validation.value.username);
370 |
371 | req.flash('success', 'Success!', validation.value.username + ' was deleted from user storage');
372 | res.redirect('/webauth-users?t=' + Date.now());
373 | });
374 |
375 | webapp.use('/webauth-users/edit', (req, res, next) => {
376 | if (!req.user || !req.user.tags.includes('admin')) {
377 | req.flash('error', 'Invalid permissions.', 'You do not have permissions to access restricted content');
378 | return res.redirect('/?t=' + Date.now());
379 | }
380 |
381 | req.setActiveMenu('users');
382 | next();
383 | });
384 |
385 | webapp.get('/webauth-users/edit', (req, res) => {
386 | const schema = Joi.object().keys({
387 | username: Joi.string()
388 | .trim()
389 | .max(256)
390 | .label('Username')
391 | .required()
392 | });
393 |
394 | const validation = Joi.validate(req.query, schema, {
395 | abortEarly: false,
396 | convert: true,
397 | stripUnknown: true
398 | });
399 |
400 | if (validation.error) {
401 | req.flash('error', 'Input fail.', 'Failed to validate input');
402 | return res.redirect('/webauth-users?t=' + Date.now());
403 | }
404 |
405 | let userData = userStorage.get(validation.value.username);
406 | if (!userData) {
407 | res.status(404);
408 | res.render('notfound');
409 | return;
410 | }
411 |
412 | res.render('users-edit', {
413 | form: {
414 | username: userData.username,
415 | enabled: !!userData.enabled,
416 | tags: userData.tags.join(', ')
417 | }
418 | });
419 | });
420 |
421 | webapp.post('/webauth-users/edit', (req, res) => {
422 | const schema = Joi.object().keys({
423 | username: Joi.string()
424 | .trim()
425 | .max(256)
426 | .label('Username')
427 | .required(),
428 | tags: Joi.string()
429 | .empty('')
430 | .trim()
431 | .max(256)
432 | .label('Tags'),
433 | password: Joi.string()
434 | .empty('')
435 | .max(256)
436 | .label('Password'),
437 | password2: Joi.string()
438 | .empty('')
439 | .max(256)
440 | .label('Password repeat'),
441 | enabled: Joi.boolean()
442 | .truthy(['Y', 'true', 'yes', 'on', 1])
443 | .falsy(['N', 'false', 'no', 'off', 0, ''])
444 | .default(false)
445 | });
446 |
447 | const validation = Joi.validate(req.body, schema, {
448 | abortEarly: false,
449 | convert: true,
450 | stripUnknown: true
451 | });
452 |
453 | let sendError = error => {
454 | res.render('users-edit', {
455 | error,
456 | form: validation.value,
457 | errors: error.errors || {}
458 | });
459 | };
460 |
461 | if (validation.error) {
462 | let errors = {};
463 |
464 | validation.error.details.forEach(detail => {
465 | errors[detail.context.key] = detail.message;
466 | });
467 |
468 | return sendError({
469 | title: 'Error',
470 | message: 'Input validation failed',
471 | errors
472 | });
473 | }
474 |
475 | if (validation.value.password && validation.value.password !== validation.value.password2) {
476 | return sendError({
477 | title: 'Error',
478 | message: 'Input validation failed',
479 | errors: {
480 | password: 'Passwords do not match'
481 | }
482 | });
483 | }
484 |
485 | let existingData = userStorage.get(validation.value.username);
486 | if (!existingData) {
487 | res.status(404);
488 | res.render('notfound');
489 | return;
490 | }
491 |
492 | let tags = (validation.value.tags || '')
493 | .split(',')
494 | .map(tag => tag.trim())
495 | .filter(tag => tag)
496 | .sort();
497 |
498 | if (validation.value.username === req.user.username) {
499 | let errors = {};
500 | if (!validation.value.enabled) {
501 | errors.enabled = 'Can not disable self';
502 | }
503 | if (existingData.tags.includes('admin') && !tags.includes('admin')) {
504 | errors.tags = 'Can not remove "admin" tag from self';
505 | }
506 | if (Object.keys(errors).length) {
507 | return sendError({
508 | title: 'Error',
509 | message: 'Input validation failed',
510 | errors
511 | });
512 | }
513 | }
514 |
515 | let userData = {
516 | enabled: validation.value.enabled,
517 | tags
518 | };
519 |
520 | if (validation.value.password) {
521 | userData.password = validation.value.password;
522 | } else {
523 | userData.password = existingData.password;
524 | }
525 |
526 | try {
527 | userStorage.update(validation.value.username, userData);
528 | req.flash('success', 'Success!', validation.value.username + ' was updated');
529 | } catch (err) {
530 | req.flash('error', 'Error!', err.message);
531 | return res.redirect('/webauth-users/edit?username=' + encodeURIComponent(validation.value.username) + '&t=' + Date.now());
532 | }
533 |
534 | res.redirect('/webauth-users?t=' + Date.now());
535 | });
536 |
537 | webapp.use('/webauth-users/new', (req, res, next) => {
538 | if (!req.user || !req.user.tags.includes('admin')) {
539 | req.flash('error', 'Invalid permissions.', 'You do not have permissions to access restricted content');
540 | return res.redirect('/?t=' + Date.now());
541 | }
542 |
543 | req.setActiveMenu('users');
544 | next();
545 | });
546 |
547 | webapp.get('/webauth-users/new', (req, res) => {
548 | res.render('users-new', { form: { enabled: true } });
549 | });
550 |
551 | webapp.post('/webauth-users/new', (req, res) => {
552 | const schema = Joi.object().keys({
553 | username: Joi.string()
554 | .trim()
555 | .max(256)
556 | .label('Username')
557 | .required(),
558 | tags: Joi.string()
559 | .empty('')
560 | .trim()
561 | .max(256)
562 | .label('Tags'),
563 | password: Joi.string()
564 | .max(256)
565 | .label('Password')
566 | .required(),
567 | password2: Joi.string()
568 | .max(256)
569 | .label('Password repeat')
570 | .required(),
571 | enabled: Joi.boolean()
572 | .truthy(['Y', 'true', 'yes', 'on', 1])
573 | .falsy(['N', 'false', 'no', 'off', 0, ''])
574 | .default(false)
575 | });
576 |
577 | const validation = Joi.validate(req.body, schema, {
578 | abortEarly: false,
579 | convert: true,
580 | stripUnknown: true
581 | });
582 |
583 | let sendError = error => {
584 | res.render('users-new', {
585 | error,
586 | form: validation.value,
587 | errors: error.errors || {}
588 | });
589 | };
590 |
591 | if (validation.error) {
592 | let errors = {};
593 |
594 | validation.error.details.forEach(detail => {
595 | errors[detail.context.key] = detail.message;
596 | });
597 |
598 | return sendError({
599 | title: 'Error',
600 | message: 'Input validation failed',
601 | errors
602 | });
603 | }
604 |
605 | if (validation.value.password !== validation.value.password2) {
606 | return sendError({
607 | title: 'Error',
608 | message: 'Input validation failed',
609 | errors: {
610 | password: 'Passwords do not match'
611 | }
612 | });
613 | }
614 |
615 | if (userStorage.get(validation.value.username)) {
616 | return sendError({
617 | title: 'Error',
618 | message: 'Selected username is already in use',
619 | errors: {
620 | username: 'This username already exists'
621 | }
622 | });
623 | }
624 |
625 | let tags = (validation.value.tags || '')
626 | .split(',')
627 | .map(tag => tag.trim())
628 | .filter(tag => tag)
629 | .sort();
630 |
631 | let userData = {
632 | enabled: validation.value.enabled,
633 | tags,
634 | password: validation.value.password
635 | };
636 |
637 | try {
638 | userStorage.create(validation.value.username, userData);
639 | req.flash('success', 'Success!', validation.value.username + ' was created');
640 | } catch (err) {
641 | req.flash('error', 'Error!', err.message);
642 | return res.redirect('/webauth-users/new?t=' + Date.now());
643 | }
644 |
645 | res.redirect('/webauth-users?t=' + Date.now());
646 | });
647 |
648 | webapp.use('/webauth-users/profile', (req, res, next) => {
649 | if (!req.user) {
650 | return res.redirect('/?t=' + Date.now());
651 | }
652 |
653 | req.setActiveMenu('profile');
654 | next();
655 | });
656 |
657 | webapp.get('/webauth-users/profile', (req, res) => {
658 | res.render('users-profile', {
659 | form: req.user
660 | });
661 | });
662 |
663 | webapp.post('/webauth-users/profile', (req, res) => {
664 | const schema = Joi.object().keys({
665 | username: Joi.string()
666 | .trim()
667 | .max(256)
668 | .label('Username')
669 | .allow([req.user.username])
670 | .required(),
671 | password: Joi.string()
672 | .empty('')
673 | .max(256)
674 | .label('Password'),
675 | password2: Joi.string()
676 | .empty('')
677 | .max(256)
678 | .label('Password repeat')
679 | });
680 |
681 | const validation = Joi.validate(req.body, schema, {
682 | abortEarly: false,
683 | convert: true,
684 | stripUnknown: true
685 | });
686 |
687 | let sendError = error => {
688 | res.render('users-profile', {
689 | error,
690 | form: validation.value,
691 | errors: error.errors || {}
692 | });
693 | };
694 |
695 | if (validation.error) {
696 | let errors = {};
697 |
698 | validation.error.details.forEach(detail => {
699 | errors[detail.context.key] = detail.message;
700 | });
701 |
702 | return sendError({
703 | title: 'Error',
704 | message: 'Input validation failed',
705 | errors
706 | });
707 | }
708 |
709 | if (validation.value.password && validation.value.password !== validation.value.password2) {
710 | return sendError({
711 | title: 'Error',
712 | message: 'Input validation failed',
713 | errors: {
714 | password: 'Passwords do not match'
715 | }
716 | });
717 | }
718 |
719 | let existingData = userStorage.get(validation.value.username);
720 | if (!existingData) {
721 | res.status(404);
722 | res.render('notfound');
723 | return;
724 | }
725 |
726 | if (validation.value.password) {
727 | existingData.password = validation.value.password;
728 | }
729 |
730 | try {
731 | userStorage.update(validation.value.username, existingData);
732 | req.flash('success', 'Success!', 'Profile settings were updated');
733 | } catch (err) {
734 | req.flash('error', 'Error!', err.message);
735 | }
736 |
737 | res.redirect('/webauth-users/profile?t=' + Date.now());
738 | });
739 |
740 | webapp.use('/webauth-users', (req, res, next) => {
741 | if (!req.user || !req.user.tags.includes('admin')) {
742 | req.flash('error', 'Invalid permissions.', 'You do not have permissions to access restricted content');
743 | return res.redirect('/?t=' + Date.now());
744 | }
745 |
746 | req.setActiveMenu('users');
747 | next();
748 | });
749 |
750 | webapp.get('/webauth-users', (req, res) => {
751 | let users = userStorage.list().map((userData, i) => {
752 | userData.index = i + 1;
753 | return userData;
754 | });
755 |
756 | res.render('users', {
757 | users
758 | });
759 | });
760 |
761 | webapp.use('/webauth-login', (req, res, next) => {
762 | req.setActiveMenu('login');
763 | next();
764 | });
765 |
766 | webapp.get('/webauth-login', (req, res) => {
767 | res.render('login');
768 | });
769 |
770 | webapp.post('/webauth-login', (req, res) => {
771 | const schema = Joi.object().keys({
772 | username: Joi.string()
773 | .trim()
774 | .max(256)
775 | .label('Username')
776 | .required(),
777 | password: Joi.string()
778 | .max(256)
779 | .label('Password')
780 | .required()
781 | });
782 |
783 | const validation = Joi.validate(req.body, schema, {
784 | abortEarly: false,
785 | convert: true,
786 | stripUnknown: true
787 | });
788 |
789 | let sendError = error =>
790 | res.render('login', {
791 | error,
792 | form: validation.value
793 | });
794 |
795 | if (validation.error) {
796 | return sendError({
797 | title: 'Authentication failed!',
798 | message: validation.error.message
799 | });
800 | }
801 |
802 | let username = validation.value.username;
803 | let password = validation.value.password;
804 |
805 | let userData = userStorage.authenticate(username, password);
806 |
807 | if (!userData) {
808 | return sendError({
809 | title: 'Authentication failed!',
810 | message: 'Unknown or disabled user'
811 | });
812 | }
813 |
814 | let session = {
815 | id: crypto.randomBytes(20).toString('hex'),
816 | username,
817 | error: {},
818 | success: {}
819 | };
820 |
821 | sessions.set(session.id, session);
822 |
823 | res.set({
824 | 'set-cookie': encodeURIComponent(config.www.cookieName) + '=' + encodeURIComponent(session.id) + '; Path=/; HttpOnly'
825 | });
826 |
827 | res.redirect('/?t=' + Date.now());
828 | });
829 |
830 | webapp.get('/', (req, res) => {
831 | req.setActiveMenu('home');
832 | res.render('index', {
833 | backends: [].concat(config.backends || []).map((backend, i) => {
834 | backend = JSON.parse(JSON.stringify(backend));
835 | backend.index = i + 1;
836 | return backend;
837 | })
838 | });
839 | });
840 |
841 | webapp.use('/', (req, res) => {
842 | if (req.notFound) {
843 | res.status(404);
844 | res.render('notfound');
845 | return;
846 | }
847 |
848 | res.status(403);
849 | res.render('error');
850 | });
851 |
852 | const server = http.createServer((req, res) => {
853 | let cookies = getCookies(req.headers.cookie);
854 | req.cookies = cookies;
855 |
856 | if (!cookies[config.www.cookieName] || !sessions.has(cookies[config.www.cookieName]) || req.url.indexOf('/webauth-') === 0) {
857 | // serve authentication webserver
858 | return webapp(req, res);
859 | }
860 |
861 | let backend;
862 | for (let b of config.backends) {
863 | if (req.url.indexOf(b.url) === 0) {
864 | backend = b;
865 | break;
866 | }
867 | }
868 |
869 | if (!backend) {
870 | req.notFound = true;
871 | return webapp(req, res);
872 | }
873 |
874 | let target = backend.options;
875 |
876 | req.headers.cookie = filterCookies(req.headers.cookie, [config.www.cookieName, config.www.csrfCookie]);
877 |
878 | proxy.web(req, res, target);
879 |
880 | proxy.on('error', (err, req, res) => {
881 | if (req.errorProcessed) {
882 | return;
883 | }
884 | req.errorProcessed = true;
885 | console.log(err);
886 | req.showError = 'Something went wrong (diagnostics code: ' + err.code + ')';
887 | return webapp(req, res);
888 | });
889 | });
890 |
891 | function filterCookies(cookieHeader, cookieKeys) {
892 | cookieHeader = [].concat(cookieHeader || []).join(';');
893 | cookieKeys = [].concat(cookieKeys || []);
894 | if (!cookieKeys || !cookieKeys.length) {
895 | return cookieHeader;
896 | }
897 |
898 | return cookieHeader
899 | .split(';')
900 | .filter(data => {
901 | let parts = data.split('=');
902 | let key = parts.shift().trim();
903 | try {
904 | key = decodeURIComponent(key);
905 | } catch (err) {
906 | // ignore
907 | }
908 | if (cookieKeys.includes(key)) {
909 | return false;
910 | }
911 | return true;
912 | })
913 | .join(';');
914 | }
915 |
916 | function getCookies(cookieHeader) {
917 | cookieHeader = [].concat(cookieHeader || []).join(';');
918 | let cookies = {};
919 | cookieHeader
920 | .split(';')
921 | .map(entry => entry.trim())
922 | .filter(entry => entry)
923 | .map(entry => {
924 | let parts = entry.split('=');
925 | let key = parts.shift().trim();
926 | let value = parts.join('=').trim();
927 | try {
928 | key = decodeURIComponent(key);
929 | } catch (err) {
930 | // ignore
931 | }
932 | try {
933 | value = decodeURIComponent(value);
934 | } catch (err) {
935 | // ignore
936 | }
937 | cookies[key] = value;
938 | });
939 |
940 | return cookies;
941 | }
942 |
943 | process.title = 'auth-proxy';
944 |
945 | // Try to load user database. Might throw
946 | userStorage.load();
947 |
948 | server.listen(config.www.port, () => {
949 | console.log('Server listening on port %s', config.www.port);
950 | });
951 |
--------------------------------------------------------------------------------
/public/webauth-js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v4.1.3 (https://getbootstrap.com/)
3 | * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e(t.bootstrap={},t.jQuery,t.Popper)}(this,function(t,e,h){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)P(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right',trigger:"hover focus",title:"",delay:0,html:!(Ie={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Se={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},we="out",Ne={HIDE:"hide"+Ee,HIDDEN:"hidden"+Ee,SHOW:(De="show")+Ee,SHOWN:"shown"+Ee,INSERTED:"inserted"+Ee,CLICK:"click"+Ee,FOCUSIN:"focusin"+Ee,FOCUSOUT:"focusout"+Ee,MOUSEENTER:"mouseenter"+Ee,MOUSELEAVE:"mouseleave"+Ee},Oe="fade",ke="show",Pe=".tooltip-inner",je=".arrow",He="hover",Le="focus",Re="click",xe="manual",We=function(){function i(t,e){if("undefined"==typeof h)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=pe(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(pe(this.getTipElement()).hasClass(ke))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),pe.removeData(this.element,this.constructor.DATA_KEY),pe(this.element).off(this.constructor.EVENT_KEY),pe(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&pe(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===pe(this.element).css("display"))throw new Error("Please use show on visible elements");var t=pe.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){pe(this.element).trigger(t);var n=pe.contains(this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=Fn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&pe(i).addClass(Oe);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:pe(document).find(this.config.container);pe(i).data(this.constructor.DATA_KEY,this),pe.contains(this.element.ownerDocument.documentElement,this.tip)||pe(i).appendTo(a),pe(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new h(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:je},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),pe(i).addClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().on("mouseover",null,pe.noop);var l=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,pe(e.element).trigger(e.constructor.Event.SHOWN),t===we&&e._leave(null,e)};if(pe(this.tip).hasClass(Oe)){var c=Fn.getTransitionDurationFromElement(this.tip);pe(this.tip).one(Fn.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=pe.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==De&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),pe(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(pe(this.element).trigger(i),!i.isDefaultPrevented()){if(pe(n).removeClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().off("mouseover",null,pe.noop),this._activeTrigger[Re]=!1,this._activeTrigger[Le]=!1,this._activeTrigger[He]=!1,pe(this.tip).hasClass(Oe)){var o=Fn.getTransitionDurationFromElement(n);pe(n).one(Fn.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){pe(this.getTipElement()).addClass(Te+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||pe(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(pe(t.querySelectorAll(Pe)),this.getTitle()),pe(t).removeClass(Oe+" "+ke)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?pe(e).parent().is(t)||t.empty().append(e):t.text(pe(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getAttachment=function(t){return Ie[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)pe(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==xe){var e=t===He?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===He?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;pe(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}pe(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Le:He]=!0),pe(e.getTipElement()).hasClass(ke)||e._hoverState===De?e._hoverState=De:(clearTimeout(e._timeout),e._hoverState=De,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===De&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Le:He]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=we,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===we&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,pe(this.element).data(),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),Fn.typeCheckConfig(ve,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=pe(this.getTipElement()),e=t.attr("class").match(be);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(pe(t).removeClass(Oe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=pe(this).data(ye),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),pe(this).data(ye,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return Ae}},{key:"NAME",get:function(){return ve}},{key:"DATA_KEY",get:function(){return ye}},{key:"Event",get:function(){return Ne}},{key:"EVENT_KEY",get:function(){return Ee}},{key:"DefaultType",get:function(){return Se}}]),i}(),pe.fn[ve]=We._jQueryInterface,pe.fn[ve].Constructor=We,pe.fn[ve].noConflict=function(){return pe.fn[ve]=Ce,We._jQueryInterface},We),Jn=(qe="popover",Ke="."+(Fe="bs.popover"),Me=(Ue=e).fn[qe],Qe="bs-popover",Be=new RegExp("(^|\\s)"+Qe+"\\S+","g"),Ve=l({},zn.Default,{placement:"right",trigger:"click",content:"",template:''}),Ye=l({},zn.DefaultType,{content:"(string|element|function)"}),ze="fade",Ze=".popover-header",Ge=".popover-body",$e={HIDE:"hide"+Ke,HIDDEN:"hidden"+Ke,SHOW:(Je="show")+Ke,SHOWN:"shown"+Ke,INSERTED:"inserted"+Ke,CLICK:"click"+Ke,FOCUSIN:"focusin"+Ke,FOCUSOUT:"focusout"+Ke,MOUSEENTER:"mouseenter"+Ke,MOUSELEAVE:"mouseleave"+Ke},Xe=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){Ue(this.getTipElement()).addClass(Qe+"-"+t)},r.getTipElement=function(){return this.tip=this.tip||Ue(this.config.template)[0],this.tip},r.setContent=function(){var t=Ue(this.getTipElement());this.setElementContent(t.find(Ze),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Ge),e),t.removeClass(ze+" "+Je)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=Ue(this.getTipElement()),e=t.attr("class").match(Be);null!==e&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||t li > .active",xn='[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',Wn=".dropdown-toggle",Un="> .dropdown-menu .active",qn=function(){function i(t){this._element=t}var t=i.prototype;return t.show=function(){var n=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&bn(this._element).hasClass(Nn)||bn(this._element).hasClass(On))){var t,i,e=bn(this._element).closest(Hn)[0],r=Fn.getSelectorFromElement(this._element);if(e){var o="UL"===e.nodeName?Rn:Ln;i=(i=bn.makeArray(bn(e).find(o)))[i.length-1]}var s=bn.Event(Dn.HIDE,{relatedTarget:this._element}),a=bn.Event(Dn.SHOW,{relatedTarget:i});if(i&&bn(i).trigger(s),bn(this._element).trigger(a),!a.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(t=document.querySelector(r)),this._activate(this._element,e);var l=function(){var t=bn.Event(Dn.HIDDEN,{relatedTarget:n._element}),e=bn.Event(Dn.SHOWN,{relatedTarget:i});bn(i).trigger(t),bn(n._element).trigger(e)};t?this._activate(t,t.parentNode,l):l()}}},t.dispose=function(){bn.removeData(this._element,Sn),this._element=null},t._activate=function(t,e,n){var i=this,r=("UL"===e.nodeName?bn(e).find(Rn):bn(e).children(Ln))[0],o=n&&r&&bn(r).hasClass(kn),s=function(){return i._transitionComplete(t,r,n)};if(r&&o){var a=Fn.getTransitionDurationFromElement(r);bn(r).one(Fn.TRANSITION_END,s).emulateTransitionEnd(a)}else s()},t._transitionComplete=function(t,e,n){if(e){bn(e).removeClass(Pn+" "+Nn);var i=bn(e.parentNode).find(Un)[0];i&&bn(i).removeClass(Nn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}if(bn(t).addClass(Nn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),Fn.reflow(t),bn(t).addClass(Pn),t.parentNode&&bn(t.parentNode).hasClass(wn)){var r=bn(t).closest(jn)[0];if(r){var o=[].slice.call(r.querySelectorAll(Wn));bn(o).addClass(Nn)}t.setAttribute("aria-expanded",!0)}n&&n()},i._jQueryInterface=function(n){return this.each(function(){var t=bn(this),e=t.data(Sn);if(e||(e=new i(this),t.data(Sn,e)),"string"==typeof n){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}}]),i}(),bn(document).on(Dn.CLICK_DATA_API,xn,function(t){t.preventDefault(),qn._jQueryInterface.call(bn(this),"show")}),bn.fn.tab=qn._jQueryInterface,bn.fn.tab.Constructor=qn,bn.fn.tab.noConflict=function(){return bn.fn.tab=An,qn._jQueryInterface},qn);!function(t){if("undefined"==typeof t)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1===e[0]&&9===e[1]&&e[2]<1||4<=e[0])throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(e),t.Util=Fn,t.Alert=Kn,t.Button=Mn,t.Carousel=Qn,t.Collapse=Bn,t.Dropdown=Vn,t.Modal=Yn,t.Popover=Jn,t.Scrollspy=Zn,t.Tab=Gn,t.Tooltip=zn,Object.defineProperty(t,"__esModule",{value:!0})});
7 | //# sourceMappingURL=bootstrap.min.js.map
--------------------------------------------------------------------------------
/public/webauth-js/jquery-3.3.1.slim.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery v3.3.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector | (c) JS Foundation and other contributors | jquery.org/license */
2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,u=n.push,s=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,d=f.toString,p=d.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},v=function e(t){return null!=t&&t===t.window},y={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in y)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function b(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var x="3.3.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector",w=function(e,t){return new w.fn.init(e,t)},C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:x,constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,u,s,l,c,f,d,p,h,g,v,y,m,b,x="sizzle"+1*new Date,w=e.document,C=0,T=0,E=ae(),N=ae(),k=ae(),A=function(e,t){return e===t&&(f=!0),0},D={}.hasOwnProperty,S=[],L=S.pop,j=S.push,q=S.push,O=S.slice,P=function(e,t){for(var n=0,r=e.length;n+~]|"+I+")"+I+"*"),_=new RegExp("="+I+"*([^\\]'\"]*?)"+I+"*\\]","g"),U=new RegExp(M),V=new RegExp("^"+R+"$"),X={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+B),PSEUDO:new RegExp("^"+M),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+I+"*(even|odd|(([+-]|)(\\d*)n|)"+I+"*(?:([+-]|)"+I+"*(\\d+)|))"+I+"*\\)|)","i"),bool:new RegExp("^(?:"+H+")$","i"),needsContext:new RegExp("^"+I+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+I+"*((?:-\\d)?\\d*)"+I+"*\\)|)(?=[^-]|$)","i")},Q=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,G=/^[^{]+\{\s*\[native \w/,K=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,J=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+I+"?|("+I+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){d()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{q.apply(S=O.call(w.childNodes),w.childNodes),S[w.childNodes.length].nodeType}catch(e){q={apply:S.length?function(e,t){j.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,u,l,c,f,h,y,m=t&&t.ownerDocument,C=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==C&&9!==C&&11!==C)return r;if(!i&&((t?t.ownerDocument||t:w)!==p&&d(t),t=t||p,g)){if(11!==C&&(f=K.exec(e)))if(o=f[1]){if(9===C){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&b(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return q.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return q.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!k[e+" "]&&(!v||!v.test(e))){if(1!==C)m=t,y=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=x),u=(h=a(e)).length;while(u--)h[u]="#"+c+" "+ye(h[u]);y=h.join(","),m=J.test(e)&&ge(t.parentNode)||t}if(y)try{return q.apply(r,m.querySelectorAll(y)),r}catch(e){}finally{c===x&&t.removeAttribute("id")}}}return s(e.replace($,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function ue(e){return e[x]=!0,e}function se(e){var t=p.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function de(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pe(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return ue(function(t){return t=+t,ue(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},d=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==p&&9===a.nodeType&&a.documentElement?(p=a,h=p.documentElement,g=!o(p),w!==p&&(i=p.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=se(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=se(function(e){return e.appendChild(p.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=G.test(p.getElementsByClassName),n.getById=se(function(e){return h.appendChild(e).id=x,!p.getElementsByName||!p.getElementsByName(x).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},y=[],v=[],(n.qsa=G.test(p.querySelectorAll))&&(se(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+I+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+I+"*(?:value|"+H+")"),e.querySelectorAll("[id~="+x+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+x+"+*").length||v.push(".#.+[+~]")}),se(function(e){e.innerHTML="";var t=p.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+I+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(n.matchesSelector=G.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&se(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),y.push("!=",M)}),v=v.length&&new RegExp(v.join("|")),y=y.length&&new RegExp(y.join("|")),t=G.test(h.compareDocumentPosition),b=t||G.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===p||e.ownerDocument===w&&b(w,e)?-1:t===p||t.ownerDocument===w&&b(w,t)?1:c?P(c,e)-P(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],u=[t];if(!i||!o)return e===p?-1:t===p?1:i?-1:o?1:c?P(c,e)-P(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)u.unshift(n);while(a[r]===u[r])r++;return r?ce(a[r],u[r]):a[r]===w?-1:u[r]===w?1:0},p):p},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&d(e),t=t.replace(_,"='$1']"),n.matchesSelector&&g&&!k[t+" "]&&(!y||!y.test(t))&&(!v||!v.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,p,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==p&&d(e),b(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==p&&d(e);var i=r.attrHandle[t.toLowerCase()],o=i&&D.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(A),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:ue,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return X.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&U.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+I+")"+e+"("+I+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace(W," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),u="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,s){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,y=u&&t.nodeName.toLowerCase(),m=!s&&!u,b=!1;if(v){if(o){while(g){d=t;while(d=d[g])if(u?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&m){b=(p=(l=(c=(f=(d=v)[x]||(d[x]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]||[])[0]===C&&l[1])&&l[2],d=p&&v.childNodes[p];while(d=++p&&d&&d[g]||(b=p=0)||h.pop())if(1===d.nodeType&&++b&&d===t){c[e]=[C,p,b];break}}else if(m&&(b=p=(l=(c=(f=(d=t)[x]||(d[x]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]||[])[0]===C&&l[1]),!1===b)while(d=++p&&d&&d[g]||(b=p=0)||h.pop())if((u?d.nodeName.toLowerCase()===y:1===d.nodeType)&&++b&&(m&&((c=(f=d[x]||(d[x]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]=[C,b]),d===t))break;return(b-=i)===r||b%r==0&&b/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[x]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?ue(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=P(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:ue(function(e){var t=[],n=[],r=u(e.replace($,"$1"));return r[x]?ue(function(e,t,n,i){var o,a=r(e,null,i,[]),u=e.length;while(u--)(o=a[u])&&(e[u]=!(t[u]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:ue(function(e){return function(t){return oe(e,t).length>0}}),contains:ue(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:ue(function(e){return V.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:pe(!1),disabled:pe(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xe(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else y=we(y===a?y.splice(h,y.length):y),i?i(null,a,y,s):q.apply(a,y)})}function Te(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],u=a||r.relative[" "],s=a?1:0,c=me(function(e){return e===t},u,!0),f=me(function(e){return P(t,e)>-1},u,!0),d=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];s1&&be(d),s>1&&ye(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),n,s0,i=e.length>0,o=function(o,a,u,s,c){var f,h,v,y=0,m="0",b=o&&[],x=[],w=l,T=o||i&&r.find.TAG("*",c),E=C+=null==w?1:Math.random()||.1,N=T.length;for(c&&(l=a===p||a||c);m!==N&&null!=(f=T[m]);m++){if(i&&f){h=0,a||f.ownerDocument===p||(d(f),u=!g);while(v=e[h++])if(v(f,a||p,u)){s.push(f);break}c&&(C=E)}n&&((f=!v&&f)&&y--,o&&b.push(f))}if(y+=m,n&&m!==y){h=0;while(v=t[h++])v(b,x,a,u);if(o){if(y>0)while(m--)b[m]||x[m]||(x[m]=L.call(s));x=we(x)}q.apply(s,x),c&&!o&&x.length>0&&y+t.length>1&&oe.uniqueSort(s)}return c&&(C=E,l=w),b};return n?ue(o):o}return u=oe.compile=function(e,t){var n,r=[],i=[],o=k[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Te(t[n]))[x]?r.push(o):i.push(o);(o=k(e,Ee(i,r))).selector=e}return o},s=oe.select=function(e,t,n,i){var o,s,l,c,f,d="function"==typeof e&&e,p=!i&&a(e=d.selector||e);if(n=n||[],1===p.length){if((s=p[0]=p[0].slice(0)).length>2&&"ID"===(l=s[0]).type&&9===t.nodeType&&g&&r.relative[s[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;d&&(t=t.parentNode),e=e.slice(s.shift().value.length)}o=X.needsContext.test(e)?0:s.length;while(o--){if(l=s[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),J.test(s[0].type)&&ge(t.parentNode)||t))){if(s.splice(o,1),!(e=i.length&&ye(s)))return q.apply(n,i),n;break}}}return(d||u(e,p))(i,t,!g,n,!t||J.test(e)&&ge(t.parentNode)||t),n},n.sortStable=x.split("").sort(A).join("")===x,n.detectDuplicates=!!f,d(),n.sortDetached=se(function(e){return 1&e.compareDocumentPosition(p.createElement("fieldset"))}),se(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&se(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),se(function(e){return null==e.getAttribute("disabled")})||le(H,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var N=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},k=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},A=w.expr.match.needsContext;function D(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var S=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function L(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return s.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(L(this,e||[],!1))},not:function(e){return this.pushStack(L(this,e||[],!0))},is:function(e){return!!L(this,"string"==typeof e&&A.test(e)?w(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:q.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),S.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,j=w(r);var O=/^(?:parents|prev(?:Until|All))/,P={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?s.call(w(e),this[0]):s.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function H(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return N(e,"parentNode")},parentsUntil:function(e,t,n){return N(e,"parentNode",n)},next:function(e){return H(e,"nextSibling")},prev:function(e){return H(e,"previousSibling")},nextAll:function(e){return N(e,"nextSibling")},prevAll:function(e){return N(e,"previousSibling")},nextUntil:function(e,t,n){return N(e,"nextSibling",n)},prevUntil:function(e,t,n){return N(e,"previousSibling",n)},siblings:function(e){return k((e.parentNode||{}).firstChild,e)},children:function(e){return k(e.firstChild)},contents:function(e){return D(e,"iframe")?e.contentDocument:(D(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(P[e]||w.uniqueSort(i),O.test(e)&&i.reverse()),this.pushStack(i)}});var I=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(I)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],u=-1,s=function(){for(i=i||e.once,r=t=!0;a.length;u=-1){n=a.shift();while(++u-1)o.splice(n,1),n<=u&&u--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||s()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function B(e){return e}function M(e){throw e}function W(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var u=this,s=arguments,l=function(){var e,l;if(!(t=o&&(r!==M&&(u=void 0,s=[e]),n.rejectWith(u,s))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:B,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:B)),n[2][3].add(a(0,e,g(r)?r:M))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],u=t[5];i[t[1]]=a.add,u&&a.add(function(){r=u},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),u=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&(W(e,a.done(u(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)W(i[n],u(n),a.reject);return a.promise()}});var $=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&$.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function z(){r.removeEventListener("DOMContentLoaded",z),e.removeEventListener("load",z),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",z),e.addEventListener("load",z));var _=function(e,t,n,r,i,o,a){var u=0,s=e.length,l=null==n;if("object"===b(n)){i=!0;for(u in n)_(e,t,u,n[u],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;u1,null,!0)},removeData:function(e){return this.each(function(){J.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=K.get(e,t),n&&(!r||Array.isArray(n)?r=K.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return K.get(e,n)||K.access(e,n,{empty:w.Callbacks("once memory").add(function(){K.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&D(e,t)?w.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var xe=r.documentElement,we=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function Ne(){return!1}function ke(){try{return r.activeElement}catch(e){}}function Ae(e,t,n,r,i,o){var a,u;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(u in t)Ae(e,u,n,r,t[u],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ne;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,u,s,l,c,f,d,p,h,g,v=K.get(e);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(xe,i),n.guid||(n.guid=w.guid++),(s=v.events)||(s=v.events={}),(a=v.handle)||(a=v.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(I)||[""]).length;while(l--)p=g=(u=Te.exec(t[l])||[])[1],h=(u[2]||"").split(".").sort(),p&&(f=w.event.special[p]||{},p=(i?f.delegateType:f.bindType)||p,f=w.event.special[p]||{},c=w.extend({type:p,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(d=s[p])||((d=s[p]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(p,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?d.splice(d.delegateCount++,0,c):d.push(c),w.event.global[p]=!0)}},remove:function(e,t,n,r,i){var o,a,u,s,l,c,f,d,p,h,g,v=K.hasData(e)&&K.get(e);if(v&&(s=v.events)){l=(t=(t||"").match(I)||[""]).length;while(l--)if(u=Te.exec(t[l])||[],p=g=u[1],h=(u[2]||"").split(".").sort(),p){f=w.event.special[p]||{},d=s[p=(r?f.delegateType:f.bindType)||p]||[],u=u[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=d.length;while(o--)c=d[o],!i&&g!==c.origType||n&&n.guid!==c.guid||u&&!u.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(e,c));a&&!d.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||w.removeEvent(e,p,v.handle),delete s[p])}else for(p in s)w.event.remove(e,p+t[l],n,r,!0);w.isEmptyObject(s)&&K.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,u,s=new Array(arguments.length),l=(K.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(s[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&u.push({elem:l,handlers:o})}return l=this,s\x20\t\r\n\f]*)[^>]*)\/>/gi,Se=/