commit
e3944df189
@ -0,0 +1,18 @@ |
||||
{ |
||||
"presets": [ |
||||
["env", { |
||||
"targets": { |
||||
"browsers": [ |
||||
"last 2 versions", |
||||
"> 4%", |
||||
"ie 11", |
||||
"safari 8", |
||||
"android 4.4" |
||||
] |
||||
} |
||||
}], |
||||
["minify", { |
||||
"mergeVars": false |
||||
}] |
||||
] |
||||
} |
@ -0,0 +1,9 @@ |
||||
# possibly minified output |
||||
js |
||||
out |
||||
|
||||
# libraries |
||||
jssrc/lib |
||||
|
||||
# php generated file |
||||
jssrc/lang.js |
@ -0,0 +1,191 @@ |
||||
{ |
||||
"parserOptions": { |
||||
"ecmaVersion": 8, |
||||
"ecmaFeatures": { |
||||
"experimentalObjectRestSpread": true, |
||||
"jsx": true |
||||
}, |
||||
"sourceType": "module" |
||||
}, |
||||
|
||||
"env": { |
||||
"es6": true, |
||||
"node": true |
||||
}, |
||||
|
||||
"plugins": [ |
||||
"import", |
||||
"node", |
||||
"promise", |
||||
"standard" |
||||
], |
||||
|
||||
"globals": { |
||||
"document": false, |
||||
"navigator": false, |
||||
"window": false |
||||
}, |
||||
|
||||
"rules": { |
||||
"accessor-pairs": "error", |
||||
"arrow-spacing": ["error", { "before": true, "after": true }], |
||||
"block-spacing": ["error", "always"], |
||||
"brace-style": ["warn", "1tbs", { "allowSingleLine": true }], |
||||
"camelcase": ["off", { "properties": "never" }], |
||||
"comma-dangle": ["error", { |
||||
"arrays": "never", |
||||
"objects": "never", |
||||
"imports": "never", |
||||
"exports": "never", |
||||
"functions": "never" |
||||
}], |
||||
"comma-spacing": ["error", { "before": false, "after": true }], |
||||
"comma-style": ["error", "last"], |
||||
"constructor-super": "error", |
||||
"curly": ["error", "multi-line"], |
||||
"dot-location": ["error", "property"], |
||||
"eol-last": "error", |
||||
"eqeqeq": ["error", "smart"], |
||||
"func-call-spacing": ["error", "never"], |
||||
"generator-star-spacing": ["error", { "before": true, "after": true }], |
||||
"handle-callback-err": ["error", "^(err|error)$" ], |
||||
"indent": ["error", 2, { "SwitchCase": 1 }], |
||||
"key-spacing": ["error", { "beforeColon": false, "afterColon": true }], |
||||
"keyword-spacing": ["error", { "before": true, "after": true }], |
||||
"new-cap": ["error", { "newIsCap": true, "capIsNew": false }], |
||||
"new-parens": "error", |
||||
"no-array-constructor": "error", |
||||
"no-caller": "error", |
||||
"no-class-assign": "error", |
||||
"no-compare-neg-zero": "error", |
||||
"no-cond-assign": "error", |
||||
"no-const-assign": "error", |
||||
"no-constant-condition": ["error", { "checkLoops": false }], |
||||
"no-control-regex": "error", |
||||
"no-debugger": "error", |
||||
"no-delete-var": "error", |
||||
"no-dupe-args": "error", |
||||
"no-dupe-class-members": "error", |
||||
"no-dupe-keys": "error", |
||||
"no-duplicate-case": "error", |
||||
"no-empty-character-class": "error", |
||||
"no-empty-pattern": "error", |
||||
"no-eval": "error", |
||||
"no-ex-assign": "error", |
||||
"no-extend-native": "warn", |
||||
"no-extra-bind": "error", |
||||
"no-extra-boolean-cast": "error", |
||||
"no-extra-parens": ["error", "functions"], |
||||
"no-fallthrough": "error", |
||||
"no-floating-decimal": "error", |
||||
"no-func-assign": "error", |
||||
"no-global-assign": "error", |
||||
"no-implied-eval": "error", |
||||
"no-inner-declarations": ["error", "functions"], |
||||
"no-invalid-regexp": "error", |
||||
"no-irregular-whitespace": "error", |
||||
"no-iterator": "error", |
||||
"no-label-var": "error", |
||||
"no-labels": ["error", { "allowLoop": false, "allowSwitch": false }], |
||||
"no-lone-blocks": "warn", |
||||
"no-mixed-operators": ["error", { |
||||
"groups": [ |
||||
["==", "!=", "===", "!==", ">", ">=", "<", "<="], |
||||
["&&", "||"], |
||||
["in", "instanceof"] |
||||
], |
||||
"allowSamePrecedence": true |
||||
}], |
||||
"no-mixed-spaces-and-tabs": "error", |
||||
"no-multi-spaces": "warn", |
||||
"no-multi-str": "error", |
||||
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }], |
||||
"no-negated-in-lhs": "error", |
||||
"no-new": "error", |
||||
"no-new-func": "error", |
||||
"no-new-object": "error", |
||||
"no-new-require": "error", |
||||
"no-new-symbol": "error", |
||||
"no-new-wrappers": "error", |
||||
"no-obj-calls": "error", |
||||
"no-octal": "error", |
||||
"no-octal-escape": "error", |
||||
"no-path-concat": "error", |
||||
"no-proto": "error", |
||||
"no-redeclare": "error", |
||||
"no-regex-spaces": "error", |
||||
"no-return-assign": ["error", "except-parens"], |
||||
"no-return-await": "error", |
||||
"no-self-assign": "error", |
||||
"no-self-compare": "error", |
||||
"no-sequences": "error", |
||||
"no-shadow-restricted-names": "error", |
||||
"no-sparse-arrays": "error", |
||||
"no-tabs": "error", |
||||
"no-template-curly-in-string": "error", |
||||
"no-this-before-super": "error", |
||||
"no-throw-literal": "error", |
||||
"no-trailing-spaces": "off", |
||||
"no-undef": "off", |
||||
"no-undef-init": "error", |
||||
"no-unexpected-multiline": "error", |
||||
"no-unmodified-loop-condition": "error", |
||||
"no-unneeded-ternary": ["error", { "defaultAssignment": false }], |
||||
"no-unreachable": "error", |
||||
"no-unsafe-finally": "error", |
||||
"no-unsafe-negation": "error", |
||||
"no-unused-expressions": ["warn", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true }], |
||||
"no-unused-vars": ["off", { "vars": "local", "args": "none", "ignoreRestSiblings": true }], |
||||
"no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }], |
||||
"no-useless-call": "error", |
||||
"no-useless-computed-key": "error", |
||||
"no-useless-constructor": "error", |
||||
"no-useless-escape": "error", |
||||
"no-useless-rename": "error", |
||||
"no-useless-return": "error", |
||||
"no-whitespace-before-property": "error", |
||||
"no-with": "error", |
||||
"object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }], |
||||
"one-var": ["error", { "initialized": "never" }], |
||||
"operator-linebreak": ["error", "after", { "overrides": { "?": "before", ":": "before" } }], |
||||
"padded-blocks": ["error", { "blocks": "never", "switches": "never", "classes": "never" }], |
||||
"prefer-promise-reject-errors": "error", |
||||
"quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }], |
||||
"rest-spread-spacing": ["error", "never"], |
||||
"semi": ["error", "never"], |
||||
"semi-spacing": ["error", { "before": false, "after": true }], |
||||
"space-before-blocks": ["error", "always"], |
||||
"space-before-function-paren": ["error", "always"], |
||||
"space-in-parens": ["error", "never"], |
||||
"space-infix-ops": "error", |
||||
"space-unary-ops": ["error", { "words": true, "nonwords": false }], |
||||
"spaced-comment": ["error", "always", { |
||||
"line": { "markers": ["*package", "!", "/", ","] }, |
||||
"block": { "balanced": true, "markers": ["*package", "!", ",", ":", "::", "flow-include"], "exceptions": ["*"] } |
||||
}], |
||||
"symbol-description": "error", |
||||
"template-curly-spacing": ["error", "never"], |
||||
"template-tag-spacing": ["error", "never"], |
||||
"unicode-bom": ["error", "never"], |
||||
"use-isnan": "error", |
||||
"valid-typeof": ["error", { "requireStringLiterals": true }], |
||||
"wrap-iife": ["error", "any", { "functionPrototypeMethods": true }], |
||||
"yield-star-spacing": ["error", "both"], |
||||
"yoda": ["error", "never"], |
||||
|
||||
"import/export": "error", |
||||
"import/first": "error", |
||||
"import/no-duplicates": "error", |
||||
"import/no-webpack-loader-syntax": "error", |
||||
|
||||
"node/no-deprecated-api": "error", |
||||
"node/process-exit-as-throw": "error", |
||||
|
||||
"promise/param-names": "error", |
||||
|
||||
"standard/array-bracket-even-spacing": ["error", "either"], |
||||
"standard/computed-property-even-spacing": ["error", "even"], |
||||
"standard/no-callback-literal": "error", |
||||
"standard/object-curly-even-spacing": ["error", "either"] |
||||
} |
||||
} |
@ -0,0 +1,373 @@ |
||||
Mozilla Public License Version 2.0 |
||||
================================== |
||||
|
||||
1. Definitions |
||||
-------------- |
||||
|
||||
1.1. "Contributor" |
||||
means each individual or legal entity that creates, contributes to |
||||
the creation of, or owns Covered Software. |
||||
|
||||
1.2. "Contributor Version" |
||||
means the combination of the Contributions of others (if any) used |
||||
by a Contributor and that particular Contributor's Contribution. |
||||
|
||||
1.3. "Contribution" |
||||
means Covered Software of a particular Contributor. |
||||
|
||||
1.4. "Covered Software" |
||||
means Source Code Form to which the initial Contributor has attached |
||||
the notice in Exhibit A, the Executable Form of such Source Code |
||||
Form, and Modifications of such Source Code Form, in each case |
||||
including portions thereof. |
||||
|
||||
1.5. "Incompatible With Secondary Licenses" |
||||
means |
||||
|
||||
(a) that the initial Contributor has attached the notice described |
||||
in Exhibit B to the Covered Software; or |
||||
|
||||
(b) that the Covered Software was made available under the terms of |
||||
version 1.1 or earlier of the License, but not also under the |
||||
terms of a Secondary License. |
||||
|
||||
1.6. "Executable Form" |
||||
means any form of the work other than Source Code Form. |
||||
|
||||
1.7. "Larger Work" |
||||
means a work that combines Covered Software with other material, in |
||||
a separate file or files, that is not Covered Software. |
||||
|
||||
1.8. "License" |
||||
means this document. |
||||
|
||||
1.9. "Licensable" |
||||
means having the right to grant, to the maximum extent possible, |
||||
whether at the time of the initial grant or subsequently, any and |
||||
all of the rights conveyed by this License. |
||||
|
||||
1.10. "Modifications" |
||||
means any of the following: |
||||
|
||||
(a) any file in Source Code Form that results from an addition to, |
||||
deletion from, or modification of the contents of Covered |
||||
Software; or |
||||
|
||||
(b) any new file in Source Code Form that contains any Covered |
||||
Software. |
||||
|
||||
1.11. "Patent Claims" of a Contributor |
||||
means any patent claim(s), including without limitation, method, |
||||
process, and apparatus claims, in any patent Licensable by such |
||||
Contributor that would be infringed, but for the grant of the |
||||
License, by the making, using, selling, offering for sale, having |
||||
made, import, or transfer of either its Contributions or its |
||||
Contributor Version. |
||||
|
||||
1.12. "Secondary License" |
||||
means either the GNU General Public License, Version 2.0, the GNU |
||||
Lesser General Public License, Version 2.1, the GNU Affero General |
||||
Public License, Version 3.0, or any later versions of those |
||||
licenses. |
||||
|
||||
1.13. "Source Code Form" |
||||
means the form of the work preferred for making modifications. |
||||
|
||||
1.14. "You" (or "Your") |
||||
means an individual or a legal entity exercising rights under this |
||||
License. For legal entities, "You" includes any entity that |
||||
controls, is controlled by, or is under common control with You. For |
||||
purposes of this definition, "control" means (a) the power, direct |
||||
or indirect, to cause the direction or management of such entity, |
||||
whether by contract or otherwise, or (b) ownership of more than |
||||
fifty percent (50%) of the outstanding shares or beneficial |
||||
ownership of such entity. |
||||
|
||||
2. License Grants and Conditions |
||||
-------------------------------- |
||||
|
||||
2.1. Grants |
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free, |
||||
non-exclusive license: |
||||
|
||||
(a) under intellectual property rights (other than patent or trademark) |
||||
Licensable by such Contributor to use, reproduce, make available, |
||||
modify, display, perform, distribute, and otherwise exploit its |
||||
Contributions, either on an unmodified basis, with Modifications, or |
||||
as part of a Larger Work; and |
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer |
||||
for sale, have made, import, and otherwise transfer either its |
||||
Contributions or its Contributor Version. |
||||
|
||||
2.2. Effective Date |
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution |
||||
become effective for each Contribution on the date the Contributor first |
||||
distributes such Contribution. |
||||
|
||||
2.3. Limitations on Grant Scope |
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under |
||||
this License. No additional rights or licenses will be implied from the |
||||
distribution or licensing of Covered Software under this License. |
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a |
||||
Contributor: |
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software; |
||||
or |
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's |
||||
modifications of Covered Software, or (ii) the combination of its |
||||
Contributions with other software (except as part of its Contributor |
||||
Version); or |
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of |
||||
its Contributions. |
||||
|
||||
This License does not grant any rights in the trademarks, service marks, |
||||
or logos of any Contributor (except as may be necessary to comply with |
||||
the notice requirements in Section 3.4). |
||||
|
||||
2.4. Subsequent Licenses |
||||
|
||||
No Contributor makes additional grants as a result of Your choice to |
||||
distribute the Covered Software under a subsequent version of this |
||||
License (see Section 10.2) or under the terms of a Secondary License (if |
||||
permitted under the terms of Section 3.3). |
||||
|
||||
2.5. Representation |
||||
|
||||
Each Contributor represents that the Contributor believes its |
||||
Contributions are its original creation(s) or it has sufficient rights |
||||
to grant the rights to its Contributions conveyed by this License. |
||||
|
||||
2.6. Fair Use |
||||
|
||||
This License is not intended to limit any rights You have under |
||||
applicable copyright doctrines of fair use, fair dealing, or other |
||||
equivalents. |
||||
|
||||
2.7. Conditions |
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted |
||||
in Section 2.1. |
||||
|
||||
3. Responsibilities |
||||
------------------- |
||||
|
||||
3.1. Distribution of Source Form |
||||
|
||||
All distribution of Covered Software in Source Code Form, including any |
||||
Modifications that You create or to which You contribute, must be under |
||||
the terms of this License. You must inform recipients that the Source |
||||
Code Form of the Covered Software is governed by the terms of this |
||||
License, and how they can obtain a copy of this License. You may not |
||||
attempt to alter or restrict the recipients' rights in the Source Code |
||||
Form. |
||||
|
||||
3.2. Distribution of Executable Form |
||||
|
||||
If You distribute Covered Software in Executable Form then: |
||||
|
||||
(a) such Covered Software must also be made available in Source Code |
||||
Form, as described in Section 3.1, and You must inform recipients of |
||||
the Executable Form how they can obtain a copy of such Source Code |
||||
Form by reasonable means in a timely manner, at a charge no more |
||||
than the cost of distribution to the recipient; and |
||||
|
||||
(b) You may distribute such Executable Form under the terms of this |
||||
License, or sublicense it under different terms, provided that the |
||||
license for the Executable Form does not attempt to limit or alter |
||||
the recipients' rights in the Source Code Form under this License. |
||||
|
||||
3.3. Distribution of a Larger Work |
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice, |
||||
provided that You also comply with the requirements of this License for |
||||
the Covered Software. If the Larger Work is a combination of Covered |
||||
Software with a work governed by one or more Secondary Licenses, and the |
||||
Covered Software is not Incompatible With Secondary Licenses, this |
||||
License permits You to additionally distribute such Covered Software |
||||
under the terms of such Secondary License(s), so that the recipient of |
||||
the Larger Work may, at their option, further distribute the Covered |
||||
Software under the terms of either this License or such Secondary |
||||
License(s). |
||||
|
||||
3.4. Notices |
||||
|
||||
You may not remove or alter the substance of any license notices |
||||
(including copyright notices, patent notices, disclaimers of warranty, |
||||
or limitations of liability) contained within the Source Code Form of |
||||
the Covered Software, except that You may alter any license notices to |
||||
the extent required to remedy known factual inaccuracies. |
||||
|
||||
3.5. Application of Additional Terms |
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support, |
||||
indemnity or liability obligations to one or more recipients of Covered |
||||
Software. However, You may do so only on Your own behalf, and not on |
||||
behalf of any Contributor. You must make it absolutely clear that any |
||||
such warranty, support, indemnity, or liability obligation is offered by |
||||
You alone, and You hereby agree to indemnify every Contributor for any |
||||
liability incurred by such Contributor as a result of warranty, support, |
||||
indemnity or liability terms You offer. You may include additional |
||||
disclaimers of warranty and limitations of liability specific to any |
||||
jurisdiction. |
||||
|
||||
4. Inability to Comply Due to Statute or Regulation |
||||
--------------------------------------------------- |
||||
|
||||
If it is impossible for You to comply with any of the terms of this |
||||
License with respect to some or all of the Covered Software due to |
||||
statute, judicial order, or regulation then You must: (a) comply with |
||||
the terms of this License to the maximum extent possible; and (b) |
||||
describe the limitations and the code they affect. Such description must |
||||
be placed in a text file included with all distributions of the Covered |
||||
Software under this License. Except to the extent prohibited by statute |
||||
or regulation, such description must be sufficiently detailed for a |
||||
recipient of ordinary skill to be able to understand it. |
||||
|
||||
5. Termination |
||||
-------------- |
||||
|
||||
5.1. The rights granted under this License will terminate automatically |
||||
if You fail to comply with any of its terms. However, if You become |
||||
compliant, then the rights granted under this License from a particular |
||||
Contributor are reinstated (a) provisionally, unless and until such |
||||
Contributor explicitly and finally terminates Your grants, and (b) on an |
||||
ongoing basis, if such Contributor fails to notify You of the |
||||
non-compliance by some reasonable means prior to 60 days after You have |
||||
come back into compliance. Moreover, Your grants from a particular |
||||
Contributor are reinstated on an ongoing basis if such Contributor |
||||
notifies You of the non-compliance by some reasonable means, this is the |
||||
first time You have received notice of non-compliance with this License |
||||
from such Contributor, and You become compliant prior to 30 days after |
||||
Your receipt of the notice. |
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent |
||||
infringement claim (excluding declaratory judgment actions, |
||||
counter-claims, and cross-claims) alleging that a Contributor Version |
||||
directly or indirectly infringes any patent, then the rights granted to |
||||
You by any and all Contributors for the Covered Software under Section |
||||
2.1 of this License shall terminate. |
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all |
||||
end user license agreements (excluding distributors and resellers) which |
||||
have been validly granted by You or Your distributors under this License |
||||
prior to termination shall survive termination. |
||||
|
||||
************************************************************************ |
||||
* * |
||||
* 6. Disclaimer of Warranty * |
||||
* ------------------------- * |
||||
* * |
||||
* Covered Software is provided under this License on an "as is" * |
||||
* basis, without warranty of any kind, either expressed, implied, or * |
||||
* statutory, including, without limitation, warranties that the * |
||||
* Covered Software is free of defects, merchantable, fit for a * |
||||
* particular purpose or non-infringing. The entire risk as to the * |
||||
* quality and performance of the Covered Software is with You. * |
||||
* Should any Covered Software prove defective in any respect, You * |
||||
* (not any Contributor) assume the cost of any necessary servicing, * |
||||
* repair, or correction. This disclaimer of warranty constitutes an * |
||||
* essential part of this License. No use of any Covered Software is * |
||||
* authorized under this License except under this disclaimer. * |
||||
* * |
||||
************************************************************************ |
||||
|
||||
************************************************************************ |
||||
* * |
||||
* 7. Limitation of Liability * |
||||
* -------------------------- * |
||||
* * |
||||
* Under no circumstances and under no legal theory, whether tort * |
||||
* (including negligence), contract, or otherwise, shall any * |
||||
* Contributor, or anyone who distributes Covered Software as * |
||||
* permitted above, be liable to You for any direct, indirect, * |
||||
* special, incidental, or consequential damages of any character * |
||||
* including, without limitation, damages for lost profits, loss of * |
||||
* goodwill, work stoppage, computer failure or malfunction, or any * |
||||
* and all other commercial damages or losses, even if such party * |
||||
* shall have been informed of the possibility of such damages. This * |
||||
* limitation of liability shall not apply to liability for death or * |
||||
* personal injury resulting from such party's negligence to the * |
||||
* extent applicable law prohibits such limitation. Some * |
||||
* jurisdictions do not allow the exclusion or limitation of * |
||||
* incidental or consequential damages, so this exclusion and * |
||||
* limitation may not apply to You. * |
||||
* * |
||||
************************************************************************ |
||||
|
||||
8. Litigation |
||||
------------- |
||||
|
||||
Any litigation relating to this License may be brought only in the |
||||
courts of a jurisdiction where the defendant maintains its principal |
||||
place of business and such litigation shall be governed by laws of that |
||||
jurisdiction, without reference to its conflict-of-law provisions. |
||||
Nothing in this Section shall prevent a party's ability to bring |
||||
cross-claims or counter-claims. |
||||
|
||||
9. Miscellaneous |
||||
---------------- |
||||
|
||||
This License represents the complete agreement concerning the subject |
||||
matter hereof. If any provision of this License is held to be |
||||
unenforceable, such provision shall be reformed only to the extent |
||||
necessary to make it enforceable. Any law or regulation which provides |
||||
that the language of a contract shall be construed against the drafter |
||||
shall not be used to construe this License against a Contributor. |
||||
|
||||
10. Versions of the License |
||||
--------------------------- |
||||
|
||||
10.1. New Versions |
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section |
||||
10.3, no one other than the license steward has the right to modify or |
||||
publish new versions of this License. Each version will be given a |
||||
distinguishing version number. |
||||
|
||||
10.2. Effect of New Versions |
||||
|
||||
You may distribute the Covered Software under the terms of the version |
||||
of the License under which You originally received the Covered Software, |
||||
or under the terms of any subsequent version published by the license |
||||
steward. |
||||
|
||||
10.3. Modified Versions |
||||
|
||||
If you create software not governed by this License, and you want to |
||||
create a new license for such software, you may create and use a |
||||
modified version of this License if you rename the license and remove |
||||
any references to the name of the license steward (except to note that |
||||
such modified license differs from this License). |
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary |
||||
Licenses |
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With |
||||
Secondary Licenses under the terms of this version of the License, the |
||||
notice described in Exhibit B of this License must be attached. |
||||
|
||||
Exhibit A - Source Code Form License Notice |
||||
------------------------------------------- |
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public |
||||
License, v. 2.0. If a copy of the MPL was not distributed with this |
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/. |
||||
|
||||
If it is not possible or desirable to put the notice in a particular |
||||
file, then You may include the notice in a location (such as a LICENSE |
||||
file in a relevant directory) where a recipient would be likely to look |
||||
for such a notice. |
||||
|
||||
You may add additional accurate notices of copyright ownership. |
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice |
||||
--------------------------------------------------------- |
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as |
||||
defined by the Mozilla Public License, v. 2.0. |
Binary file not shown.
@ -1,189 +1,130 @@ |
||||
/** Global generic init */ |
||||
$.ready(function () { |
||||
// Checkbox UI (checkbox CSS and hidden input with int value)
|
||||
$('.Row.checkbox').forEach(function(x) { |
||||
var inp = x.querySelector('input'); |
||||
var box = x.querySelector('.box'); |
||||
$('.Row.checkbox').forEach(function (x) { |
||||
let inp = x.querySelector('input') |
||||
let box = x.querySelector('.box') |
||||
|
||||
$(box).toggleClass('checked', inp.value); |
||||
$(box).toggleClass('checked', inp.value) |
||||
|
||||
var hdl = function() { |
||||
inp.value = 1 - inp.value; |
||||
let hdl = function () { |
||||
inp.value = 1 - inp.value |
||||
$(box).toggleClass('checked', inp.value) |
||||
}; |
||||
} |
||||
|
||||
$(x).on('click', hdl).on('keypress', cr(hdl)); |
||||
}); |
||||
$(x).on('click', hdl).on('keypress', cr(hdl)) |
||||
}) |
||||
|
||||
// Expanding boxes on mobile
|
||||
$('.Box.mobcol,.Box.fold').forEach(function(x) { |
||||
var h = x.querySelector('h2'); |
||||
|
||||
var hdl = function() { |
||||
$(x).toggleClass('expanded'); |
||||
}; |
||||
$(h).on('click', hdl).on('keypress', cr(hdl)); |
||||
}); |
||||
|
||||
$('form').forEach(function(x) { |
||||
$(x).on('keypress', function(e) { |
||||
if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) { |
||||
x.submit(); |
||||
$('.Box.mobcol,.Box.fold').forEach(function (x) { |
||||
let h = x.querySelector('h2') |
||||
|
||||
let hdl = function () { |
||||
$(x).toggleClass('expanded') |
||||
} |
||||
$(h).on('click', hdl).on('keypress', cr(hdl)) |
||||
}) |
||||
|
||||
$('form').forEach(function (x) { |
||||
$(x).on('keypress', function (e) { |
||||
if ((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey) { |
||||
x.submit() |
||||
} |
||||
}) |
||||
}); |
||||
}) |
||||
|
||||
// loader dots...
|
||||
setInterval(function () { |
||||
$('.anim-dots').each(function (x) { |
||||
var $x = $(x); |
||||
var dots = $x.html() + '.'; |
||||
if (dots.length == 5) dots = '.'; |
||||
$x.html(dots); |
||||
}); |
||||
}, 1000); |
||||
let $x = $(x) |
||||
let dots = $x.html() + '.' |
||||
if (dots.length === 5) dots = '.' |
||||
$x.html(dots) |
||||
}) |
||||
}, 1000) |
||||
|
||||
// flipping number boxes with the mouse wheel
|
||||
$('input[type=number]').on('mousewheel', function(e) { |
||||
var $this = $(this); |
||||
var val = +$this.val(); |
||||
if (isNaN(val)) val = 1; |
||||
|
||||
var step = +($this.attr('step') || 1); |
||||
var min = +$this.attr('min'); |
||||
var max = +$this.attr('max'); |
||||
if(e.wheelDelta > 0) { |
||||
val += step; |
||||
$('input[type=number]').on('mousewheel', function (e) { |
||||
let $this = $(this) |
||||
let val = +$this.val() |
||||
if (isNaN(val)) val = 1 |
||||
|
||||
const step = +($this.attr('step') || 1) |
||||
const min = +$this.attr('min') |
||||
const max = +$this.attr('max') |
||||
if (e.wheelDelta > 0) { |
||||
val += step |
||||
} else { |
||||
val -= step; |
||||
val -= step |
||||
} |
||||
|
||||
if (typeof min != 'undefined') val = Math.max(val, +min); |
||||
if (typeof max != 'undefined') val = Math.min(val, +max); |
||||
$this.val(val); |
||||
if (undef(min)) val = Math.max(val, +min) |
||||
if (undef(max)) val = Math.min(val, +max) |
||||
$this.val(val) |
||||
|
||||
if ("createEvent" in document) { |
||||
var evt = document.createEvent("HTMLEvents"); |
||||
evt.initEvent("change", false, true); |
||||
$this[0].dispatchEvent(evt); |
||||
if ('createEvent' in document) { |
||||
let evt = document.createEvent('HTMLEvents') |
||||
evt.initEvent('change', false, true) |
||||
$this[0].dispatchEvent(evt) |
||||
} else { |
||||
$this[0].fireEvent("onchange"); |
||||
$this[0].fireEvent('onchange') |
||||
} |
||||
|
||||
e.preventDefault(); |
||||
}); |
||||
e.preventDefault() |
||||
}) |
||||
|
||||
var errAt = location.search.indexOf('err='); |
||||
// populate the form errors box from GET arg ?err=...
|
||||
// (a way to pass errors back from server via redirect)
|
||||
let errAt = location.search.indexOf('err=') |
||||
if (errAt !== -1 && qs('.Box.errors')) { |
||||
var errs = location.search.substr(errAt+4).split(','); |
||||
var hres = []; |
||||
errs.forEach(function(er) { |
||||
var lbl = qs('label[for="'+er+'"]'); |
||||
let errs = location.search.substr(errAt + 4).split(',') |
||||
let hres = [] |
||||
errs.forEach(function (er) { |
||||
let lbl = qs('label[for="' + er + '"]') |
||||
if (lbl) { |
||||
lbl.classList.add('error'); |
||||
hres.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, '')); |
||||
lbl.classList.add('error') |
||||
hres.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, '')) |
||||
} else { |
||||
hres.push(er); |
||||
hres.push(er) |
||||
} |
||||
}); |
||||
}) |
||||
|
||||
qs('.Box.errors .list').innerHTML = hres.join(', '); |
||||
qs('.Box.errors').classList.remove('hidden'); |
||||
qs('.Box.errors .list').innerHTML = hres.join(', ') |
||||
qs('.Box.errors').classList.remove('hidden') |
||||
} |
||||
|
||||
Modal.init(); |
||||
Notify.init(); |
||||
Modal.init() |
||||
Notify.init() |
||||
|
||||
// remove tabindixes from h2 if wide
|
||||
if (window.innerWidth > 550) { |
||||
$('.Box h2').forEach(function (x) { |
||||
x.removeAttribute('tabindex'); |
||||
}); |
||||
x.removeAttribute('tabindex') |
||||
}) |
||||
|
||||
// brand works as a link back to term in widescreen mode
|
||||
var br = qs('#brand'); |
||||
br && br.addEventListener('click', function() { |
||||
location.href='/'; // go to terminal
|
||||
}); |
||||
let br = qs('#brand') |
||||
br && br.addEventListener('click', function () { |
||||
location.href = '/' // go to terminal
|
||||
}) |
||||
} |
||||
}); |
||||
}) |
||||
|
||||
$._loader = function(vis) { |
||||
$('#loader').toggleClass('show', vis); |
||||
}; |
||||
// setup the ajax loader
|
||||
$._loader = function (vis) { |
||||
$('#loader').toggleClass('show', vis) |
||||
} |
||||
|
||||
function showPage() { |
||||
$('#content').addClass('load'); |
||||
// reveal content on load
|
||||
function showPage () { |
||||
$('#content').addClass('load') |
||||
} |
||||
|
||||
$.ready(function() { |
||||
// Auto reveal pages other than the terminal (sets window.noAutoShow)
|
||||
$.ready(function () { |
||||
if (window.noAutoShow !== true) { |
||||
setTimeout(function () { |
||||
showPage(); |
||||
}, 1); |
||||
} |
||||
}); |
||||
|
||||
|
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */ |
||||
if (!String.fromCodePoint) { |
||||
(function() { |
||||
var defineProperty = (function() { |
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try { |
||||
var object = {}; |
||||
var $defineProperty = Object.defineProperty; |
||||
var result = $defineProperty(object, object, object) && $defineProperty; |
||||
} catch(error) {} |
||||
return result; |
||||
}()); |
||||
var stringFromCharCode = String.fromCharCode; |
||||
var floor = Math.floor; |
||||
var fromCodePoint = function() { |
||||
var MAX_SIZE = 0x4000; |
||||
var codeUnits = []; |
||||
var highSurrogate; |
||||
var lowSurrogate; |
||||
var index = -1; |
||||
var length = arguments.length; |
||||
if (!length) { |
||||
return ''; |
||||
} |
||||
var result = ''; |
||||
while (++index < length) { |
||||
var codePoint = Number(arguments[index]); |
||||
if ( |
||||
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
|
||||
codePoint < 0 || // not a valid Unicode code point
|
||||
codePoint > 0x10FFFF || // not a valid Unicode code point
|
||||
floor(codePoint) != codePoint // not an integer
|
||||
) { |
||||
throw RangeError('Invalid code point: ' + codePoint); |
||||
} |
||||
if (codePoint <= 0xFFFF) { // BMP code point
|
||||
codeUnits.push(codePoint); |
||||
} else { // Astral code point; split in surrogate halves
|
||||
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
|
||||
codePoint -= 0x10000; |
||||
highSurrogate = (codePoint >> 10) + 0xD800; |
||||
lowSurrogate = (codePoint % 0x400) + 0xDC00; |
||||
codeUnits.push(highSurrogate, lowSurrogate); |
||||
showPage() |
||||
}, 1) |
||||
} |
||||
if (index + 1 == length || codeUnits.length > MAX_SIZE) { |
||||
result += stringFromCharCode.apply(null, codeUnits); |
||||
codeUnits.length = 0; |
||||
} |
||||
} |
||||
return result; |
||||
}; |
||||
if (defineProperty) { |
||||
defineProperty(String, 'fromCodePoint', { |
||||
'value': fromCodePoint, |
||||
'configurable': true, |
||||
'writable': true |
||||
}); |
||||
} else { |
||||
String.fromCodePoint = fromCodePoint; |
||||
} |
||||
}()); |
||||
} |
||||
}) |
||||
|
@ -0,0 +1,106 @@ |
||||
window.attachDebugScreen = function (screen) { |
||||
const debugCanvas = mk('canvas') |
||||
const ctx = debugCanvas.getContext('2d') |
||||
|
||||
debugCanvas.style.position = 'absolute' |
||||
// hackity hack should probably set this in CSS
|
||||
debugCanvas.style.top = '6px' |
||||
debugCanvas.style.left = '6px' |
||||
debugCanvas.style.pointerEvents = 'none' |
||||
|
||||
let addCanvas = function () { |
||||
if (!debugCanvas.parentNode) screen.canvas.parentNode.appendChild(debugCanvas) |
||||
} |
||||
let removeCanvas = function () { |
||||
if (debugCanvas.parentNode) debugCanvas.parentNode.removeChild(debugCanvas) |
||||
} |
||||
let updateCanvasSize = function () { |
||||
let { width, height, devicePixelRatio } = screen.window |
||||
let cellSize = screen.getCellSize() |
||||
debugCanvas.width = width * cellSize.width * devicePixelRatio |
||||
debugCanvas.height = height * cellSize.height * devicePixelRatio |
||||
debugCanvas.style.width = `${width * cellSize.width}px` |
||||
debugCanvas.style.height = `${height * cellSize.height}px` |
||||
} |
||||
|
||||
let startTime, endTime, lastReason |
||||
let cells = new Map() |
||||
|
||||
let startDrawing |
||||
|
||||
screen._debug = { |
||||
drawStart (reason) { |
||||
lastReason = reason |
||||
startTime = Date.now() |
||||
}, |
||||
drawEnd () { |
||||
endTime = Date.now() |
||||
console.log(`Draw: ${lastReason} (${(endTime - startTime)} ms) with fancy graphics: ${screen.window.graphics}`) |
||||
startDrawing() |
||||
}, |
||||
setCell (cell, flags) { |
||||
cells.set(cell, [flags, Date.now()]) |
||||
} |
||||
} |
||||
|
||||
let isDrawing = false |
||||
|
||||
let drawLoop = function () { |
||||
if (isDrawing) requestAnimationFrame(drawLoop) |
||||
|
||||
let { devicePixelRatio, width, height } = screen.window |
||||
let { width: cellWidth, height: cellHeight } = screen.getCellSize() |
||||
let screenLength = width * height |
||||
let now = Date.now() |
||||
|
||||
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0) |
||||
ctx.clearRect(0, 0, width * cellWidth, height * cellHeight) |
||||
|
||||
let activeCells = 0 |
||||
for (let cell = 0; cell < screenLength; cell++) { |
||||
if (!cells.has(cell) || cells.get(cell)[0] === 0) continue |
||||
|
||||
let [flags, timestamp] = cells.get(cell) |
||||
let elapsedTime = (now - timestamp) / 1000 |
||||
|
||||
if (elapsedTime > 1) continue |
||||
|
||||
activeCells++ |
||||
ctx.globalAlpha = 0.5 * Math.max(0, 1 - elapsedTime) |
||||
|
||||
let x = cell % width |
||||
let y = Math.floor(cell / width) |
||||
|
||||
if (flags & 1) { |
||||
// redrawn
|
||||
ctx.fillStyle = '#f0f' |
||||
} |
||||
if (flags & 2) { |
||||
// updated
|
||||
ctx.fillStyle = '#0f0' |
||||
} |
||||
|
||||
ctx.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight) |
||||
|
||||
if (flags & 4) { |
||||
// wide cell
|
||||
ctx.lineWidth = 2 |
||||
ctx.strokeStyle = '#f00' |
||||
ctx.strokeRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight) |
||||
} |
||||
} |
||||
|
||||
if (activeCells === 0) { |
||||
isDrawing = false |
||||
removeCanvas() |
||||
} |
||||
} |
||||
|
||||
startDrawing = function () { |
||||
if (isDrawing) return |
||||
addCanvas() |
||||
updateCanvasSize() |
||||
isDrawing = true |
||||
drawLoop() |
||||
} |
||||
} |
@ -1,8 +1,8 @@ |
||||
// Generated from PHP locale file
|
||||
var _tr = { |
||||
let _tr = { |
||||
"wifi.connected_ip_is": "Connected, IP is ", |
||||
"wifi.not_conn": "Not connected.", |
||||
"wifi.enter_passwd": "Enter password for \":ssid:\"" |
||||
}; |
||||
|
||||
function tr(key) { return _tr[key] || '?'+key+'?'; } |
||||
function tr (key) { return _tr[key] || '?' + key + '?' } |
||||
|
@ -0,0 +1,63 @@ |
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */ |
||||
if (!String.fromCodePoint) { |
||||
(function () { |
||||
var defineProperty = (function () { |
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try { |
||||
var object = {}; |
||||
var $defineProperty = Object.defineProperty; |
||||
var result = $defineProperty(object, object, object) && $defineProperty; |
||||
} catch (error) { |
||||
} |
||||
return result; |
||||
}()); |
||||
var stringFromCharCode = String.fromCharCode; |
||||
var floor = Math.floor; |
||||
var fromCodePoint = function () { |
||||
var MAX_SIZE = 0x4000; |
||||
var codeUnits = []; |
||||
var highSurrogate; |
||||
var lowSurrogate; |
||||
var index = -1; |
||||
var length = arguments.length; |
||||
if (!length) { |
||||
return ''; |
||||
} |
||||
var result = ''; |
||||
while (++index < length) { |
||||
var codePoint = Number(arguments[index]); |
||||
if ( |
||||
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
|
||||
codePoint < 0 || // not a valid Unicode code point
|
||||
codePoint > 0x10FFFF || // not a valid Unicode code point
|
||||
floor(codePoint) != codePoint // not an integer
|
||||
) { |
||||
throw RangeError('Invalid code point: ' + codePoint); |
||||
} |
||||
if (codePoint <= 0xFFFF) { // BMP code point
|
||||
codeUnits.push(codePoint); |
||||
} else { // Astral code point; split in surrogate halves
|
||||
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
|
||||
codePoint -= 0x10000; |
||||
highSurrogate = (codePoint >> 10) + 0xD800; |
||||
lowSurrogate = (codePoint % 0x400) + 0xDC00; |
||||
codeUnits.push(highSurrogate, lowSurrogate); |
||||
} |
||||
if (index + 1 == length || codeUnits.length > MAX_SIZE) { |
||||
result += stringFromCharCode.apply(null, codeUnits); |
||||
codeUnits.length = 0; |
||||
} |
||||
} |
||||
return result; |
||||
}; |
||||
if (defineProperty) { |
||||
defineProperty(String, 'fromCodePoint', { |
||||
'value': fromCodePoint, |
||||
'configurable': true, |
||||
'writable': true |
||||
}); |
||||
} else { |
||||
String.fromCodePoint = fromCodePoint; |
||||
} |
||||
}()); |
||||
} |
@ -1,44 +1,44 @@ |
||||
/** Module for toggling a modal overlay */ |
||||
(function () { |
||||
var modal = {}; |
||||
var curCloseCb = null; |
||||
let modal = {} |
||||
let curCloseCb = null |
||||
|
||||
modal.show = function (sel, closeCb) { |
||||
var $m = $(sel); |
||||
$m.removeClass('hidden visible'); |
||||
let $m = $(sel) |
||||
$m.removeClass('hidden visible') |
||||
setTimeout(function () { |
||||
$m.addClass('visible'); |
||||
}, 1); |
||||
curCloseCb = closeCb; |
||||
}; |
||||
$m.addClass('visible') |
||||
}, 1) |
||||
curCloseCb = closeCb |
||||
} |
||||
|
||||
modal.hide = function (sel) { |
||||
var $m = $(sel); |
||||
$m.removeClass('visible'); |
||||
let $m = $(sel) |
||||
$m.removeClass('visible') |
||||
setTimeout(function () { |
||||
$m.addClass('hidden'); |
||||
if (curCloseCb) curCloseCb(); |
||||
}, 500); // transition time
|
||||
}; |
||||
$m.addClass('hidden') |
||||
if (curCloseCb) curCloseCb() |
||||
}, 500) // transition time
|
||||
} |
||||
|
||||
modal.init = function () { |
||||
// close modal by click outside the dialog
|
||||
$('.Modal').on('click', function () { |
||||
if ($(this).hasClass('no-close')) return; // this is a no-close modal
|
||||
modal.hide(this); |
||||
}); |
||||
if ($(this).hasClass('no-close')) return // this is a no-close modal
|
||||
modal.hide(this) |
||||
}) |
||||
|
||||
$('.Dialog').on('click', function (e) { |
||||
e.stopImmediatePropagation(); |
||||
}); |
||||
e.stopImmediatePropagation() |
||||
}) |
||||
|
||||
// Hide all modals on esc
|
||||
$(window).on('keydown', function (e) { |
||||
if (e.which == 27) { |
||||
modal.hide('.Modal'); |
||||
if (e.which === 27) { |
||||
modal.hide('.Modal') |
||||
} |
||||
}) |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
window.Modal = modal; |
||||
})(); |
||||
window.Modal = modal |
||||
})() |
||||
|
@ -1,32 +1,35 @@ |
||||
(function (nt) { |
||||
var sel = '#notif'; |
||||
window.Notify = (function () { |
||||
let nt = {} |
||||
const sel = '#notif' |
||||
|
||||
var hideTmeo1; // timeout to start hiding (transition)
|
||||
var hideTmeo2; // timeout to add the hidden class
|
||||
let hideTmeo1 // timeout to start hiding (transition)
|
||||
let hideTmeo2 // timeout to add the hidden class
|
||||
|
||||
nt.show = function (message, timeout) { |
||||
$(sel).html(message); |
||||
Modal.show(sel); |
||||
$(sel).html(message) |
||||
Modal.show(sel) |
||||
|
||||
clearTimeout(hideTmeo1); |
||||
clearTimeout(hideTmeo2); |
||||
clearTimeout(hideTmeo1) |
||||
clearTimeout(hideTmeo2) |
||||
|
||||
if (undef(timeout)) timeout = 2500; |
||||
if (undef(timeout)) timeout = 2500 |
||||
|
||||
hideTmeo1 = setTimeout(nt.hide, timeout); |
||||
}; |
||||
hideTmeo1 = setTimeout(nt.hide, timeout) |
||||
} |
||||
|
||||
nt.hide = function () { |
||||
var $m = $(sel); |
||||
$m.removeClass('visible'); |
||||
let $m = $(sel) |
||||
$m.removeClass('visible') |
||||
hideTmeo2 = setTimeout(function () { |
||||
$m.addClass('hidden'); |
||||
}, 250); // transition time
|
||||
}; |
||||
|
||||
nt.init = function() { |
||||
$(sel).on('click', function() { |
||||
nt.hide(this); |
||||
}); |
||||
}; |
||||
})(window.Notify = {}); |
||||
$m.addClass('hidden') |
||||
}, 250) // transition time
|
||||
} |
||||
|
||||
nt.init = function () { |
||||
$(sel).on('click', function () { |
||||
nt.hide(this) |
||||
}) |
||||
} |
||||
|
||||
return nt |
||||
})() |
||||
|
@ -0,0 +1,104 @@ |
||||
window.initSoftKeyboard = function (screen) { |
||||
const input = qs('#softkb-input') |
||||
if (!input) return // abort, we're not on the terminal page
|
||||
|
||||
let keyboardOpen = false |
||||
|
||||
let updateInputPosition = function () { |
||||
if (!keyboardOpen) return |
||||
|
||||
let [x, y] = screen.gridToScreen(screen.cursor.x, screen.cursor.y, true) |
||||
input.style.transform = `translate(${x}px, ${y}px)` |
||||
} |
||||
|
||||
input.addEventListener('focus', () => { |
||||
keyboardOpen = true |
||||
updateInputPosition() |
||||
}) |
||||
|
||||
input.addEventListener('blur', () => (keyboardOpen = false)) |
||||
|
||||
screen.on('cursor-moved', updateInputPosition) |
||||
|
||||
window.kbOpen = function openSoftKeyboard (open) { |
||||
keyboardOpen = open |
||||
updateInputPosition() |
||||
if (open) input.focus() |
||||
else input.blur() |
||||
} |
||||
|
||||
let lastCompositionString = '' |
||||
let compositing = false |
||||
|
||||
let sendInputDelta = function (newValue) { |
||||
let resend = false |
||||
if (newValue.length > lastCompositionString.length) { |
||||
if (newValue.startsWith(lastCompositionString)) { |
||||
// characters have been added at the end
|
||||
Input.sendString(newValue.substr(lastCompositionString.length)) |
||||
} else resend = true |
||||
} else if (newValue.length < lastCompositionString.length) { |
||||
if (lastCompositionString.startsWith(newValue)) { |
||||
// characters have been removed at the end
|
||||
Input.sendString('\b'.repeat(lastCompositionString.length - |
||||
newValue.length)) |
||||
} else resend = true |
||||
} else if (newValue !== lastCompositionString) resend = true |
||||
|
||||
if (resend) { |
||||
// the entire string changed; resend everything
|
||||
Input.sendString('\b'.repeat(lastCompositionString.length) + |
||||
newValue) |
||||
} |
||||
lastCompositionString = newValue |
||||
} |
||||
|
||||
input.addEventListener('keydown', e => { |
||||
if (e.key === 'Unidentified') return |
||||
|
||||
input.value = '' |
||||
|
||||
if (e.key === 'Backspace') { |
||||
e.preventDefault() |
||||
Input.sendString('\b') |
||||
} else if (e.key === 'Enter') { |
||||
e.preventDefault() |
||||
Input.sendString('\x0d') |
||||
} |
||||
}) |
||||
|
||||
input.addEventListener('keypress', e => { |
||||
e.stopPropagation() |
||||
}) |
||||
|
||||
input.addEventListener('input', e => { |
||||
e.stopPropagation() |
||||
|
||||
if (e.isComposing) { |
||||
sendInputDelta(e.data) |
||||
} else { |
||||
if (e.inputType === 'insertCompositionText') Input.sendString(e.data) |
||||
else if (e.inputType === 'deleteContentBackward') { |
||||
lastCompositionString = '' |
||||
sendInputDelta('') |
||||
} else if (e.inputType === 'insertText') { |
||||
Input.sendString(e.data) |
||||
} |
||||
} |
||||
}) |
||||
|
||||
input.addEventListener('compositionstart', e => { |
||||
lastCompositionString = '' |
||||
compositing = true |
||||
console.log('compositionstart') |
||||
}) |
||||
|
||||
input.addEventListener('compositionend', e => { |
||||
lastCompositionString = '' |
||||
compositing = false |
||||
input.value = '' |
||||
console.log('compositionend') |
||||
}) |
||||
|
||||
screen.on('open-soft-keyboard', () => input.focus()) |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,50 @@ |
||||
/** Init the terminal sub-module - called from HTML */ |
||||
window.termInit = function () { |
||||
Conn.init(); |
||||
Input.init(); |
||||
TermUpl.init(); |
||||
}; |
||||
window.termInit = function (labels, theme) { |
||||
Conn.init() |
||||
Input.init() |
||||
TermUpl.init() |
||||
|
||||
const screen = new window.TermScreen() |
||||
|
||||
let didNotifyAboutScreen = false |
||||
Object.defineProperty(window, 'Screen', { |
||||
get () { |
||||
if (!didNotifyAboutScreen) { |
||||
console.warn('Use local variables instead of window.Screen') |
||||
didNotifyAboutScreen = true |
||||
} |
||||
return screen |
||||
} |
||||
}) |
||||
|
||||
qs('#screen').appendChild(screen.canvas) |
||||
screen.load(labels, theme) // load labels and theme
|
||||
|
||||
{ |
||||
let fitScreen = false |
||||
let fitScreenIfNeeded = function fitScreenIfNeeded () { |
||||
screen.window.fitIntoWidth = fitScreen ? window.innerWidth - 20 : 0 |
||||
screen.window.fitIntoHeight = fitScreen ? window.innerHeight : 0 |
||||
} |
||||
fitScreenIfNeeded() |
||||
window.addEventListener('resize', fitScreenIfNeeded) |
||||
|
||||
window.toggleFitScreen = function () { |
||||
fitScreen = !fitScreen |
||||
const resizeButtonIcon = qs('#resize-button-icon') |
||||
if (fitScreen) { |
||||
resizeButtonIcon.classList.remove('icn-resize-small') |
||||
resizeButtonIcon.classList.add('icn-resize-full') |
||||
} else { |
||||
resizeButtonIcon.classList.remove('icn-resize-full') |
||||
resizeButtonIcon.classList.add('icn-resize-small') |
||||
} |
||||
fitScreenIfNeeded() |
||||
} |
||||
} |
||||
|
||||
window.initSoftKeyboard(screen) |
||||
if (window.attachDebugScreen) window.attachDebugScreen(screen) |
||||
|
||||
window.termScreen = screen // for debugging
|
||||
} |
||||
|
@ -1,134 +1,137 @@ |
||||
/** Handle connections */ |
||||
var Conn = (function () { |
||||
var ws; |
||||
var heartbeatTout; |
||||
var pingIv; |
||||
var xoff = false; |
||||
var autoXoffTout; |
||||
var reconTout; |
||||
window.Conn = (function () { |
||||
let ws |
||||
let heartbeatTout |
||||
let pingIv |
||||
let xoff = false |
||||
let autoXoffTout |
||||
let reconTout |
||||
|
||||
var pageShown = false; |
||||
let pageShown = false |
||||
|
||||
function onOpen(evt) { |
||||
console.log("CONNECTED"); |
||||
doSend("i"); |
||||
function onOpen (evt) { |
||||
console.log('CONNECTED') |
||||
heartbeat() |
||||
doSend('i') |
||||
} |
||||
|
||||
function onClose(evt) { |
||||
console.warn("SOCKET CLOSED, code " + evt.code + ". Reconnecting..."); |
||||
clearTimeout(reconTout); |
||||
function onClose (evt) { |
||||
console.warn('SOCKET CLOSED, code ' + evt.code + '. Reconnecting...') |
||||
clearTimeout(reconTout) |
||||
reconTout = setTimeout(function () { |
||||
init(); |
||||
}, 2000); |
||||
init() |
||||
}, 2000) |
||||
// this happens when the buffer gets fucked up via invalid unicode.
|
||||
// we basically use polling instead of socket then
|
||||
} |
||||
|
||||
function onMessage(evt) { |
||||
function onMessage (evt) { |
||||
try { |
||||
// . = heartbeat
|
||||
switch (evt.data.charAt(0)) { |
||||
case 'B': |
||||
case 'T': |
||||
case 'S': |
||||
Screen.load(evt.data); |
||||
if(!pageShown) { |
||||
showPage(); |
||||
pageShown = true; |
||||
} |
||||
break; |
||||
case '.': |
||||
// heartbeat, no-op message
|
||||
break |
||||
|
||||
case '-': |
||||
//console.log('xoff');
|
||||
xoff = true; |
||||
// console.log('xoff');
|
||||
xoff = true |
||||
autoXoffTout = setTimeout(function () { |
||||
xoff = false; |
||||
}, 250); |
||||
break; |
||||
xoff = false |
||||
}, 250) |
||||
break |
||||
|
||||
case '+': |
||||
//console.log('xon');
|
||||
xoff = false; |
||||
clearTimeout(autoXoffTout); |
||||
break; |
||||
// console.log('xon');
|
||||
xoff = false |
||||
clearTimeout(autoXoffTout) |
||||
break |
||||
|
||||
default: |
||||
Screen.load(evt.data) |
||||
if (!pageShown) { |
||||
showPage() |
||||
pageShown = true |
||||
} |
||||
heartbeat(); |
||||
break |
||||
} |
||||
heartbeat() |
||||
} catch (e) { |
||||
console.error(e); |
||||
console.error(e) |
||||
} |
||||
} |
||||
|
||||
function canSend() { |
||||
return !xoff; |
||||
function canSend () { |
||||
return !xoff |
||||
} |
||||
|
||||
function doSend(message) { |
||||
function doSend (message) { |
||||
if (_demo) { |
||||
console.log("TX: ", message); |
||||
return true; // Simulate success
|
||||
console.log('TX: ', message) |
||||
return true // Simulate success
|
||||
} |
||||
if (xoff) { |
||||
// TODO queue
|
||||
console.log("Can't send, flood control."); |
||||
return false; |
||||
console.log("Can't send, flood control.") |
||||
return false |
||||
} |
||||
|
||||
if (!ws) return false; // for dry testing
|
||||
if (ws.readyState != 1) { |
||||
console.error("Socket not ready"); |
||||
return false; |
||||
if (!ws) return false // for dry testing
|
||||
if (ws.readyState !== 1) { |
||||
console.error('Socket not ready') |
||||
return false |
||||
} |
||||
if (typeof message != "string") { |
||||
message = JSON.stringify(message); |
||||
if (typeof message != 'string') { |
||||
message = JSON.stringify(message) |
||||
} |
||||
ws.send(message); |
||||
return true; |
||||
ws.send(message) |
||||
return true |
||||
} |
||||
|
||||
function init() { |
||||
function init () { |
||||
if (_demo) { |
||||
console.log("Demo mode!"); |
||||
Screen.load(_demo_screen); |
||||
showPage(); |
||||
return; |
||||
console.log('Demo mode!') |
||||
Screen.load(_demo_screen) |
||||
showPage() |
||||
return |
||||
} |
||||
|
||||
clearTimeout(reconTout); |
||||
clearTimeout(heartbeatTout); |
||||
clearTimeout(reconTout) |
||||
clearTimeout(heartbeatTout) |
||||
|
||||
ws = new WebSocket("ws://" + _root + "/term/update.ws"); |
||||
ws.onopen = onOpen; |
||||
ws.onclose = onClose; |
||||
ws.onmessage = onMessage; |
||||
console.log("Opening socket."); |
||||
heartbeat(); |
||||
ws = new WebSocket('ws://' + _root + '/term/update.ws') |
||||
ws.onopen = onOpen |
||||
ws.onclose = onClose |
||||
ws.onmessage = onMessage |
||||
console.log('Opening socket.') |
||||
heartbeat() |
||||
} |
||||
|
||||
function heartbeat() { |
||||
clearTimeout(heartbeatTout); |
||||
heartbeatTout = setTimeout(heartbeatFail, 2000); |
||||
function heartbeat () { |
||||
clearTimeout(heartbeatTout) |
||||
heartbeatTout = setTimeout(heartbeatFail, 2000) |
||||
} |
||||
|
||||
function heartbeatFail() { |
||||
console.error("Heartbeat lost, probing server..."); |
||||
function heartbeatFail () { |
||||
console.error('Heartbeat lost, probing server...') |
||||
pingIv = setInterval(function () { |
||||
console.log("> ping"); |
||||
console.log('> ping') |
||||
$.get('http://' + _root + '/system/ping', function (resp, status) { |
||||
if (status == 200) { |
||||
clearInterval(pingIv); |
||||
console.info("Server ready, reloading page..."); |
||||
location.reload(); |
||||
if (status === 200) { |
||||
clearInterval(pingIv) |
||||
console.info('Server ready, reloading page...') |
||||
location.reload() |
||||
} |
||||
}, { |
||||
timeout: 100, |
||||
}); |
||||
}, 1000); |
||||
timeout: 100 |
||||
}) |
||||
}, 1000) |
||||
} |
||||
|
||||
return { |
||||
ws: null, |
||||
init: init, |
||||
send: doSend, |
||||
canSend: canSend, // check flood control
|
||||
}; |
||||
})(); |
||||
canSend: canSend // check flood control
|
||||
} |
||||
})() |
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,146 +1,146 @@ |
||||
/** File upload utility */ |
||||
var TermUpl = (function() { |
||||
var lines, // array of lines without newlines
|
||||
window.TermUpl = (function () { |
||||
let lines, // array of lines without newlines
|
||||
line_i, // current line index
|
||||
fuTout, // timeout handle for line sending
|
||||
send_delay_ms, // delay between lines (ms)
|
||||
nl_str, // newline string to use
|
||||
curLine, // current line (when using fuOil)
|
||||
inline_pos; // Offset in line (for long lines)
|
||||
inline_pos // Offset in line (for long lines)
|
||||
|
||||
// lines longer than this are split to chunks
|
||||
// sending a super-ling string through the socket is not a good idea
|
||||
var MAX_LINE_LEN = 128; |
||||
const MAX_LINE_LEN = 128 |
||||
|
||||
function fuOpen() { |
||||
fuStatus("Ready..."); |
||||
Modal.show('#fu_modal', onClose); |
||||
$('#fu_form').toggleClass('busy', false); |
||||
Input.blockKeys(true); |
||||
function fuOpen () { |
||||
fuStatus('Ready...') |
||||
Modal.show('#fu_modal', onClose) |
||||
$('#fu_form').toggleClass('busy', false) |
||||
Input.blockKeys(true) |
||||
} |
||||
|
||||
function onClose() { |
||||
console.log("Upload modal closed."); |
||||
clearTimeout(fuTout); |
||||
line_i = 0; |
||||
Input.blockKeys(false); |
||||
function onClose () { |
||||
console.log('Upload modal closed.') |
||||
clearTimeout(fuTout) |
||||
line_i = 0 |
||||
Input.blockKeys(false) |
||||
} |
||||
|
||||
function fuStatus(msg) { |
||||
qs('#fu_prog').textContent = msg; |
||||
function fuStatus (msg) { |
||||
qs('#fu_prog').textContent = msg |
||||
} |
||||
|
||||
function fuSend() { |
||||
var v = qs('#fu_text').value; |
||||
function fuSend () { |
||||
let v = qs('#fu_text').value |
||||
if (!v.length) { |
||||
fuClose(); |
||||
return; |
||||
fuClose() |
||||
return |
||||
} |
||||
|
||||
lines = v.split('\n'); |
||||
line_i = 0; |
||||
inline_pos = 0; // offset in line
|
||||
send_delay_ms = qs('#fu_delay').value; |
||||
lines = v.split('\n') |
||||
line_i = 0 |
||||
inline_pos = 0 // offset in line
|
||||
send_delay_ms = qs('#fu_delay').value |
||||
|
||||
// sanitize - 0 causes overflows
|
||||
if (send_delay_ms < 0) { |
||||
send_delay_ms = 0; |
||||
qs('#fu_delay').value = send_delay_ms; |
||||
send_delay_ms = 0 |
||||
qs('#fu_delay').value = send_delay_ms |
||||
} |
||||
|
||||
nl_str = { |
||||
'CR': '\r', |
||||
'LF': '\n', |
||||
'CRLF': '\r\n', |
||||
}[qs('#fu_crlf').value]; |
||||
'CRLF': '\r\n' |
||||
}[qs('#fu_crlf').value] |
||||
|
||||
$('#fu_form').toggleClass('busy', true); |
||||
fuStatus("Starting..."); |
||||
fuSendLine(); |
||||
$('#fu_form').toggleClass('busy', true) |
||||
fuStatus('Starting...') |
||||
fuSendLine() |
||||
} |
||||
|
||||
function fuSendLine() { |
||||
function fuSendLine () { |
||||
if (!$('#fu_modal').hasClass('visible')) { |
||||
// Modal is closed, cancel
|
||||
return; |
||||
return |
||||
} |
||||
|
||||
if (!Conn.canSend()) { |
||||
// postpone
|
||||
fuTout = setTimeout(fuSendLine, 1); |
||||
return; |
||||
fuTout = setTimeout(fuSendLine, 1) |
||||
return |
||||
} |
||||
|
||||
if (inline_pos == 0) { |
||||
curLine = lines[line_i++] + nl_str; |
||||
if (inline_pos === 0) { |
||||
curLine = lines[line_i++] + nl_str |
||||
} |
||||
|
||||
var chunk; |
||||
let chunk |
||||
if ((curLine.length - inline_pos) <= MAX_LINE_LEN) { |
||||
chunk = curLine.substr(inline_pos, MAX_LINE_LEN); |
||||
inline_pos = 0; |
||||
chunk = curLine.substr(inline_pos, MAX_LINE_LEN) |
||||
inline_pos = 0 |
||||
} else { |
||||
chunk = curLine.substr(inline_pos, MAX_LINE_LEN); |
||||
inline_pos += MAX_LINE_LEN; |
||||
chunk = curLine.substr(inline_pos, MAX_LINE_LEN) |
||||
inline_pos += MAX_LINE_LEN |
||||
} |
||||
|
||||
if (!Input.sendString(chunk)) { |
||||
fuStatus("FAILED!"); |
||||
return; |
||||
fuStatus('FAILED!') |
||||
return |
||||
} |
||||
|
||||
var all = lines.length; |
||||
let all = lines.length |
||||
|
||||
fuStatus(line_i+" / "+all+ " ("+(Math.round((line_i/all)*1000)/10)+"%)"); |
||||
fuStatus(line_i + ' / ' + all + ' (' + (Math.round((line_i / all) * 1000) / 10) + '%)') |
||||
|
||||
if (lines.length > line_i || inline_pos > 0) { |
||||
fuTout = setTimeout(fuSendLine, send_delay_ms); |
||||
fuTout = setTimeout(fuSendLine, send_delay_ms) |
||||
} else { |
||||
closeWhenReady(); |
||||
closeWhenReady() |
||||
} |
||||
} |
||||
|
||||
function closeWhenReady() { |
||||
function closeWhenReady () { |
||||
if (!Conn.canSend()) { |
||||
// stuck in XOFF still, wait to process...
|
||||
fuStatus("Waiting for Tx buffer..."); |
||||
setTimeout(closeWhenReady, 100); |
||||
fuStatus('Waiting for Tx buffer...') |
||||
setTimeout(closeWhenReady, 100) |
||||
} else { |
||||
fuStatus("Done."); |
||||
fuStatus('Done.') |
||||
// delay to show it
|
||||
setTimeout(function() { |
||||
fuClose(); |
||||
}, 100); |
||||
setTimeout(function () { |
||||
fuClose() |
||||
}, 100) |
||||
} |
||||
} |
||||
|
||||
function fuClose() { |
||||
Modal.hide('#fu_modal'); |
||||
function fuClose () { |
||||
Modal.hide('#fu_modal') |
||||
} |
||||
|
||||
return { |
||||
init: function() { |
||||
init: function () { |
||||
qs('#fu_file').addEventListener('change', function (evt) { |
||||
var reader = new FileReader(); |
||||
var file = evt.target.files[0]; |
||||
console.log("Selected file type: "+file.type); |
||||
let reader = new FileReader() |
||||
let file = evt.target.files[0] |
||||
console.log('Selected file type: ' + file.type) |
||||
if (!file.type.match(/text\/.*|application\/(json|csv|.*xml.*|.*script.*)/)) { |
||||
// Deny load of blobs like img - can crash browser and will get corrupted anyway
|
||||
if (!confirm("This does not look like a text file: "+file.type+"\nReally load?")) { |
||||
qs('#fu_file').value = ''; |
||||
return; |
||||
} |
||||
} |
||||
reader.onload = function(e) { |
||||
var txt = e.target.result.replace(/[\r\n]+/,'\n'); |
||||
qs('#fu_text').value = txt; |
||||
}; |
||||
console.log("Loading file..."); |
||||
reader.readAsText(file); |
||||
}, false); |
||||
if (!confirm('This does not look like a text file: ' + file.type + '\nReally load?')) { |
||||
qs('#fu_file').value = '' |
||||
return |
||||
} |
||||
} |
||||
reader.onload = function (e) { |
||||
const txt = e.target.result.replace(/[\r\n]+/, '\n') |
||||
qs('#fu_text').value = txt |
||||
} |
||||
console.log('Loading file...') |
||||
reader.readAsText(file) |
||||
}, false) |
||||
}, |
||||
close: fuClose, |
||||
start: fuSend, |
||||
open: fuOpen, |
||||
open: fuOpen |
||||
} |
||||
})(); |
||||
})() |
||||
|
@ -0,0 +1,18 @@ |
||||
{ |
||||
"name": "espterm-front-end", |
||||
"version": "1.0.0", |
||||
"description": "ESPTerm web interface", |
||||
"license": "MPL-2.0", |
||||
"devDependencies": { |
||||
"babel-cli": "^6.26.0", |
||||
"babel-minify": "^0.2.0", |
||||
"babel-preset-env": "^1.6.0", |
||||
"node-sass": "^4.5.3", |
||||
"standard": "^10.0.3" |
||||
}, |
||||
"scripts": { |
||||
"babel": "babel $@", |
||||
"minify": "babel-minify $@", |
||||
"sass": "node-sass $@" |
||||
} |
||||
} |
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue