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. |
@ -1,25 +1,29 @@ |
|||||||
#!/bin/bash |
#!/bin/bash |
||||||
|
|
||||||
echo "Packing JS..." |
echo 'Packing JS...' |
||||||
|
npm run babel -- -o js/app.js --source-maps jssrc/lib \ |
||||||
|
jssrc/lib/chibi.js \ |
||||||
|
jssrc/lib/keymaster.js \ |
||||||
|
jssrc/lib/polyfills.js \ |
||||||
|
jssrc/utils.js \ |
||||||
|
jssrc/modal.js \ |
||||||
|
jssrc/notif.js \ |
||||||
|
jssrc/appcommon.js \ |
||||||
|
jssrc/lang.js \ |
||||||
|
jssrc/wifi.js \ |
||||||
|
jssrc/term_* \ |
||||||
|
jssrc/debug_screen.js \ |
||||||
|
jssrc/soft_keyboard.js \ |
||||||
|
jssrc/term.js |
||||||
|
|
||||||
cat jssrc/chibi.js \ |
echo 'Building CSS...' |
||||||
jssrc/keymaster.js \ |
|
||||||
jssrc/utils.js \ |
|
||||||
jssrc/modal.js \ |
|
||||||
jssrc/notif.js \ |
|
||||||
jssrc/appcommon.js \ |
|
||||||
jssrc/lang.js \ |
|
||||||
jssrc/wifi.js \ |
|
||||||
jssrc/term_* \ |
|
||||||
jssrc/term.js > js/app-full.js |
|
||||||
|
|
||||||
yuicompressor js/app-full.js > js/app.js |
npm run sass -- --output-style compressed sass/app.scss css/app.css |
||||||
|
|
||||||
echo "Building CSS..." |
echo 'Building HTML...' |
||||||
|
|
||||||
sass --style=compressed sass/app.scss css/app.css |
rm out/* |
||||||
|
php ./dump_js_lang.php |
||||||
|
php ./compile_html.php |
||||||
|
|
||||||
echo "Building HTML..." |
echo 'ESPTerm front-end ready' |
||||||
php ./build_html.php |
|
||||||
|
|
||||||
echo "ESPTerm front-end ready" |
|
||||||
|
Binary file not shown.
@ -1,189 +1,130 @@ |
|||||||
/** Global generic init */ |
/** Global generic init */ |
||||||
$.ready(function () { |
$.ready(function () { |
||||||
// Checkbox UI (checkbox CSS and hidden input with int value)
|
// Checkbox UI (checkbox CSS and hidden input with int value)
|
||||||
$('.Row.checkbox').forEach(function(x) { |
$('.Row.checkbox').forEach(function (x) { |
||||||
var inp = x.querySelector('input'); |
let inp = x.querySelector('input') |
||||||
var box = x.querySelector('.box'); |
let box = x.querySelector('.box') |
||||||
|
|
||||||
$(box).toggleClass('checked', inp.value); |
$(box).toggleClass('checked', inp.value) |
||||||
|
|
||||||
var hdl = function() { |
let hdl = function () { |
||||||
inp.value = 1 - inp.value; |
inp.value = 1 - inp.value |
||||||
$(box).toggleClass('checked', 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
|
// Expanding boxes on mobile
|
||||||
$('.Box.mobcol,.Box.fold').forEach(function(x) { |
$('.Box.mobcol,.Box.fold').forEach(function (x) { |
||||||
var h = x.querySelector('h2'); |
let h = x.querySelector('h2') |
||||||
|
|
||||||
var hdl = function() { |
let hdl = function () { |
||||||
$(x).toggleClass('expanded'); |
$(x).toggleClass('expanded') |
||||||
}; |
} |
||||||
$(h).on('click', hdl).on('keypress', cr(hdl)); |
$(h).on('click', hdl).on('keypress', cr(hdl)) |
||||||
}); |
}) |
||||||
|
|
||||||
$('form').forEach(function(x) { |
$('form').forEach(function (x) { |
||||||
$(x).on('keypress', function(e) { |
$(x).on('keypress', function (e) { |
||||||
if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) { |
if ((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey) { |
||||||
x.submit(); |
x.submit() |
||||||
} |
} |
||||||
}) |
}) |
||||||
}); |
}) |
||||||
|
|
||||||
// loader dots...
|
// loader dots...
|
||||||
setInterval(function () { |
setInterval(function () { |
||||||
$('.anim-dots').each(function (x) { |
$('.anim-dots').each(function (x) { |
||||||
var $x = $(x); |
let $x = $(x) |
||||||
var dots = $x.html() + '.'; |
let dots = $x.html() + '.' |
||||||
if (dots.length == 5) dots = '.'; |
if (dots.length === 5) dots = '.' |
||||||
$x.html(dots); |
$x.html(dots) |
||||||
}); |
}) |
||||||
}, 1000); |
}, 1000) |
||||||
|
|
||||||
// flipping number boxes with the mouse wheel
|
// flipping number boxes with the mouse wheel
|
||||||
$('input[type=number]').on('mousewheel', function(e) { |
$('input[type=number]').on('mousewheel', function (e) { |
||||||
var $this = $(this); |
let $this = $(this) |
||||||
var val = +$this.val(); |
let val = +$this.val() |
||||||
if (isNaN(val)) val = 1; |
if (isNaN(val)) val = 1 |
||||||
|
|
||||||
var step = +($this.attr('step') || 1); |
const step = +($this.attr('step') || 1) |
||||||
var min = +$this.attr('min'); |
const min = +$this.attr('min') |
||||||
var max = +$this.attr('max'); |
const max = +$this.attr('max') |
||||||
if(e.wheelDelta > 0) { |
if (e.wheelDelta > 0) { |
||||||
val += step; |
val += step |
||||||
} else { |
} else { |
||||||
val -= step; |
val -= step |
||||||
} |
} |
||||||
|
|
||||||
if (typeof min != 'undefined') val = Math.max(val, +min); |
if (undef(min)) val = Math.max(val, +min) |
||||||
if (typeof max != 'undefined') val = Math.min(val, +max); |
if (undef(max)) val = Math.min(val, +max) |
||||||
$this.val(val); |
$this.val(val) |
||||||
|
|
||||||
if ("createEvent" in document) { |
if ('createEvent' in document) { |
||||||
var evt = document.createEvent("HTMLEvents"); |
let evt = document.createEvent('HTMLEvents') |
||||||
evt.initEvent("change", false, true); |
evt.initEvent('change', false, true) |
||||||
$this[0].dispatchEvent(evt); |
$this[0].dispatchEvent(evt) |
||||||
} else { |
} 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=...
|
||||||
if (errAt !== -1 && qs('.Box.errors')) { |
// (a way to pass errors back from server via redirect)
|
||||||
var errs = location.search.substr(errAt+4).split(','); |
let errAt = location.search.indexOf('err=') |
||||||
var hres = []; |
if (errAt !== -1 && qs('.Box.errors')) { |
||||||
errs.forEach(function(er) { |
let errs = location.search.substr(errAt + 4).split(',') |
||||||
var lbl = qs('label[for="'+er+'"]'); |
let hres = [] |
||||||
if (lbl) { |
errs.forEach(function (er) { |
||||||
lbl.classList.add('error'); |
let lbl = qs('label[for="' + er + '"]') |
||||||
hres.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, '')); |
if (lbl) { |
||||||
} else { |
lbl.classList.add('error') |
||||||
hres.push(er); |
hres.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, '')) |
||||||
} |
} else { |
||||||
}); |
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() |
||||||
// remove tabindixes from h2 if wide
|
Notify.init() |
||||||
if (window.innerWidth > 550) { |
|
||||||
$('.Box h2').forEach(function (x) { |
// remove tabindixes from h2 if wide
|
||||||
x.removeAttribute('tabindex'); |
if (window.innerWidth > 550) { |
||||||
}); |
$('.Box h2').forEach(function (x) { |
||||||
|
x.removeAttribute('tabindex') |
||||||
// brand works as a link back to term in widescreen mode
|
}) |
||||||
var br = qs('#brand'); |
|
||||||
br && br.addEventListener('click', function() { |
// brand works as a link back to term in widescreen mode
|
||||||
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) { |
||||||
function showPage() { |
$('#loader').toggleClass('show', vis) |
||||||
$('#content').addClass('load'); |
|
||||||
} |
} |
||||||
|
|
||||||
$.ready(function() { |
// reveal content on load
|
||||||
if (window.noAutoShow !== true) { |
function showPage () { |
||||||
setTimeout(function () { |
$('#content').addClass('load') |
||||||
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); |
|
||||||
} |
|
||||||
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; |
|
||||||
} |
|
||||||
}()); |
|
||||||
} |
} |
||||||
|
|
||||||
|
// Auto reveal pages other than the terminal (sets window.noAutoShow)
|
||||||
|
$.ready(function () { |
||||||
|
if (window.noAutoShow !== true) { |
||||||
|
setTimeout(function () { |
||||||
|
showPage() |
||||||
|
}, 1) |
||||||
|
} |
||||||
|
}) |
||||||
|
@ -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
|
// Generated from PHP locale file
|
||||||
var _tr = { |
let _tr = { |
||||||
"wifi.connected_ip_is": "Connected, IP is ", |
"wifi.connected_ip_is": "Connected, IP is ", |
||||||
"wifi.not_conn": "Not connected.", |
"wifi.not_conn": "Not connected.", |
||||||
"wifi.enter_passwd": "Enter password for \":ssid:\"" |
"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 */ |
/** Module for toggling a modal overlay */ |
||||||
(function () { |
(function () { |
||||||
var modal = {}; |
let modal = {} |
||||||
var curCloseCb = null; |
let curCloseCb = null |
||||||
|
|
||||||
modal.show = function (sel, closeCb) { |
modal.show = function (sel, closeCb) { |
||||||
var $m = $(sel); |
let $m = $(sel) |
||||||
$m.removeClass('hidden visible'); |
$m.removeClass('hidden visible') |
||||||
setTimeout(function () { |
setTimeout(function () { |
||||||
$m.addClass('visible'); |
$m.addClass('visible') |
||||||
}, 1); |
}, 1) |
||||||
curCloseCb = closeCb; |
curCloseCb = closeCb |
||||||
}; |
} |
||||||
|
|
||||||
modal.hide = function (sel) { |
modal.hide = function (sel) { |
||||||
var $m = $(sel); |
let $m = $(sel) |
||||||
$m.removeClass('visible'); |
$m.removeClass('visible') |
||||||
setTimeout(function () { |
setTimeout(function () { |
||||||
$m.addClass('hidden'); |
$m.addClass('hidden') |
||||||
if (curCloseCb) curCloseCb(); |
if (curCloseCb) curCloseCb() |
||||||
}, 500); // transition time
|
}, 500) // transition time
|
||||||
}; |
} |
||||||
|
|
||||||
modal.init = function () { |
modal.init = function () { |
||||||
// close modal by click outside the dialog
|
// close modal by click outside the dialog
|
||||||
$('.Modal').on('click', function () { |
$('.Modal').on('click', function () { |
||||||
if ($(this).hasClass('no-close')) return; // this is a no-close modal
|
if ($(this).hasClass('no-close')) return // this is a no-close modal
|
||||||
modal.hide(this); |
modal.hide(this) |
||||||
}); |
}) |
||||||
|
|
||||||
$('.Dialog').on('click', function (e) { |
$('.Dialog').on('click', function (e) { |
||||||
e.stopImmediatePropagation(); |
e.stopImmediatePropagation() |
||||||
}); |
}) |
||||||
|
|
||||||
// Hide all modals on esc
|
// Hide all modals on esc
|
||||||
$(window).on('keydown', function (e) { |
$(window).on('keydown', function (e) { |
||||||
if (e.which == 27) { |
if (e.which === 27) { |
||||||
modal.hide('.Modal'); |
modal.hide('.Modal') |
||||||
} |
} |
||||||
}); |
}) |
||||||
}; |
} |
||||||
|
|
||||||
window.Modal = modal; |
window.Modal = modal |
||||||
})(); |
})() |
||||||
|
@ -1,32 +1,35 @@ |
|||||||
(function (nt) { |
window.Notify = (function () { |
||||||
var sel = '#notif'; |
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); |
nt.show = function (message, timeout) { |
||||||
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); |
nt.hide = function () { |
||||||
$m.removeClass('visible'); |
let $m = $(sel) |
||||||
hideTmeo2 = setTimeout(function () { |
$m.removeClass('visible') |
||||||
$m.addClass('hidden'); |
hideTmeo2 = setTimeout(function () { |
||||||
}, 250); // transition time
|
$m.addClass('hidden') |
||||||
}; |
}, 250) // transition time
|
||||||
|
} |
||||||
nt.init = function() { |
|
||||||
$(sel).on('click', function() { |
nt.init = function () { |
||||||
nt.hide(this); |
$(sel).on('click', function () { |
||||||
}); |
nt.hide(this) |
||||||
}; |
}) |
||||||
})(window.Notify = {}); |
} |
||||||
|
|
||||||
|
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 */ |
/** Init the terminal sub-module - called from HTML */ |
||||||
window.termInit = function () { |
window.termInit = function (labels, theme) { |
||||||
Conn.init(); |
Conn.init() |
||||||
Input.init(); |
Input.init() |
||||||
TermUpl.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 */ |
/** Handle connections */ |
||||||
var Conn = (function () { |
window.Conn = (function () { |
||||||
var ws; |
let ws |
||||||
var heartbeatTout; |
let heartbeatTout |
||||||
var pingIv; |
let pingIv |
||||||
var xoff = false; |
let xoff = false |
||||||
var autoXoffTout; |
let autoXoffTout |
||||||
var reconTout; |
let reconTout |
||||||
|
|
||||||
var pageShown = false; |
let pageShown = false |
||||||
|
|
||||||
function onOpen(evt) { |
function onOpen (evt) { |
||||||
console.log("CONNECTED"); |
console.log('CONNECTED') |
||||||
doSend("i"); |
heartbeat() |
||||||
|
doSend('i') |
||||||
} |
} |
||||||
|
|
||||||
function onClose(evt) { |
function onClose (evt) { |
||||||
console.warn("SOCKET CLOSED, code " + evt.code + ". Reconnecting..."); |
console.warn('SOCKET CLOSED, code ' + evt.code + '. Reconnecting...') |
||||||
clearTimeout(reconTout); |
clearTimeout(reconTout) |
||||||
reconTout = setTimeout(function () { |
reconTout = setTimeout(function () { |
||||||
init(); |
init() |
||||||
}, 2000); |
}, 2000) |
||||||
// this happens when the buffer gets fucked up via invalid unicode.
|
// this happens when the buffer gets fucked up via invalid unicode.
|
||||||
// we basically use polling instead of socket then
|
// we basically use polling instead of socket then
|
||||||
} |
} |
||||||
|
|
||||||
function onMessage(evt) { |
function onMessage (evt) { |
||||||
try { |
try { |
||||||
// . = heartbeat
|
// . = heartbeat
|
||||||
switch (evt.data.charAt(0)) { |
switch (evt.data.charAt(0)) { |
||||||
case 'B': |
case '.': |
||||||
case 'T': |
// heartbeat, no-op message
|
||||||
case 'S': |
break |
||||||
Screen.load(evt.data); |
|
||||||
if(!pageShown) { |
|
||||||
showPage(); |
|
||||||
pageShown = true; |
|
||||||
} |
|
||||||
break; |
|
||||||
|
|
||||||
case '-': |
case '-': |
||||||
//console.log('xoff');
|
// console.log('xoff');
|
||||||
xoff = true; |
xoff = true |
||||||
autoXoffTout = setTimeout(function () { |
autoXoffTout = setTimeout(function () { |
||||||
xoff = false; |
xoff = false |
||||||
}, 250); |
}, 250) |
||||||
break; |
break |
||||||
|
|
||||||
case '+': |
case '+': |
||||||
//console.log('xon');
|
// console.log('xon');
|
||||||
xoff = false; |
xoff = false |
||||||
clearTimeout(autoXoffTout); |
clearTimeout(autoXoffTout) |
||||||
break; |
break |
||||||
|
|
||||||
|
default: |
||||||
|
Screen.load(evt.data) |
||||||
|
if (!pageShown) { |
||||||
|
showPage() |
||||||
|
pageShown = true |
||||||
|
} |
||||||
|
break |
||||||
} |
} |
||||||
heartbeat(); |
heartbeat() |
||||||
} catch (e) { |
} catch (e) { |
||||||
console.error(e); |
console.error(e) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
function canSend() { |
function canSend () { |
||||||
return !xoff; |
return !xoff |
||||||
} |
} |
||||||
|
|
||||||
function doSend(message) { |
function doSend (message) { |
||||||
if (_demo) { |
if (_demo) { |
||||||
console.log("TX: ", message); |
console.log('TX: ', message) |
||||||
return true; // Simulate success
|
return true // Simulate success
|
||||||
} |
} |
||||||
if (xoff) { |
if (xoff) { |
||||||
// TODO queue
|
// TODO queue
|
||||||
console.log("Can't send, flood control."); |
console.log("Can't send, flood control.") |
||||||
return false; |
return false |
||||||
} |
} |
||||||
|
|
||||||
if (!ws) return false; // for dry testing
|
if (!ws) return false // for dry testing
|
||||||
if (ws.readyState != 1) { |
if (ws.readyState !== 1) { |
||||||
console.error("Socket not ready"); |
console.error('Socket not ready') |
||||||
return false; |
return false |
||||||
} |
} |
||||||
if (typeof message != "string") { |
if (typeof message != 'string') { |
||||||
message = JSON.stringify(message); |
message = JSON.stringify(message) |
||||||
} |
} |
||||||
ws.send(message); |
ws.send(message) |
||||||
return true; |
return true |
||||||
} |
} |
||||||
|
|
||||||
function init() { |
function init () { |
||||||
if (_demo) { |
if (_demo) { |
||||||
console.log("Demo mode!"); |
console.log('Demo mode!') |
||||||
Screen.load(_demo_screen); |
Screen.load(_demo_screen) |
||||||
showPage(); |
showPage() |
||||||
return; |
return |
||||||
} |
} |
||||||
|
|
||||||
clearTimeout(reconTout); |
clearTimeout(reconTout) |
||||||
clearTimeout(heartbeatTout); |
clearTimeout(heartbeatTout) |
||||||
|
|
||||||
ws = new WebSocket("ws://" + _root + "/term/update.ws"); |
ws = new WebSocket('ws://' + _root + '/term/update.ws') |
||||||
ws.onopen = onOpen; |
ws.onopen = onOpen |
||||||
ws.onclose = onClose; |
ws.onclose = onClose |
||||||
ws.onmessage = onMessage; |
ws.onmessage = onMessage |
||||||
console.log("Opening socket."); |
console.log('Opening socket.') |
||||||
heartbeat(); |
heartbeat() |
||||||
} |
} |
||||||
|
|
||||||
function heartbeat() { |
function heartbeat () { |
||||||
clearTimeout(heartbeatTout); |
clearTimeout(heartbeatTout) |
||||||
heartbeatTout = setTimeout(heartbeatFail, 2000); |
heartbeatTout = setTimeout(heartbeatFail, 2000) |
||||||
} |
} |
||||||
|
|
||||||
function heartbeatFail() { |
function heartbeatFail () { |
||||||
console.error("Heartbeat lost, probing server..."); |
console.error('Heartbeat lost, probing server...') |
||||||
pingIv = setInterval(function () { |
pingIv = setInterval(function () { |
||||||
console.log("> ping"); |
console.log('> ping') |
||||||
$.get('http://' + _root + '/system/ping', function (resp, status) { |
$.get('http://' + _root + '/system/ping', function (resp, status) { |
||||||
if (status == 200) { |
if (status === 200) { |
||||||
clearInterval(pingIv); |
clearInterval(pingIv) |
||||||
console.info("Server ready, reloading page..."); |
console.info('Server ready, reloading page...') |
||||||
location.reload(); |
location.reload() |
||||||
} |
} |
||||||
}, { |
}, { |
||||||
timeout: 100, |
timeout: 100 |
||||||
}); |
}) |
||||||
}, 1000); |
}, 1000) |
||||||
} |
} |
||||||
|
|
||||||
return { |
return { |
||||||
ws: null, |
ws: null, |
||||||
init: init, |
init: init, |
||||||
send: doSend, |
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 */ |
/** File upload utility */ |
||||||
var TermUpl = (function() { |
window.TermUpl = (function () { |
||||||
var lines, // array of lines without newlines
|
let lines, // array of lines without newlines
|
||||||
line_i, // current line index
|
line_i, // current line index
|
||||||
fuTout, // timeout handle for line sending
|
fuTout, // timeout handle for line sending
|
||||||
send_delay_ms, // delay between lines (ms)
|
send_delay_ms, // delay between lines (ms)
|
||||||
nl_str, // newline string to use
|
nl_str, // newline string to use
|
||||||
curLine, // current line (when using fuOil)
|
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
|
// lines longer than this are split to chunks
|
||||||
// sending a super-ling string through the socket is not a good idea
|
// 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() { |
function fuOpen () { |
||||||
fuStatus("Ready..."); |
fuStatus('Ready...') |
||||||
Modal.show('#fu_modal', onClose); |
Modal.show('#fu_modal', onClose) |
||||||
$('#fu_form').toggleClass('busy', false); |
$('#fu_form').toggleClass('busy', false) |
||||||
Input.blockKeys(true); |
Input.blockKeys(true) |
||||||
} |
} |
||||||
|
|
||||||
function onClose() { |
function onClose () { |
||||||
console.log("Upload modal closed."); |
console.log('Upload modal closed.') |
||||||
clearTimeout(fuTout); |
clearTimeout(fuTout) |
||||||
line_i = 0; |
line_i = 0 |
||||||
Input.blockKeys(false); |
Input.blockKeys(false) |
||||||
} |
} |
||||||
|
|
||||||
function fuStatus(msg) { |
function fuStatus (msg) { |
||||||
qs('#fu_prog').textContent = msg; |
qs('#fu_prog').textContent = msg |
||||||
} |
} |
||||||
|
|
||||||
function fuSend() { |
function fuSend () { |
||||||
var v = qs('#fu_text').value; |
let v = qs('#fu_text').value |
||||||
if (!v.length) { |
if (!v.length) { |
||||||
fuClose(); |
fuClose() |
||||||
return; |
return |
||||||
} |
} |
||||||
|
|
||||||
lines = v.split('\n'); |
lines = v.split('\n') |
||||||
line_i = 0; |
line_i = 0 |
||||||
inline_pos = 0; // offset in line
|
inline_pos = 0 // offset in line
|
||||||
send_delay_ms = qs('#fu_delay').value; |
send_delay_ms = qs('#fu_delay').value |
||||||
|
|
||||||
// sanitize - 0 causes overflows
|
// sanitize - 0 causes overflows
|
||||||
if (send_delay_ms < 0) { |
if (send_delay_ms < 0) { |
||||||
send_delay_ms = 0; |
send_delay_ms = 0 |
||||||
qs('#fu_delay').value = send_delay_ms; |
qs('#fu_delay').value = send_delay_ms |
||||||
} |
} |
||||||
|
|
||||||
nl_str = { |
nl_str = { |
||||||
'CR': '\r', |
'CR': '\r', |
||||||
'LF': '\n', |
'LF': '\n', |
||||||
'CRLF': '\r\n', |
'CRLF': '\r\n' |
||||||
}[qs('#fu_crlf').value]; |
}[qs('#fu_crlf').value] |
||||||
|
|
||||||
$('#fu_form').toggleClass('busy', true); |
$('#fu_form').toggleClass('busy', true) |
||||||
fuStatus("Starting..."); |
fuStatus('Starting...') |
||||||
fuSendLine(); |
fuSendLine() |
||||||
} |
} |
||||||
|
|
||||||
function fuSendLine() { |
function fuSendLine () { |
||||||
if (!$('#fu_modal').hasClass('visible')) { |
if (!$('#fu_modal').hasClass('visible')) { |
||||||
// Modal is closed, cancel
|
// Modal is closed, cancel
|
||||||
return; |
return |
||||||
} |
} |
||||||
|
|
||||||
if (!Conn.canSend()) { |
if (!Conn.canSend()) { |
||||||
// postpone
|
// postpone
|
||||||
fuTout = setTimeout(fuSendLine, 1); |
fuTout = setTimeout(fuSendLine, 1) |
||||||
return; |
return |
||||||
} |
} |
||||||
|
|
||||||
if (inline_pos == 0) { |
if (inline_pos === 0) { |
||||||
curLine = lines[line_i++] + nl_str; |
curLine = lines[line_i++] + nl_str |
||||||
} |
} |
||||||
|
|
||||||
var chunk; |
let chunk |
||||||
if ((curLine.length - inline_pos) <= MAX_LINE_LEN) { |
if ((curLine.length - inline_pos) <= MAX_LINE_LEN) { |
||||||
chunk = curLine.substr(inline_pos, MAX_LINE_LEN); |
chunk = curLine.substr(inline_pos, MAX_LINE_LEN) |
||||||
inline_pos = 0; |
inline_pos = 0 |
||||||
} else { |
} else { |
||||||
chunk = curLine.substr(inline_pos, MAX_LINE_LEN); |
chunk = curLine.substr(inline_pos, MAX_LINE_LEN) |
||||||
inline_pos += MAX_LINE_LEN; |
inline_pos += MAX_LINE_LEN |
||||||
} |
} |
||||||
|
|
||||||
if (!Input.sendString(chunk)) { |
if (!Input.sendString(chunk)) { |
||||||
fuStatus("FAILED!"); |
fuStatus('FAILED!') |
||||||
return; |
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) { |
if (lines.length > line_i || inline_pos > 0) { |
||||||
fuTout = setTimeout(fuSendLine, send_delay_ms); |
fuTout = setTimeout(fuSendLine, send_delay_ms) |
||||||
} else { |
} else { |
||||||
closeWhenReady(); |
closeWhenReady() |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
function closeWhenReady() { |
function closeWhenReady () { |
||||||
if (!Conn.canSend()) { |
if (!Conn.canSend()) { |
||||||
// stuck in XOFF still, wait to process...
|
// stuck in XOFF still, wait to process...
|
||||||
fuStatus("Waiting for Tx buffer..."); |
fuStatus('Waiting for Tx buffer...') |
||||||
setTimeout(closeWhenReady, 100); |
setTimeout(closeWhenReady, 100) |
||||||
} else { |
} else { |
||||||
fuStatus("Done."); |
fuStatus('Done.') |
||||||
// delay to show it
|
// delay to show it
|
||||||
setTimeout(function() { |
setTimeout(function () { |
||||||
fuClose(); |
fuClose() |
||||||
}, 100); |
}, 100) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
function fuClose() { |
function fuClose () { |
||||||
Modal.hide('#fu_modal'); |
Modal.hide('#fu_modal') |
||||||
} |
} |
||||||
|
|
||||||
return { |
return { |
||||||
init: function() { |
init: function () { |
||||||
qs('#fu_file').addEventListener('change', function (evt) { |
qs('#fu_file').addEventListener('change', function (evt) { |
||||||
var reader = new FileReader(); |
let reader = new FileReader() |
||||||
var file = evt.target.files[0]; |
let file = evt.target.files[0] |
||||||
console.log("Selected file type: "+file.type); |
console.log('Selected file type: ' + file.type) |
||||||
if (!file.type.match(/text\/.*|application\/(json|csv|.*xml.*|.*script.*)/)) { |
if (!file.type.match(/text\/.*|application\/(json|csv|.*xml.*|.*script.*)/)) { |
||||||
// Deny load of blobs like img - can crash browser and will get corrupted anyway
|
// 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?")) { |
if (!confirm('This does not look like a text file: ' + file.type + '\nReally load?')) { |
||||||
qs('#fu_file').value = ''; |
qs('#fu_file').value = '' |
||||||
return; |
return |
||||||
} |
} |
||||||
} |
} |
||||||
reader.onload = function(e) { |
reader.onload = function (e) { |
||||||
var txt = e.target.result.replace(/[\r\n]+/,'\n'); |
const txt = e.target.result.replace(/[\r\n]+/, '\n') |
||||||
qs('#fu_text').value = txt; |
qs('#fu_text').value = txt |
||||||
}; |
} |
||||||
console.log("Loading file..."); |
console.log('Loading file...') |
||||||
reader.readAsText(file); |
reader.readAsText(file) |
||||||
}, false); |
}, false) |
||||||
}, |
}, |
||||||
close: fuClose, |
close: fuClose, |
||||||
start: fuSend, |
start: fuSend, |
||||||
open: fuOpen, |
open: fuOpen |
||||||
} |
} |
||||||
})(); |
})() |
||||||
|
@ -1,163 +1,163 @@ |
|||||||
(function(w) { |
(function (w) { |
||||||
var authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2']; |
const authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2'] |
||||||
var curSSID; |
let curSSID |
||||||
|
|
||||||
// Get XX % for a slider input
|
// Get XX % for a slider input
|
||||||
function rangePt(inp) { |
function rangePt (inp) { |
||||||
return Math.round(((inp.value / inp.max)*100)) + '%'; |
return Math.round(((inp.value / inp.max) * 100)) + '%' |
||||||
} |
} |
||||||
|
|
||||||
// Display selected STA SSID etc
|
// Display selected STA SSID etc
|
||||||
function selectSta(name, password, ip) { |
function selectSta (name, password, ip) { |
||||||
$('#sta_ssid').val(name); |
$('#sta_ssid').val(name) |
||||||
$('#sta_password').val(password); |
$('#sta_password').val(password) |
||||||
|
|
||||||
$('#sta-nw').toggleClass('hidden', name.length == 0); |
$('#sta-nw').toggleClass('hidden', name.length === 0) |
||||||
$('#sta-nw-nil').toggleClass('hidden', name.length > 0); |
$('#sta-nw-nil').toggleClass('hidden', name.length > 0) |
||||||
|
|
||||||
$('#sta-nw .essid').html(e(name)); |
$('#sta-nw .essid').html(esc(name)) |
||||||
var nopw = undef(password) || password.length == 0; |
const nopw = undef(password) || password.length === 0 |
||||||
$('#sta-nw .passwd').toggleClass('hidden', nopw); |
$('#sta-nw .passwd').toggleClass('hidden', nopw) |
||||||
$('#sta-nw .nopasswd').toggleClass('hidden', !nopw); |
$('#sta-nw .nopasswd').toggleClass('hidden', !nopw) |
||||||
$('#sta-nw .ip').html(ip.length>0 ? tr('wifi.connected_ip_is')+ip : tr('wifi.not_conn')); |
$('#sta-nw .ip').html(ip.length > 0 ? tr('wifi.connected_ip_is') + ip : tr('wifi.not_conn')) |
||||||
} |
} |
||||||
|
|
||||||
/** Update display for received response */ |
/** Update display for received response */ |
||||||
function onScan(resp, status) { |
function onScan (resp, status) { |
||||||
//var ap_json = {
|
// var ap_json = {
|
||||||
// "result": {
|
// "result": {
|
||||||
// "inProgress": "0",
|
// "inProgress": "0",
|
||||||
// "APs": [
|
// "APs": [
|
||||||
// {"essid": "Chlivek", "bssid": "88:f7:c7:52:b3:99", "rssi": "204", "enc": "4", "channel": "1"},
|
// {"essid": "Chlivek", "bssid": "88:f7:c7:52:b3:99", "rssi": "204", "enc": "4", "channel": "1"},
|
||||||
// {"essid": "TyNikdy", "bssid": "5c:f4:ab:0d:f1:1b", "rssi": "164", "enc": "3", "channel": "1"},
|
// {"essid": "TyNikdy", "bssid": "5c:f4:ab:0d:f1:1b", "rssi": "164", "enc": "3", "channel": "1"},
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
//};
|
// };
|
||||||
|
|
||||||
if (status != 200) { |
if (status !== 200) { |
||||||
// bad response
|
// bad response
|
||||||
rescan(5000); // wait 5sm then retry
|
rescan(5000) // wait 5sm then retry
|
||||||
return; |
return |
||||||
} |
} |
||||||
|
|
||||||
try { |
try { |
||||||
resp = JSON.parse(resp); |
resp = JSON.parse(resp) |
||||||
} catch (e) { |
} catch (e) { |
||||||
console.log(e); |
console.log(e) |
||||||
rescan(5000); |
rescan(5000) |
||||||
return; |
return |
||||||
} |
} |
||||||
|
|
||||||
var done = !bool(resp.result.inProgress) && (resp.result.APs.length > 0); |
const done = !bool(resp.result.inProgress) && (resp.result.APs.length > 0) |
||||||
rescan(done ? 15000 : 1000); |
rescan(done ? 15000 : 1000) |
||||||
if (!done) return; // no redraw yet
|
if (!done) return // no redraw yet
|
||||||
|
|
||||||
// clear the AP list
|
// clear the AP list
|
||||||
var $list = $('#ap-list'); |
let $list = $('#ap-list') |
||||||
// remove old APs
|
// remove old APs
|
||||||
$('#ap-list .AP').remove(); |
$('#ap-list .AP').remove() |
||||||
|
|
||||||
$list.toggleClass('hidden', !done); |
$list.toggleClass('hidden', !done) |
||||||
$('#ap-loader').toggleClass('hidden', done); |
$('#ap-loader').toggleClass('hidden', done) |
||||||
|
|
||||||
// scan done
|
// scan done
|
||||||
resp.result.APs.sort(function (a, b) { |
resp.result.APs.sort(function (a, b) { |
||||||
return b.rssi - a.rssi; |
return b.rssi - a.rssi |
||||||
}).forEach(function (ap) { |
}).forEach(function (ap) { |
||||||
ap.enc = parseInt(ap.enc); |
ap.enc = parseInt(ap.enc) |
||||||
|
|
||||||
if (ap.enc > 4) return; // hide unsupported auths
|
if (ap.enc > 4) return // hide unsupported auths
|
||||||
|
|
||||||
var item = mk('div'); |
let item = mk('div') |
||||||
|
|
||||||
var $item = $(item) |
let $item = $(item) |
||||||
.data('ssid', ap.essid) |
.data('ssid', ap.essid) |
||||||
.data('pwd', ap.enc) |
.data('pwd', ap.enc) |
||||||
.attr('tabindex', 0) |
.attr('tabindex', 0) |
||||||
.addClass('AP'); |
.addClass('AP') |
||||||
|
|
||||||
// mark current SSID
|
// mark current SSID
|
||||||
if (ap.essid == curSSID) { |
if (ap.essid === curSSID) { |
||||||
$item.addClass('selected'); |
$item.addClass('selected') |
||||||
} |
} |
||||||
|
|
||||||
var inner = mk('div'); |
let inner = mk('div') |
||||||
$(inner).addClass('inner') |
let escapedSSID = $.htmlEscape(ap.essid) |
||||||
.htmlAppend('<div class="rssi">{0}</div>'.format(ap.rssi_perc)) |
$(inner).addClass('inner') |
||||||
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format($.htmlEscape(ap.essid))) |
.htmlAppend(`<div class="rssi">${ap.rssi_perc}</div>`) |
||||||
.htmlAppend('<div class="auth">{0}</div>'.format(authStr[ap.enc])); |
.htmlAppend(`<div class="essid" title="${escapedSSID}">${escapedSSID}</div>`) |
||||||
|
.htmlAppend(`<div class="auth">${authStr[ap.enc]}</div>`) |
||||||
$item.on('click', function () { |
|
||||||
var $th = $(this); |
$item.on('click', function () { |
||||||
|
let $th = $(this) |
||||||
var conn_ssid = $th.data('ssid'); |
|
||||||
var conn_pass = ''; |
const conn_ssid = $th.data('ssid') |
||||||
|
let conn_pass = '' |
||||||
if (+$th.data('pwd')) { |
|
||||||
// this AP needs a password
|
if (+$th.data('pwd')) { |
||||||
conn_pass = prompt(tr("wifi.enter_passwd").replace(":ssid:", conn_ssid)); |
// this AP needs a password
|
||||||
if (!conn_pass) return; |
conn_pass = prompt(tr('wifi.enter_passwd').replace(':ssid:', conn_ssid)) |
||||||
} |
if (!conn_pass) return |
||||||
|
} |
||||||
$('#sta_password').val(conn_pass); |
|
||||||
$('#sta_ssid').val(conn_ssid); |
$('#sta_password').val(conn_pass) |
||||||
selectSta(conn_ssid, conn_pass, ''); |
$('#sta_ssid').val(conn_ssid) |
||||||
}); |
selectSta(conn_ssid, conn_pass, '') |
||||||
|
}) |
||||||
|
|
||||||
item.appendChild(inner); |
item.appendChild(inner) |
||||||
$list[0].appendChild(item); |
$list[0].appendChild(item) |
||||||
}); |
}) |
||||||
} |
} |
||||||
|
|
||||||
function startScanning() { |
function startScanning () { |
||||||
$('#ap-loader').removeClass('hidden'); |
$('#ap-loader').removeClass('hidden') |
||||||
$('#ap-scan').addClass('hidden'); |
$('#ap-scan').addClass('hidden') |
||||||
$('#ap-loader .anim-dots').html('.'); |
$('#ap-loader .anim-dots').html('.') |
||||||
|
|
||||||
scanAPs(); |
scanAPs() |
||||||
} |
} |
||||||
|
|
||||||
/** Ask the CGI what APs are visible (async) */ |
/** Ask the CGI what APs are visible (async) */ |
||||||
function scanAPs() { |
function scanAPs () { |
||||||
if (_demo) { |
if (_demo) { |
||||||
onScan(_demo_aps, 200); |
onScan(_demo_aps, 200) |
||||||
} else { |
} else { |
||||||
$.get('http://' + _root + '/cfg/wifi/scan', onScan); |
$.get('http://' + _root + '/cfg/wifi/scan', onScan) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
function rescan(time) { |
function rescan (time) { |
||||||
setTimeout(scanAPs, time); |
setTimeout(scanAPs, time) |
||||||
} |
} |
||||||
|
|
||||||
/** Set up the WiFi page */ |
/** Set up the WiFi page */ |
||||||
function wifiInit(cfg) { |
function wifiInit (cfg) { |
||||||
// Update slider value displays
|
// Update slider value displays
|
||||||
$('.Row.range').forEach(function(x) { |
$('.Row.range').forEach(function (x) { |
||||||
var inp = x.querySelector('input'); |
let inp = x.querySelector('input') |
||||||
var disp1 = x.querySelector('.x-disp1'); |
let disp1 = x.querySelector('.x-disp1') |
||||||
var disp2 = x.querySelector('.x-disp2'); |
let disp2 = x.querySelector('.x-disp2') |
||||||
var t = rangePt(inp); |
let t = rangePt(inp) |
||||||
$(disp1).html(t); |
$(disp1).html(t) |
||||||
$(disp2).html(t); |
$(disp2).html(t) |
||||||
$(inp).on('input', function() { |
$(inp).on('input', function () { |
||||||
t = rangePt(inp); |
t = rangePt(inp) |
||||||
$(disp1).html(t); |
$(disp1).html(t) |
||||||
$(disp2).html(t); |
$(disp2).html(t) |
||||||
}); |
}) |
||||||
}); |
}) |
||||||
|
|
||||||
// Forget STA credentials
|
// Forget STA credentials
|
||||||
$('#forget-sta').on('click', function() { |
$('#forget-sta').on('click', function () { |
||||||
selectSta('', '', ''); |
selectSta('', '', '') |
||||||
return false; |
return false |
||||||
}); |
}) |
||||||
|
|
||||||
selectSta(cfg.sta_ssid, cfg.sta_password, cfg.sta_active_ip); |
selectSta(cfg.sta_ssid, cfg.sta_password, cfg.sta_active_ip) |
||||||
curSSID = cfg.sta_active_ssid; |
curSSID = cfg.sta_active_ssid |
||||||
} |
} |
||||||
|
|
||||||
w.init = wifiInit; |
w.init = wifiInit |
||||||
w.startScanning = startScanning; |
w.startScanning = startScanning |
||||||
})(window.WiFi = {}); |
})(window.WiFi = {}) |
||||||
|
@ -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