├── .babelrc
├── .gitignore
├── views
├── script.pug
├── index.pug
├── head.pug
└── unauthorized.pug
├── static
├── favicon.png
├── index.css
└── index.js
├── screenshots
└── node-web-console.jpg
├── client
├── css
│ └── index.css
└── js
│ └── index.js
├── config
└── index.js
├── server
├── app.js
└── RPCServer.js
├── package.json
├── README.zh-CN.md
├── README.md
└── LICENSE
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**
2 | .eslintignore
3 | .eslintrc.js
4 | .cache
5 | .vscode
6 |
--------------------------------------------------------------------------------
/views/script.pug:
--------------------------------------------------------------------------------
1 | script.
2 | var NO_LOGIN = #{NO_LOGIN}
3 | script(src="/static/index.js")
4 |
--------------------------------------------------------------------------------
/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChrisCindy/node-web-console/HEAD/static/favicon.png
--------------------------------------------------------------------------------
/screenshots/node-web-console.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChrisCindy/node-web-console/HEAD/screenshots/node-web-console.jpg
--------------------------------------------------------------------------------
/views/index.pug:
--------------------------------------------------------------------------------
1 | doctype html
2 | html
3 | head
4 | include head.pug
5 | body
6 | .shell
7 | include script.pug
8 |
--------------------------------------------------------------------------------
/views/head.pug:
--------------------------------------------------------------------------------
1 | meta(charset="utf-8")
2 | meta(http-equiv="X-UA-Compatible", content="IE=edge")
3 | title Node Web Console
4 | meta(name="viewport", content="width=device-width, initial-scale=1")
5 | meta(name="description", content="Node Web Console")
6 | meta(name="author", content="chriscindy (http://www.fecoding.cn)")
7 | meta(name="robots", content="none")
8 | //- style
9 | link(rel="stylesheet", href="/static/index.css")
10 | link(rel="icon", type="image/png", sizes="16x16" href="/static/favicon.png")
11 |
--------------------------------------------------------------------------------
/client/css/index.css:
--------------------------------------------------------------------------------
1 | body { background-color: #000000; }
2 |
3 | body, .terminal, .cmd, .terminal .terminal-output div div, .terminal .prompt {
4 | color: #cccccc;
5 | font-family: monospace, fixed;
6 | font-size: 15px;
7 | line-height: 18px;
8 | }
9 |
10 | a, a:hover, .terminal a, .terminal a:hover { color: #6c71c4; }
11 |
12 | .spaced { margin: 15px 0; }
13 | .spaced-top { margin: 15px 0 0 0; }
14 | .spaced-bottom { margin: 0 0 15px 0; }
15 |
16 | .configure { margin: 20px; }
17 | .configure .variable { color: #d33682; }
18 | .configure p, .configure ul { margin: 5px 0 0 0; }
19 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // Disable login if set true (don't ask for credentials, be careful)
3 | noLogin: false,
4 | // Single-user credentials
5 | user: 'dev',
6 | password: 'dev',
7 | // Multi-user credentials
8 | // Example:
9 | // account: { 'user1': 'password1', 'user2':'password2' }
10 | accounts: {},
11 | // Password hash algorithm (password must be hashed)
12 | // Example:
13 | // passwordHashAlgorithm: 'md5'
14 | // $PASSWORD_HASH_ALGORITHM: 'sha256'
15 | passwordHashAlgorithm: '',
16 | // Home directory (multi-user mode supported)
17 | // Example:
18 | // homeDirectory: /tmp'
19 | // homeDirectory: {user1': '/home/user1', 'user2': '/home/user2' }
20 | homeDirectory: ''
21 | }
22 |
--------------------------------------------------------------------------------
/views/unauthorized.pug:
--------------------------------------------------------------------------------
1 | doctype html
2 | html
3 | head
4 | include head.pug
5 | body
6 | .configure
7 | p Node Web Console must be configured before use:
8 | ul
9 | li Open the project with your favorite text editor.
10 | li
11 | | Open
12 | span.variable config/index.js
13 | | and enter your
14 | span.variable $USER
15 | | and
16 | span.variable $PASSWORD
17 | | credentials, edit any other settings that you like (see description in the comments).
18 | p
19 | | For more information visit:
20 | a(href="https://github.com/ChrisCindy/node-web-console") https://github.com/ChrisCindy/node-web-console
21 |
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa')
2 | const serve = require('koa-static')
3 | const views = require('koa-views')
4 | const bodyParser = require('koa-bodyparser')
5 | const c2k = require('koa-connect')
6 | const Router = require('koa-router')
7 | const mount = require('koa-mount')
8 | const path = require('path')
9 | const { RPCServer, isConfigured, noLogin } = require('./RPCServer')
10 |
11 | const server = new Koa()
12 | server.use(bodyParser())
13 | server.use(async (ctx, next) => {
14 | try {
15 | ctx.req.body = ctx.request.body
16 | await next()
17 | } catch (err) {
18 | ctx.status = 500
19 | ctx.message = err.message || 'Sorry, an error has occurred.'
20 | }
21 | })
22 |
23 | server.use(views(path.join(__dirname, '../views'), {
24 | extension: 'pug'
25 | }))
26 |
27 | server.use(mount('/static', serve(path.join(__dirname, '../static'))))
28 |
29 | const router = new Router()
30 | router.get('/console', async (ctx) => {
31 | if (isConfigured) {
32 | await ctx.render('index', {
33 | NO_LOGIN: noLogin
34 | })
35 | } else {
36 | await ctx.render('unauthorized')
37 | }
38 | })
39 | router.post('/rpc', c2k(RPCServer.middleware()))
40 |
41 | server.use(router.routes()).use(router.allowedMethods())
42 |
43 | server.listen(3000)
44 | console.log('server started and listens on port 3000')
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-web-console",
3 | "version": "1.0.0",
4 | "description": "a simple web-based SSH, remote shell in your browser based on Node.js, inspired by web-console",
5 | "main": "server/app.js",
6 | "scripts": {
7 | "client:dev": "parcel watch client/js/index.js -d static",
8 | "server:dev": "cross-env NODE_ENV=development nodemon server/app.js",
9 | "client:build": "cross-env NODE_ENV=production parcel build client/js/index.js -d static",
10 | "prod": "npm run client:build && cross-env NODE_ENV=production pm2 start server/app.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/ChrisCindy/node-web-console.git"
15 | },
16 | "keywords": [
17 | "web",
18 | "console",
19 | "nodejs"
20 | ],
21 | "author": "ChrisCindy",
22 | "license": "ISC",
23 | "bugs": {
24 | "url": "https://github.com/ChrisCindy/node-web-console/issues"
25 | },
26 | "homepage": "https://github.com/ChrisCindy/node-web-console#readme",
27 | "dependencies": {
28 | "jayson": "^2.0.6",
29 | "jquery": "^3.3.1",
30 | "jquery-mousewheel": "^3.1.13",
31 | "jquery.terminal": "^1.15.0",
32 | "koa": "^2.5.1",
33 | "koa-bodyparser": "^4.2.0",
34 | "koa-connect": "^2.0.1",
35 | "koa-mount": "^3.0.0",
36 | "koa-router": "^7.4.0",
37 | "koa-static": "^4.0.2",
38 | "koa-views": "^6.1.4",
39 | "natural-compare-lite": "^1.4.0",
40 | "normalize.css": "^8.0.0",
41 | "pug": "^2.0.3"
42 | },
43 | "devDependencies": {
44 | "babel-eslint": "^8.2.3",
45 | "cross-env": "^5.1.5",
46 | "eslint": "^5.14.1",
47 | "eslint-config-standard": "^11.0.0",
48 | "eslint-plugin-import": "^2.11.0",
49 | "eslint-plugin-node": "^6.0.1",
50 | "eslint-plugin-promise": "^3.7.0",
51 | "eslint-plugin-standard": "^3.1.0",
52 | "nodemon": "^1.17.4",
53 | "parcel-bundler": "^1.8.1"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/README.zh-CN.md:
--------------------------------------------------------------------------------
1 | [English](./README.md) | 简体中文
2 | ```
3 | _ __ __ _ __ __ ______ __
4 | / | / /___ ____/ /__ | | / /__ / /_ / ____/___ ____ _________ / /__
5 | / |/ / __ \/ __ / _ \ | | /| / / _ \/ __ \ / / / __ \/ __ \/ ___/ __ \/ / _ \
6 | / /| / /_/ / /_/ / __/ | |/ |/ / __/ /_/ / / /___/ /_/ / / / (__ ) /_/ / / __/
7 | /_/ |_/\____/\__,_/\___/ |__/|__/\___/_.___/ \____/\____/_/ /_/____/\____/_/\___/
8 |
9 | ```
10 | ## 概要
11 |
12 | Node-Web-Console 是基于 Node.js 开发的一个网页版应用(可以理解为网页版的 SSH)。它允许你直接从浏览器向服务器发送并执行 shell 命令。这个项目受到另一个 php 项目 [web-console](https://github.com/nickola/web-console) 的启发并基于其移植而来。Node-Web-Console 非常轻量,其不需要依赖数据库,同时能够在 3 分钟内配置完毕。
13 |
14 | 
15 |
16 | ## 特性
17 |
18 | - 界面整洁友好,看起来就像是真的 shell 终端
19 | - 易于扩展。Node-Web-Console 使用 [Koa](https://github.com/koajs/koa) 作为 web 服务器,你可以很容易地扩展或者将其集成到你自己的项目中
20 | - 安全。你可以在你的服务器上配置 HTTPS(SSL),然后 Node-Web-Console 的所有流量都将受到保护。同时,Node-Web-Console 设置了标记,不允许搜索引擎索引其所有页面
21 | - 极速配置。Node-Web-Console 是一个纯 Node.js 项目,其可被极快地安装和配置使用
22 | - 移动端友好。Node-Web-Console 支持移动浏览器上的软键盘输入。因此你可以在你的移动设备上方便地使用它
23 |
24 | ## 安装
25 |
26 | Node-Web-Console 使用 [Koa](https://github.com/koajs/koa) 作为 web 服务器,因此 node 版本需要至少 7.6.0 或者更高。
27 |
28 | ```shell
29 | git clone https://github.com/ChrisCindy/node-web-console.git
30 | cd node-web-console
31 |
32 | # 安装依赖
33 | npm install
34 | ```
35 |
36 | ## 上手
37 |
38 | - 配置选项
39 |
40 | 打开 `config/index.js` 并输入你的 `$USER` 和 `$PASSWORD` 凭证,同时你可以编辑其他任何设置(注释中有设置的描述)。
41 |
42 | - 启动 web 服务器
43 |
44 | - Development mode
45 |
46 | ```shell
47 | ## 构建并且监听前端脚本及样式表
48 | npm run client:dev
49 |
50 | ## 打开另一个 shell
51 | ## 启动 web 服务器
52 | npm run server:dev
53 | ```
54 |
55 | - Production mode
56 |
57 | ```shell
58 | ## 构建并且压缩前端脚本和样式表
59 | npm run client:build
60 |
61 | ## 使用 pm2 启动 web 服务器
62 | npm run prod
63 | ```
64 |
65 | - 在浏览器打开 `http://localhost:3000/console`,开始使用吧。
66 |
67 |
68 | ## 证书
69 |
70 | Node-Web-Console 使用 [GNU LGPL Version 3](http://www.gnu.org/licenses/lgpl.html) 证书。
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | English | [简体中文](./README.zh-CN.md)
2 | ```
3 | _ __ __ _ __ __ ______ __
4 | / | / /___ ____/ /__ | | / /__ / /_ / ____/___ ____ _________ / /__
5 | / |/ / __ \/ __ / _ \ | | /| / / _ \/ __ \ / / / __ \/ __ \/ ___/ __ \/ / _ \
6 | / /| / /_/ / /_/ / __/ | |/ |/ / __/ /_/ / / /___/ /_/ / / / (__ ) /_/ / / __/
7 | /_/ |_/\____/\__,_/\___/ |__/|__/\___/_.___/ \____/\____/_/ /_/____/\____/_/\___/
8 |
9 | ```
10 | ## Summary
11 |
12 | Node-Web-Console is a web-based Node.js application that allows to execute shell commands on a server directly from a browser (web-based SSH). This project is inspired by the php-based [web-console](https://github.com/nickola/web-console).
13 | The application is very light, does not require any database and can be installed and configured in about 3 minutes.
14 |
15 | 
16 |
17 | ## Features
18 |
19 | - Clean interface, which looks and feels like a real shell terminal
20 | - Easy to extend. Node-Web-Console uses [Koa](https://github.com/koajs/koa) to start the web server. You can easily extend it or integrate it to your own project
21 | - Secure. You can configure HTTPS (SSL) on your web server and all Node-Web-Console traffic will be protected. Also, Node-Web-Console has a mark to search engines that will disallow the Node-Web-Console page from indexing
22 | - Fast configuration. Node-Web-Console is a pure Node.js project and can be installed and configured quickly
23 | - Mobile friendly. Node-Web-Console supports inputs from the virtual keyboard in the mobile browser. So you can use it conveniently in your mobile devices like iPad, iPhone and Android phones.
24 |
25 |
26 | ## Installation
27 |
28 | Node-Web-Console uses [Koa](https://github.com/koajs/koa) to start the web server, so it requires node v7.6.0 or higher.
29 |
30 | ```shell
31 | git clone https://github.com/ChrisCindy/node-web-console.git
32 | cd node-web-console
33 |
34 | # instal dependencies
35 | npm install
36 | ```
37 |
38 | ## Getting Started
39 |
40 | - configure the settings
41 |
42 | open `config/index.js` and enter your `$USER` and `$PASSWORD` credentials, edit any other settings that you like (see description in the comments).
43 |
44 | - start the web server
45 |
46 | - Development mode
47 |
48 | ```shell
49 | ## build and watch client side scripts and stylesheets
50 | npm run client:dev
51 |
52 | ## open another shell
53 | ## start the web server
54 | npm run server:dev
55 | ```
56 |
57 | - Production mode
58 |
59 | ```shell
60 | ## build and minify client side scripts and stylesheets
61 | npm run client:build
62 |
63 | ## start the server with pm2
64 | npm run prod
65 | ```
66 |
67 | - open `http://localhost:3000/console` in your browser and enjoy it.
68 |
69 | ## License
70 |
71 | Node-Web-Console is licensed under [GNU LGPL Version 3](http://www.gnu.org/licenses/lgpl.html) license.
72 |
--------------------------------------------------------------------------------
/client/js/index.js:
--------------------------------------------------------------------------------
1 | // CSS
2 | import 'normalize.css'
3 | import 'jquery.terminal/css/jquery.terminal.min.css'
4 | import '../css/index.css'
5 |
6 | const $ = require('jquery')
7 | const jt = require('jquery.terminal')
8 | jt(window, $)
9 | require('jquery-mousewheel')($)
10 |
11 | const settings = {
12 | // url: `//${window.location.hostname}:4000/`,
13 | url: `/rpc`,
14 | promptPathLength: 32,
15 | domain: document.domain || window.location.host,
16 | isSmallWindow: $(document).width() < 625
17 | }
18 | const environment = {
19 | user: '',
20 | hostname: '',
21 | path: ''
22 | }
23 | /* global NO_LOGIN */
24 | const noLogin = NO_LOGIN
25 | let silentMode = false
26 |
27 | // Default banner
28 | let bannerMain = 'Node Web Console'
29 | const bannerLink = 'https://github.com/ChrisCindy/node-web-console'
30 | let bannerExtra = `${bannerLink}\n`
31 |
32 | // Big banner
33 | if (!settings.isSmallWindow) {
34 | bannerMain =
35 | ' _ __ __ _ __ __ ______ __' +
36 | '\n / | / /___ ____/ /__ | | / /__ / /_ / ____/___ ____ _________ / /__ ' +
37 | '\n / |/ / __ \\/ __ / _ \\ | | /| / / _ \\/ __ \\ / / / __ \\/ __ \\/ ___/ __ \\/ / _ \\' +
38 | '\n / /| / /_/ / /_/ / __/ | |/ |/ / __/ /_/ / / /___/ /_/ / / / (__ ) /_/ / / __/' +
39 | '\n/_/ |_/\\____/\\__,_/\\___/ |__/|__/\\___/_.___/ \\____/\\____/_/ /_/____/\\____/_/\\___/ '
40 | bannerExtra = `\n${bannerLink}\n`
41 | }
42 |
43 | let terminal = null
44 |
45 | init()
46 |
47 | function init () {
48 | terminal = $('.shell').terminal(interpreter, {
49 | login: !noLogin ? login : false,
50 | prompt: makePrompt(),
51 | greetings: !noLogin ? 'You are authenticated' : '',
52 | completion: completion,
53 | exceptionHandler: function (exception) {
54 | if (!silentMode) {
55 | terminal.exception(exception)
56 | }
57 | }
58 | }).css({
59 | overflow: 'auto'
60 | })
61 |
62 | // Logout
63 | if (noLogin) {
64 | terminal.set_token('NO_LOGIN')
65 | } else {
66 | logout()
67 | $(window).on('unload', () => {
68 | logout()
69 | })
70 | }
71 |
72 | // Banner
73 | if (bannerMain) {
74 | terminal.echo(bannerMain)
75 | }
76 | if (bannerExtra) {
77 | terminal.echo(bannerExtra)
78 | }
79 | }
80 |
81 | // Output
82 | function showOutput (output) {
83 | if (output) {
84 | if (typeof output === 'string') {
85 | terminal.echo(output)
86 | } else if (output instanceof Array) {
87 | terminal.echo($.map(output, function (object) {
88 | return $.json_stringify(object)
89 | }).join(' '))
90 | } else if (typeof output === 'object') {
91 | terminal.echo($.json_stringify(output))
92 | } else {
93 | terminal.echo(output)
94 | }
95 | }
96 | }
97 |
98 | // Prompt
99 | function makePrompt () {
100 | let path = environment.path
101 | if (path && path.length > settings.promptPathLength) {
102 | path = `...${path.slice(path.length - settings.promptPathLength + 3)}`
103 | }
104 | return `[[b;#d33682;]${(environment.user || 'user')}]@[[b;#6c71c4;]${(environment.hostname || settings.domain || 'node-web-console')}]${(path || '~')}$ `
105 | }
106 |
107 | function updatePrompt (terminal) {
108 | terminal.set_prompt(makePrompt())
109 | }
110 |
111 | // Environment
112 | function updateEnvironment (terminal, data) {
113 | if (data) {
114 | $.extend(environment, data)
115 | updatePrompt(terminal)
116 | }
117 | }
118 |
119 | // Service
120 | function service (terminal, method, parameters, success, error, options) {
121 | options = $.extend({ 'pause': true }, options)
122 | if (options.pause) {
123 | terminal.pause()
124 | }
125 | $.jrpc(settings.url, method, parameters, function (json) {
126 | if (options.pause) {
127 | terminal.resume()
128 | }
129 |
130 | if (!json.error) {
131 | if (success) {
132 | success(json.result)
133 | }
134 | } else if (error) {
135 | error()
136 | } else {
137 | let message = $.trim(json.error.message || '')
138 | let data = $.trim(json.error.data || '')
139 |
140 | if (!message && data) {
141 | message = data
142 | data = ''
143 | }
144 | terminal.error(`[ERROR] RPC: ${(message || 'Unknown error')}${(data ? (' (' + data + ')') : '')}`)
145 | }
146 | }, function (xhr, status, errorData) {
147 | if (options.pause) {
148 | terminal.resume()
149 | }
150 |
151 | if (error) {
152 | error()
153 | } else {
154 | if (status !== 'abort') {
155 | const response = $.trim(xhr.responseText || '')
156 |
157 | terminal.error(`[ERROR] AJAX: ${(status || 'Unknown error')}${(response ? ('\nServer reponse:\n' + response) : '')}`)
158 | }
159 | }
160 | })
161 | }
162 |
163 | function serviceAuthenticated (terminal, method, parameters, success, error, options) {
164 | const token = terminal.token()
165 | if (token) {
166 | const serviceParameters = [token, environment]
167 | if (parameters && parameters.length) {
168 | serviceParameters.push.apply(serviceParameters, parameters)
169 | }
170 | service(terminal, method, serviceParameters, success, error, options)
171 | } else {
172 | // Should never happen
173 | terminal.error('[ERROR] Access denied (no authentication token found)')
174 | }
175 | }
176 |
177 | // Interpreter
178 | function interpreter (command, terminal) {
179 | command = $.trim(command || '')
180 | if (!command) {
181 | return
182 | }
183 |
184 | const commandParsed = $.terminal.parse_command(command)
185 | let method = null
186 | let parameters = []
187 |
188 | if (commandParsed.name.toLowerCase() === 'cd') {
189 | method = 'cd'
190 | parameters = [commandParsed.args.length ? commandParsed.args[0] : '']
191 | } else {
192 | method = 'run'
193 | parameters = [command]
194 | }
195 |
196 | if (method) {
197 | serviceAuthenticated(terminal, method, parameters, function (result) {
198 | updateEnvironment(terminal, result.environment)
199 | showOutput(result.output)
200 | })
201 | }
202 | }
203 |
204 | // Login
205 | function login (user, password, callback) {
206 | user = $.trim(user || '')
207 | password = $.trim(password || '')
208 |
209 | if (user && password) {
210 | service(terminal, 'login', [user, password], function (result) {
211 | if (result && result.token) {
212 | environment.user = user
213 | // FIXME: updateEnvironment not work Synchronously
214 | setTimeout(() => {
215 | updateEnvironment(terminal, result.environment)
216 | }, 0)
217 | showOutput(result.output)
218 | callback(result.token)
219 | } else {
220 | callback(null)
221 | }
222 | }, function () {
223 | callback(null)
224 | })
225 | } else {
226 | callback(null)
227 | }
228 | }
229 |
230 | // Completion
231 | function completion (pattern, callback) {
232 | const view = this.terminal().export_view()
233 | const command = view.command.substring(0, view.position)
234 |
235 | serviceAuthenticated(terminal, 'completion', [pattern, command], function (result) {
236 | showOutput(result.output)
237 |
238 | if (result.completion && result.completion.length) {
239 | result.completion.reverse()
240 | callback(result.completion)
241 | }
242 | }, null, { pause: false })
243 | }
244 |
245 | // Logout
246 | function logout () {
247 | silentMode = true
248 |
249 | try {
250 | terminal.clear()
251 | terminal.logout()
252 | } catch (error) { }
253 |
254 | silentMode = false
255 | }
256 |
--------------------------------------------------------------------------------
/server/RPCServer.js:
--------------------------------------------------------------------------------
1 | const crypto = require('crypto')
2 | const os = require('os')
3 | const fs = require('fs')
4 | const path = require('path')
5 | const { execSync } = require('child_process')
6 | require('natural-compare-lite')
7 | const jayson = require('jayson')
8 | const config = require('../config')
9 |
10 | function getHash (algorithm, str) {
11 | const hash = crypto.createHash(algorithm)
12 | hash.update(str.trim())
13 | return hash.digest('hex')
14 | }
15 |
16 | function isDirectory (thePath) {
17 | return fs.existsSync(thePath) && fs.statSync(thePath).isDirectory()
18 | }
19 |
20 | // Command execution
21 | function executeCommand (command) {
22 | return execSync(`${command} 2>&1`, {
23 | encoding: 'utf8'
24 | })
25 | }
26 |
27 | // Initializing
28 | if (config.user && config.password) {
29 | config.accounts[config.user] = config.password
30 | }
31 | const isConfigured = config.noLogin || Object.keys(config.accounts).length >= 1
32 |
33 | // RPC Server
34 | let HOME_DIRECTORY = ''
35 |
36 | const methods = {
37 | error: (message) => {
38 | throw new Error(message)
39 | },
40 |
41 | // Authentication
42 | authenticateUser: (user, password) => {
43 | user = user.trim()
44 | password = password.trim()
45 | if (user && password) {
46 | if (config.accounts[user]) {
47 | if (config.passwordHashAlgorithm) {
48 | password = getHash(config.passwordHashAlgorithm, password)
49 | }
50 | if (password === config.accounts[user]) {
51 | return `${user}:${getHash('sha256', password)}`
52 | }
53 | }
54 | }
55 | throw new Error('Incorrect user or password')
56 | },
57 | authenticateToken: (token) => {
58 | if (config.noLogin) {
59 | return true
60 | }
61 | token = token.trim()
62 | const tokenParts = token.split(':', 2)
63 |
64 | if (tokenParts.length === 2) {
65 | const user = tokenParts[0].trim()
66 | const passwordHash = tokenParts[1].trim()
67 |
68 | if (user && passwordHash) {
69 | if (config.accounts) {
70 | const realPasswordHash = getHash('sha256', config.accounts[user])
71 | if (passwordHash === realPasswordHash) {
72 | return user
73 | }
74 | }
75 | }
76 | }
77 |
78 | throw new Error('Incorrect user or password')
79 | },
80 | getHomeDirectory: (user) => {
81 | if (typeof config.homeDirectory === 'string') {
82 | if (config.homeDirectory) {
83 | return config.homeDirectory
84 | }
85 | } else if (typeof user === 'string' && user && config.homeDirectory.user) {
86 | return config.homeDirectory.user
87 | }
88 | return process.cwd()
89 | },
90 |
91 | // Environment
92 | getEnvironment: () => {
93 | return {
94 | path: process.cwd(),
95 | hostname: os.hostname()
96 | }
97 | },
98 | setEnvironment: (environment = {}, methods) => {
99 | const thePath = environment.path || HOME_DIRECTORY
100 | if (thePath) {
101 | if (isDirectory(thePath)) {
102 | try {
103 | process.chdir(thePath)
104 | } catch (e) {
105 | return {
106 | output: 'Unable to change directory to current working directory, updating current directory',
107 | environment: methods.getEnvironment.handler()
108 | }
109 | }
110 | }
111 | } else {
112 | return {
113 | output: 'Current working directory not found, updating current directory',
114 | environment: methods.getEnvironment.handler()
115 | }
116 | }
117 | },
118 |
119 | // Initialization
120 | initialize: (token, environment, methods) => {
121 | const user = methods.authenticateToken.handler(token)
122 | HOME_DIRECTORY = methods.getHomeDirectory.handler(user)
123 | const result = methods.setEnvironment.handler(environment, methods)
124 | if (result) {
125 | return result
126 | }
127 | },
128 |
129 | // Methods
130 | login: (user, password, methods) => {
131 | const result = {
132 | token: methods.authenticateUser.handler(user, password),
133 | environment: methods.getEnvironment.handler()
134 | }
135 | const homeDirectory = methods.getHomeDirectory.handler(user)
136 | if (homeDirectory) {
137 | if (isDirectory(homeDirectory)) {
138 | result.environment.path = homeDirectory
139 | } else {
140 | result.output = `Home directory not found: ${homeDirectory}`
141 | }
142 | }
143 | return result
144 | },
145 | cd: (token, environment, thePath, methods) => {
146 | const result = methods.initialize.handler(token, environment, methods)
147 | if (result) {
148 | return result
149 | }
150 | thePath = thePath.trim()
151 | if (!thePath) {
152 | thePath = HOME_DIRECTORY
153 | }
154 | if (thePath) {
155 | if (isDirectory(thePath)) {
156 | try {
157 | process.chdir(thePath)
158 | } catch (e) {
159 | return {
160 | output: `cd ${thePath}: Unable to change directory`
161 | }
162 | }
163 | } else {
164 | return {
165 | output: `cd ${thePath}: No such directory`
166 | }
167 | }
168 | }
169 | return {
170 | environment: methods.getEnvironment.handler()
171 | }
172 | },
173 | completion: (token, environment, pattern, command, methods) => {
174 | const result = methods.initialize.handler(token, environment, methods)
175 | if (result) {
176 | return result
177 | }
178 | let scanPath = ''
179 | let completionPrefix = ''
180 | let completion = []
181 |
182 | if (pattern) {
183 | if (!isDirectory(pattern)) {
184 | pattern = path.dirname(pattern)
185 | pattern = pattern === '.' ? '' : pattern
186 | }
187 | if (pattern) {
188 | if (isDirectory(pattern)) {
189 | scanPath = completionPrefix = pattern
190 | if (completionPrefix.substr(-1) !== '/') {
191 | completionPrefix += '/'
192 | }
193 | }
194 | } else {
195 | scanPath = process.cwd()
196 | }
197 | } else {
198 | scanPath = process.cwd()
199 | }
200 |
201 | if (scanPath) {
202 | // Loading directory listing
203 | completion = fs.readdirSync(scanPath)
204 | completion.sort(String.naturalCompare)
205 |
206 | // Prefix
207 | if (completionPrefix && completion.length > 0) {
208 | completion = completion.map(c => completionPrefix + c)
209 | }
210 | // Pattern
211 | if (pattern && completion.length > 0) {
212 | completion = completion.filter(c => {
213 | return pattern === c.substr(0, pattern.length)
214 | })
215 | }
216 | }
217 |
218 | return {
219 | completion
220 | }
221 | },
222 |
223 | run: (token, environment, command, methods) => {
224 | const result = methods.initialize.handler(token, environment, methods)
225 | if (result) {
226 | return result
227 | }
228 | let output = command ? executeCommand(command) : ''
229 | if (output && output.substr(-1) === '\n') {
230 | output = output.substr(0, output.length - 1)
231 | }
232 | return {
233 | output
234 | }
235 | }
236 | }
237 |
238 | const RPCServer = jayson.server(methods, {
239 | router (method, params) {
240 | return (p, cb) => {
241 | try {
242 | const result = this._methods[method].handler(...p, this._methods)
243 | cb(null, result)
244 | } catch (e) {
245 | console.log(e)
246 | const error = {
247 | code: 500,
248 | message: e.message || 'Internal Error'
249 | }
250 | cb(error)
251 | }
252 | }
253 | }
254 | })
255 |
256 | module.exports = {
257 | RPCServer,
258 | isConfigured,
259 | noLogin: config.noLogin
260 | }
261 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/static/index.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}/*!
2 | * __ _____ ________ __
3 | * / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / /
4 | * __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ /
5 | * / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__
6 | * \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/
7 | * \/ /____/ version 1.15.0
8 | * http://terminal.jcubic.pl
9 | *
10 | * This file is part of jQuery Terminal.
11 | *
12 | * Copyright (c) 2011-2018 Jakub Jankiewicz
13 | * Released under the MIT license
14 | *
15 | * Date: Sun, 13 May 2018 09:34:11 +0000
16 | */.cmd .format,.cmd .prompt,.cmd .prompt div,.terminal .terminal-output .format,.terminal .terminal-output div div{display:inline-block}.cmd,.terminal h1,.terminal h2,.terminal h3,.terminal h4,.terminal h5,.terminal h6,.terminal pre{margin:0}.terminal h1,.terminal h2,.terminal h3,.terminal h4,.terminal h5,.terminal h6{line-height:1.2em}.cmd .clipboard{position:absolute;left:-16px;top:0;width:20px;height:16px;background:transparent;border:none;color:transparent;outline:none;padding:0;resize:none;z-index:1000;overflow:hidden;white-space:pre;text-indent:-9999em}.terminal audio,.terminal canvas,.terminal img,.terminal object,.terminal value{cursor:default}.terminal .error{color:red}.terminal{position:relative;overflow-y:auto}.cmd,.terminal{contain:content}body.terminal{height:100%;min-height:100vh;margin:0}.terminal>div{overflow:hidden}.terminal>.font .resizer,.terminal>.resizer{position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;z-index:-1;visibility:hidden;height:100%;border:none;padding:0;width:100%}.cmd{padding:0;position:relative;float:left;padding-bottom:3px}.terminal a[tabindex="1000"],.terminal a[tabindex="1000"]:active,.terminal a[tabindex="1000"]:focus{outline:none}.cmd .inverted,.terminal .inverted{background-color:#aaa;color:#000}.cmd .cursor{border-bottom:3px solid transparent;margin-bottom:-3px;background-clip:content-box}.cmd .cursor.blink{animation:terminal-blink 1s infinite step-start;border-left:1px solid transparent;margin-left:-1px}.bar.cmd .inverted,.bar.terminal .inverted{border-left-color:#aaa}.cmd .prompt,.terminal .terminal-output div div{display:block;line-height:14px;height:auto}.terminal .terminal-output>div:not(.raw) div{white-space:nowrap}.cmd .prompt>span{float:left}.cmd,.terminal{font-family:monospace;color:#aaa;background-color:#000;font-size:12px;line-height:14px;box-sizing:border-box;cursor:text}.cmd div{clear:both}.cmd .prompt+div{clear:right}.terminal-output>div>div{min-height:14px}terminal .terminal-output>div{margin-top:-1px}.terminal-output>div.raw>div *{overflow-wrap:break-word;word-wrap:break-word}.terminal .font{position:absolute;font-size:inherit;width:1em;height:1em;top:-100%;left:0;margin-bottom:1px}.terminal .terminal-output div span{display:inline-block}.cmd>span:not(.prompt){float:left}.cmd .prompt span.line{display:block;float:none}.terminal table{border-collapse:collapse}.terminal td{border:1px solid #aaa}.cmd .prompt span::-moz-selection,.cmd>div::-moz-selection,.cmd>div span::-moz-selection,.cmd>span::-moz-selection,.cmd>span span::-moz-selection,.cmd div::-moz-selection,.terminal .terminal-output .raw div::-moz-selection,.terminal .terminal-output div div::-moz-selection,.terminal .terminal-output div div a::-moz-selection,.terminal .terminal-output div span::-moz-selection,.terminal h1::-moz-selection,.terminal h2::-moz-selection,.terminal h3::-moz-selection,.terminal h4::-moz-selection,.terminal h5::-moz-selection,.terminal h6::-moz-selection,.terminal pre::-moz-selection,.terminal td::-moz-selection{background-color:#aaa;color:#000}.cmd .prompt span::selection,.cmd>div::selection,.cmd>div span::selection,.cmd>span::selection,.cmd>span span::selection,.cmd div::selection,.terminal .terminal-output .raw div::selection,.terminal .terminal-output div div::selection,.terminal .terminal-output div div a::selection,.terminal .terminal-output div span::selection,.terminal h1::selection,.terminal h2::selection,.terminal h3::selection,.terminal h4::selection,.terminal h5::selection,.terminal h6::selection,.terminal pre::selection,.terminal td::selection{background-color:hsla(0,0%,67%,.99);color:#000}.terminal .terminal-output div.error,.terminal .terminal-output div.error div{color:red;color:var(--error-color,red)}.tilda{position:fixed;top:0;left:0;width:100%;z-index:1100}.ui-dialog-content .terminal{width:100%;height:100%;box-sizing:border-box}.ui-dialog .ui-dialog-content.dterm{padding:0}.clear{clear:both}.terminal a{color:#0f60ff;color:var(--link-color,#0f60ff)}.terminal a:hover{background:#0f60ff;background:var(--link-color,#0f60ff);color:var(--background,#000);text-decoration:none}.terminal .terminal-fill{position:absolute;left:0;top:-100%;width:100%;height:100%;margin:1px 0 0;border:none;opacity:.01;pointer-events:none;box-sizing:border-box}.terminal,.terminal .terminal-fill{padding:10px}@keyframes terminal-blink{0%,to{background-color:#000;color:#aaa}50%{background-color:#bbb;color:#000}}@keyframes terminal-bar{0%,to{border-left-color:#aaa}50%{border-left-color:#000}}@keyframes terminal-underline{0%,to{border-bottom-color:#aaa;position:relative;line-height:12px;margin-top:1px;border-left:none;margin-left:0}50%{border-bottom-color:#000;position:relative;line-height:12px;margin-top:1px;border-left:none;margin-left:0}}.underline-animation .cursor.blink{border-left:none;animation-name:terminal-underline}.bar-animation .cursor.blink{animation-name:terminal-bar}@supports (--css:variables){.cmd,.terminal{color:var(--color,#aaa);background-color:var(--background,#000)}.terminal .font{width:calc(var(--size, 1) * 1em);height:calc(var(--size, 1) * 1em)}.terminal span[style*="--length"]{width:calc(var(--length, 1) * var(--char-width, 7.23438) * 1px);display:inline-block}.cmd,.cmd .prompt,.terminal,.terminal .terminal-output>div>div{font-size:calc(var(--size, 1) * 12px);line-height:calc(var(--size, 1) * 14px)}.terminal .terminal-output>div>div{min-height:calc(var(--size, 1) * 14px)}.cmd .inverted,.terminal .inverted{background-color:var(--color,#aaa);color:var(--background,#000)}.cmd .cursor.blink{animation:var(--animation,terminal-blink) 1s infinite step-start;color:var(--color,#aaa);background-color:var(--background,#000)}.cmd .prompt span::-moz-selection,.cmd>div::-moz-selection,.cmd>div span::-moz-selection,.cmd>span::-moz-selection,.cmd>span span::-moz-selection,.cmd div::-moz-selection,.terminal .terminal-output div div::-moz-selection,.terminal .terminal-output div div a::-moz-selection,.terminal .terminal-output div span::-moz-selection,.terminal h1::-moz-selection,.terminal h2::-moz-selection,.terminal h3::-moz-selection,.terminal h4::-moz-selection,.terminal h5::-moz-selection,.terminal h6::-moz-selection,.terminal pre::-moz-selection,.terminal td::-moz-selection{background-color:var(--color,#aaa);color:var(--background,#000)}.cmd .prompt span::selection,.cmd>div::selection,.cmd>div span::selection,.cmd>span::selection,.cmd>span span::selection,.cmd div::selection,.terminal .terminal-output div div::selection,.terminal .terminal-output div div a::selection,.terminal .terminal-output div span::selection,.terminal h1::selection,.terminal h2::selection,.terminal h3::selection,.terminal h4::selection,.terminal h5::selection,.terminal h6::selection,.terminal pre::selection,.terminal td::selection{background-color:var(--color,hsla(0,0%,67%,.99));color:var(--background,#000)}@keyframes terminal-blink{0%,to{background-color:var(--background,#000);color:var(--color,#aaa)}50%{background-color:var(--color,#aaa);color:var(--background,#000)}}@keyframes terminal-bar{0%,to{border-left-color:var(--background,#000)}50%{border-left-color:var(--color,#aaa)}}@keyframes terminal-underline{0%,to{border-bottom-color:var(--background,#000);position:relative;line-height:calc(var(--size, 1) * 12px);margin-top:calc(var(--size, 1) * 1px);border-left:none;margin-left:0}50%{border-bottom-color:var(--color,#aaa);position:relative;line-height:calc(var(--size, 1) * 12px);margin-top:calc(var(--size, 1) * 1px);border-left:none;margin-left:0}}}@supports (-ms-ime-align:auto){.cmd .prompt span::selection,.cmd>div::selection,.cmd>div span::selection,.cmd>span::selection,.cmd>span span::selection,.cmd div::selection,.terminal .terminal-output div div::selection,.terminal .terminal-output div div a::selection,.terminal .terminal-output div span::selection,.terminal h1::selection,.terminal h2::selection,.terminal h3::selection,.terminal h4::selection,.terminal h5::selection,.terminal h6::selection,.terminal pre::selection,.terminal td::selection{background-color:hsla(0,0%,67%,.99);color:#000}}body{background-color:#000}.cmd,.terminal,.terminal .prompt,.terminal .terminal-output div div,body{color:#ccc;font-family:monospace,fixed;font-size:15px;line-height:18px}.terminal a,.terminal a:hover,a,a:hover{color:#6c71c4}.spaced{margin:15px 0}.spaced-top{margin:15px 0 0}.spaced-bottom{margin:0 0 15px}.configure{margin:20px}.configure .variable{color:#d33682}.configure p,.configure ul{margin:5px 0 0}
--------------------------------------------------------------------------------
/static/index.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,n,t){function i(n,t){function o(e){return i(o.resolve(e))}function c(r){return e[n][1][r]||r}if(!r[n]){if(!e[n]){var l="function"==typeof parcelRequire&&parcelRequire;if(!t&&l)return l(n,!0);if(u)return u(n,!0);if(f&&"string"==typeof n)return f(n);var p=new Error("Cannot find module '"+n+"'");throw p.code="MODULE_NOT_FOUND",p}o.resolve=c;var a=r[n]=new i.Module(n);e[n][0].call(a.exports,o,a,a.exports,this)}return r[n].exports}function o(e){this.id=e,this.bundle=i,this.exports={}}var u="function"==typeof parcelRequire&&parcelRequire,f="function"==typeof require&&require;i.isParcelRequire=!0,i.Module=o,i.modules=e,i.cache=r,i.parent=u;for(var c=0;c1)for(var n=1;n0&&t-1 in e)}w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return a.call(this)},get:function(e){return null==e?a.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(a.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&&n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","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?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,y,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!v||!v.test(e))){if(1!==T)m=t,y=e;else if("object"!==t.nodeName.toLowerCase()){for((c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;s--;)h[s]="#"+c+" "+ye(h[s]);y=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(y)try{return L.apply(r,m.querySelectorAll(y)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];return function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){for(var n=e.split("|"),i=n.length;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)for(;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 pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(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 se(function(t){return t=+t,se(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(h=(d=a).documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).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(void 0!==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=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==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){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&g)return t.getElementsByClassName(e)},y=[],v=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||v.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),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=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),y.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),y=y.length&&new RegExp(y.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.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)for(;t=t.parentNode;)if(t===e)return!0;return!1},D=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===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(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],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[r]===s[r];)r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[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,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.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(D),f){for(;t=e[o++];)t===e[o]&&(i=r.push(o));for(;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 for(;t=e[r++];)n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,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 V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.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("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==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($," ")+" ").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),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,y=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(v){if(o){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&m){for(x=(d=(l=(c=(f=(p=v)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==y:1!==p.nodeType)||!++x||(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p!==t)););return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){for(var r,o=i(e,t),a=o.length;a--;)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){for(var o,a=r(e,null,i,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.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===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!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 G.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){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s-1&&(o[l]=!(a[l]=f))}}else y=be(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):L.apply(a,y)})}function Te(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[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}];u1&&xe(p),u>1&&ye(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,v,y=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){for(h=0,a||f.ownerDocument===d||(p(f),s=!g);v=e[h++];)if(v(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!v&&f)&&y--,o&&x.push(f))}if(y+=m,n&&m!==y){for(h=0;v=t[h++];)v(x,b,a,s);if(o){if(y>0)for(;m--;)x[m]||b[m]||(b[m]=j.call(u));b=be(b)}L.apply(u,b),c&&!o&&b.length>0&&y+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}(o,i))).selector=e}return s},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}for(o=V.needsContext.test(e)?0:u.length;o--&&(l=u[o],!r.relative[c=l.type]);)if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ye(u)))return L.apply(n,i),n;break}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(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&&ue(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}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(t);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 k=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return v(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 l.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(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var r,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:i,!0)),A.test(r[1])&&w.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(o=i.getElementById(r[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(i);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}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?l.call(w(e),this[0]):l.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))}}),w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(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&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){return e}function I(e){throw e}function W(e,t,n,r){var i;try{e&&v(i=e.promise)?i.call(e).done(t).fail(n):e&&v(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1)for(n=a.shift();++s-1;)o.splice(n,1),n<=s&&s--}),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||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},w.extend({Deferred:function(e){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=v(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&v(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(e,r,i){var o=0;function a(e,n,r,i){return function(){var s=this,u=arguments,l=function(){var t,l;if(!(e=o&&(r!==I&&(s=void 0,u=[t]),n.rejectWith(s,u))}};e?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),t.setTimeout(c))}}return w.Deferred(function(t){n[0][3].add(a(0,t,v(i)?i:R,t.notifyWith)),n[1][3].add(a(0,t,v(e)?e:R)),n[2][3].add(a(0,t,v(r)?r:I))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},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),e&&e.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=a.call(arguments),o=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?a.call(arguments):n,--t||o.resolveWith(r,i)}};if(t<=1&&(W(e,o.done(s(n)).resolve,o.reject,!t),"pending"===o.state()||v(i[n]&&i[n].then)))return o.then();for(;n--;)W(i[n],s(n),o.reject);return o.promise()}});var $=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(e,n){t.console&&t.console.warn&&e&&$.test(e.name)&&t.console.warn("jQuery.Deferred exception: "+e.message,e.stack,n)},w.readyException=function(e){t.setTimeout(function(){throw e})};var B=w.Deferred();function F(){i.removeEventListener("DOMContentLoaded",F),t.removeEventListener("load",F),w.ready()}w.fn.ready=function(e){return B.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||B.resolveWith(i,[w]))}}),w.ready.then=B.then,"complete"===i.readyState||"loading"!==i.readyState&&!i.documentElement.doScroll?t.setTimeout(w.ready):(i.addEventListener("DOMContentLoaded",F),t.addEventListener("load",F));var _=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===b(n))for(s in i=!0,n)_(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,v(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(;s1,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=Q.get(e,t),n&&(!r||Array.isArray(n)?r=Q.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);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){w.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Q.get(e,n)||Q.access(e,n,{empty:w.Callbacks("once memory").add(function(){Q.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,pe=/^$|^module$|\/(?:java|ecma)script/i,de={option:[1,""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};function he(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ge(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=he(f.appendChild(o),"script"),l&&ge(a),n)for(c=0;o=a[c++];)pe.test(o.type||"")&&n.push(o);return f}ve=i.createDocumentFragment().appendChild(i.createElement("div")),(ye=i.createElement("input")).setAttribute("type","radio"),ye.setAttribute("checked","checked"),ye.setAttribute("name","t"),ve.appendChild(ye),g.checkClone=ve.cloneNode(!0).cloneNode(!0).lastChild.checked,ve.innerHTML="",g.noCloneChecked=!!ve.cloneNode(!0).lastChild.defaultValue;var be=i.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return i.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)De(e,s,n,r,t[s],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=ke;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,s,u,l,c,f,p,d,h,g,v=Q.get(e);if(v)for(n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(t){return void 0!==w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;l--;)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){for(l=(t=(t||"").match(M)||[""]).length;l--;)if(d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){for(f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||w.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=w.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=w.event.special[s.type]||{};for(u[0]=s,t=1;t=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&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/