Compare commits
330 Commits
Author | SHA1 | Date |
---|---|---|
josdejong | 52b6e94e39 | |
jos | 38007b715d | |
jos | 92297c0d2c | |
jos | ae1e39ba3f | |
jos | 76e4bf5e16 | |
jos | a869fa0f3a | |
jos | 17df475c11 | |
jos | 21d8db13d0 | |
jos | 7012de59d6 | |
jos | ce37b88296 | |
jos | f1ddc03c6d | |
jos | f33342b54b | |
jos | 0f24a9204f | |
jos | 9a2c4f1e6a | |
jos | aaa498b4ec | |
jos | 6c17d6256c | |
jos | a0de92b919 | |
jos | 3d2f9ce8df | |
jos | 4f86135050 | |
jos | 3e795b3275 | |
jos | e298d63311 | |
jos | 263f38f45c | |
jos | b4a0dd6a3d | |
jos | 2999fbc380 | |
jos | 2c560264c0 | |
jos | 0349f94ead | |
jos | 4c539cb4f5 | |
jos | 702dc5ed20 | |
jos | 37e22908c7 | |
jos | 3865a1d365 | |
jos | fdd44a276c | |
jos | 081cde4489 | |
jos | 0fb816d074 | |
jos | 3028617ff7 | |
jos | b6aea6df9d | |
jos | 088bee74cb | |
jos | 80f45f39ec | |
jos | 9b2b8233af | |
jos | e2ffd04e6e | |
jos | 41a7069398 | |
jos | a62b1e1c94 | |
jos | ec987385c8 | |
jos | 410353c86f | |
jos | dc814a3aa5 | |
jos | 56124cf17f | |
jos | d6ad4c87d0 | |
jos | fc1f73c4a6 | |
jos | b1f51b04a0 | |
jos | cc3e441361 | |
jos | 65df3428ac | |
jos | 1b2bcd6dd9 | |
jos | 950463f3a9 | |
jos | 0ba51276f4 | |
jos | 1127c15ea2 | |
jos | 7cdfb44345 | |
jos | 14e6315159 | |
jos | 2466b6d4da | |
jos | 9eb944d926 | |
jos | d53e5360a3 | |
jos | 5d4a551526 | |
jos | d73b79cb4e | |
jos | ccf89162b7 | |
jos | 419bdc6307 | |
jos | 9a37fe0451 | |
jos | 8b2541e3d8 | |
jos | e5c6459f73 | |
jos | 8bb9cb10cc | |
jos | 46661177db | |
jos | 1d4a5af82e | |
jos | 9e0249f550 | |
jos | 6385e4b193 | |
jos | 29fe32da40 | |
jos | 99790c0fac | |
jos | abfcca84d6 | |
jos | 8ffb7702ba | |
jos | 29fed7099f | |
jos | b011b196e1 | |
jos | 346517b947 | |
jos | 0e22497467 | |
jos | 34ca56aee0 | |
jos | aee48b75db | |
jos | e69d962ada | |
jos | 9db7fdf0e5 | |
jos | dd22b97eb0 | |
jos | 1f5f85a4e0 | |
jos | e979e0f016 | |
jos | 7064578b31 | |
jos | 1a6661fbb5 | |
jos | 156f330e4e | |
jos | 53b20e2f59 | |
jos | a059eb844e | |
jos | f491a00575 | |
jos | 378c8ef250 | |
jos | 338d19b5a9 | |
jos | 040fe12d75 | |
jos | 5fb69ffcc5 | |
jos | e30674a971 | |
jos | 49cc7eb288 | |
jos | c19334894c | |
jos | a9174edf16 | |
jos | 745b77725b | |
jos | 0a7df1dad9 | |
jos | b3c41bce03 | |
jos | b71afda45a | |
jos | 157f63d11a | |
jos | e69868da85 | |
jos | 56528dc054 | |
jos | bac12dcc5a | |
jos | 2e75e5a9cf | |
jos | 8cded5496c | |
jos | cb354f2331 | |
jos | dd989f9a64 | |
jos | 8425579718 | |
jos | a2f7f61389 | |
jos | cea4e2c101 | |
jos | 0910aa5a63 | |
jos | 26fa339c35 | |
jos | 375ea56316 | |
jos | 52ec02e3e1 | |
jos | eee0b55636 | |
jos | 943d721d84 | |
jos | f3313158db | |
jos | 6f9776a07a | |
jos | 432e169f32 | |
jos | 2ee11399ec | |
jos | 86f8aa56d0 | |
jos | 12f1543ef9 | |
jos | 598fe63d80 | |
jos | 99cc07577d | |
jos | 8462bcda2c | |
jos | 239b702040 | |
jos | 91c1fd823f | |
jos | 7380be631c | |
jos | 1c37b303b9 | |
jos | e069d3f0b3 | |
jos | 633daf6c95 | |
jos | 3ca23568cf | |
jos | a03d962daa | |
jos | c5ce6525a5 | |
jos | 52977b009c | |
jos | b162f5ed13 | |
jos | 5b1426eb7c | |
jos | f786940d8d | |
jos | 94b7be90d3 | |
jos | bb8734707e | |
jos | bb6565f3b3 | |
jos | 111b85a4cb | |
jos | fb71b61ba5 | |
jos | 82ff880c27 | |
jos | 82ddb965fb | |
jos | 7eb9464474 | |
Jos de Jong | 1c09829622 | |
jos | 1cd6d71dda | |
jos | b730cb29bf | |
jos | 07f92467e7 | |
jos | ba1eb2a837 | |
jos | 6f1fbac9ac | |
jos | 675432c52d | |
jos | ce484c670a | |
jos | 984c8bac3d | |
jos | ea01e581cd | |
jos | 840e5f41a7 | |
jos | 85ae6d3b5e | |
jos | 1cce254e9c | |
jos | ab8d4b4be6 | |
jos | 0f7c601635 | |
jos | b256e1c8ce | |
jos | a9a453f51f | |
jos | d5500bef89 | |
jos | 65e868b1c3 | |
jos | afcf19bac5 | |
jos | 9f81dfb2f6 | |
jos | 0e490fdeee | |
jos | bef37648b7 | |
jos | d2e1bed9d6 | |
jos | 349e6015a3 | |
jos | fec1bb8f23 | |
jos | 6ba53f7364 | |
jos | 100efb35ae | |
jos | 198e8edf85 | |
jos | aa4b963592 | |
jos | 95f0a31731 | |
jos | d1f35c6214 | |
jos | c7418807d8 | |
jos | f209df27a9 | |
jos | 2e434f49f9 | |
jos | 785ab5205c | |
jos | 98f56efc47 | |
jos | 4208dff7b9 | |
jos | 8824cd10f8 | |
jos | 37f2f77124 | |
jos | 939ad792d6 | |
jos | e5e61b71e3 | |
jos | 96186b836a | |
jos | e6cb225514 | |
jos | 70810655b8 | |
jos | fe3bc56d53 | |
jos | 8da5ece3b8 | |
jos | e45aa82b4b | |
jos | 6120949acf | |
jos | 2bc4cf7dfe | |
jos | f01f52094e | |
jos | 68a5b1476f | |
jos | 18d63fcd9a | |
jos | 116d5fe620 | |
jos | a70ba17bc7 | |
jos | f882756dda | |
jos | a06f7ac5d1 | |
jos | a7c77ff9d1 | |
jos | 3f3fef9b40 | |
jos | a5145ca004 | |
jos | b179318dc6 | |
jos | b73ff81e19 | |
jos | 9cbb7574c0 | |
jos | 8a3fffcd24 | |
jos | 076853bf04 | |
jos | f2f1636ef0 | |
jos | 8f6ddf91bc | |
jos | 4a12eed14f | |
jos | 9722414728 | |
jos | 099c5ee8ba | |
jos | 9d05195aec | |
jos | 25ecb2dfab | |
jos | a399aef0ea | |
jos | f6dfe874db | |
jos | dc8bb9349c | |
jos | 30aa73d3a3 | |
jos | 3b1a5c2d24 | |
jos | 6cf85d2ce9 | |
jos | 482720b28c | |
jos | bb62795b6b | |
jos | bde1c9c4d1 | |
jos | 1cad4fc8ea | |
jos | 5ff41f2d9d | |
jos | a607d2c2f4 | |
jos | ceea59b30d | |
jos | ae90077ed6 | |
jos | b5ce5cb2ae | |
jos | adb3c6bdd9 | |
jos | 5fc5e302dc | |
jos | 1bcc6382aa | |
jos | 873d5f8ae2 | |
jos | 88aed193c6 | |
jos | 339b73fe51 | |
jos | d755ca9d03 | |
jos | 0c3faa03ea | |
jos | 804c68010f | |
jos | b44a465207 | |
jos | 84031dc3ef | |
jos | 10e9f16b75 | |
jos | 6db127739c | |
jos | 75c1497de1 | |
jos | 54788aa593 | |
jos | 2a1fabc6ac | |
jos | 291cfbfa22 | |
jos | 515e7a6bf2 | |
jos | 7f979b97dd | |
jos | 0a857aaad6 | |
jos | ad36723618 | |
jos | c8b3bd2d7a | |
jos | 7b6a3747d2 | |
jos | fb758d3135 | |
jos | 83ae2bab07 | |
jos | d235425f36 | |
jos | ff7f683b11 | |
jos | de6d7c9551 | |
jos | 611db9c431 | |
jos | 3d467e65e1 | |
jos | 678db6bced | |
jos | 44183e01bd | |
jos | 7adc760b33 | |
jos | ec63a9a759 | |
jos | bb8850fb91 | |
jos | fe0f98dfe0 | |
jos | 94671507b5 | |
jos | 9350debba2 | |
jos | 7280239771 | |
jos | ea6b00b67d | |
jos | 71c38f07af | |
jos | 123b0c7cb4 | |
jos | 56fc3164ec | |
jos | 737963b908 | |
jos | b6d622e0d0 | |
jos | c5a68b1da3 | |
jos | d8a0079032 | |
jos | 069d35ace4 | |
jos | c8a5614511 | |
jos | 41821ef825 | |
jos | 422315dba2 | |
jos | ebca411384 | |
jos | 24ab2899dc | |
jos | a12505b017 | |
jos | 25dfa2c0e1 | |
jos | d014146956 | |
jos | 1099e3859f | |
jos | 608f45c8e7 | |
jos | a065fbb09e | |
jos | ab37f5ba9a | |
jos | 9d61ce001a | |
jos | 7f6e7459df | |
jos | 4baa98c961 | |
jos | 274a99ddef | |
jos | 525a41a7d5 | |
jos | 49f0ed3e4f | |
jos | 38250a38ba | |
jos | 224e436828 | |
jos | 5416676735 | |
jos | 667d3f32aa | |
jos | c3c836fa89 | |
jos | a9f0fe07c1 | |
jos | a1859a9aff | |
jos | cd08e16789 | |
jos | 37ae2111ee | |
jos | fda4f94655 | |
jos | fd631cd34c | |
jos | d0abc3124a | |
jos | b796de2128 | |
jos | 5adf72dcc1 | |
jos | 32853ea0d8 | |
jos | 7fb5ca6517 | |
jos | c0f075a9e6 | |
jos | 8723855bdf | |
jos | 22785614af | |
jos | c46f27334c | |
jos | 1c3a323387 | |
jos | f011e3f107 | |
jos | 89657a539f | |
jos | 4e0aa5659c | |
jos | 0d250cbdcd | |
jos | b4cf47b06f |
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"plugins": [
|
||||
"transform-class-properties",
|
||||
"transform-object-rest-spread",
|
||||
"transform-react-jsx",
|
||||
["transform-runtime", {
|
||||
"polyfill": false,
|
||||
"regenerator": true
|
||||
}]
|
||||
],
|
||||
"presets": [
|
||||
["env", {
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "ie >= 9", "safari >= 7"]
|
||||
},
|
||||
"modules": "commonjs",
|
||||
"loose": true
|
||||
}]
|
||||
]
|
||||
}
|
|
@ -1,6 +1,31 @@
|
|||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Ignore css files generated from scss
|
||||
/src/**/*.css
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/lib
|
||||
/dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# webstorm
|
||||
.idea
|
||||
build
|
||||
downloads
|
||||
node_modules
|
||||
*.zip
|
||||
npm-debug.log
|
||||
|
||||
# visual studio code
|
||||
.vscode
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
|
|
@ -9,7 +9,7 @@ There are a few preferences regarding code contributions:
|
|||
- `jsoneditor` follows the node.js code style as described
|
||||
[here](http://nodeguide.com/style.html).
|
||||
- Send pull requests to the `develop` branch, not the `master` branch.
|
||||
- Only commit changes done in the source files under `./src`, not to the builds
|
||||
which are located under the `./dist` folder.
|
||||
- Only commit changes done in the source files under `./src`, do not
|
||||
commit builds located in the `./lib` and `./dist` folders.
|
||||
|
||||
Thanks!
|
||||
|
|
177
LICENSE
177
LICENSE
|
@ -1,176 +1,7 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
Copyright 2015-2017 Jos de Jong
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
1. Definitions.
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
17
NOTICE
17
NOTICE
|
@ -1,17 +0,0 @@
|
|||
JSON Editor
|
||||
https://github.com/josdejong/jsoneditor
|
||||
|
||||
Copyright (C) 2011-2015 Jos de Jong
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
75
README.md
75
README.md
|
@ -47,18 +47,39 @@ Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 9+.
|
|||
|
||||
## Install
|
||||
|
||||
with npm (recommended):
|
||||
Install via npm:
|
||||
|
||||
npm install jsoneditor
|
||||
|
||||
with bower:
|
||||
### Versions
|
||||
|
||||
bower install jsoneditor
|
||||
There are two versions of jsoneditor available: a full version and
|
||||
a minimalist version.
|
||||
|
||||
#### Full version
|
||||
|
||||
If you're not sure which version to use, use the full version: jsoneditor.js.
|
||||
|
||||
#### Minimalist version
|
||||
|
||||
The minimalist version, jsoneditor-minimalist.js, has excluded the following libraries:
|
||||
|
||||
- `ace` (via `brace`), used for the code editor.
|
||||
- `ajv`, used for JSON schema validation.
|
||||
|
||||
This reduces the the size of the minified and gzipped JavaScript considerably.
|
||||
|
||||
When to use the minimalist version?
|
||||
|
||||
- If you don't need the mode "code" and don't need JSON schema validation.
|
||||
- Or if you want to provide `ace` and/or `ajv` yourself via the configuration
|
||||
options, for example when you already use Ace in other parts of your
|
||||
web application too and don't want to bundle the library twice.
|
||||
|
||||
|
||||
#### More
|
||||
### More
|
||||
|
||||
There is a directive available for using JSONEditor in Angular.js:
|
||||
There is a directive available for using `jsoneditor` in Angular.js:
|
||||
|
||||
[https://github.com/angular-tools/ng-jsoneditor](https://github.com/angular-tools/ng-jsoneditor)
|
||||
|
||||
|
@ -72,17 +93,16 @@ There is a directive available for using JSONEditor in Angular.js:
|
|||
<!-- when using the mode "code", it's important to specify charset utf-8 -->
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<link href="jsoneditor/dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
|
||||
<script src="jsoneditor/dist/jsoneditor.min.js"></script>
|
||||
<script src="jsoneditor/dist/jsoneditor.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="jsoneditor" style="width: 400px; height: 400px;"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
var container = document.getElementById("jsoneditor");
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var options = {};
|
||||
var editor = new JSONEditor(container, options);
|
||||
var editor = jsoneditor(container, options);
|
||||
|
||||
// set json
|
||||
var json = {
|
||||
|
@ -120,36 +140,23 @@ jsoneditor:
|
|||
npm run build
|
||||
```
|
||||
|
||||
This will generate the files `./jsoneditor.js`, `./jsoneditor.css`, and
|
||||
minified versions in the dist of the project.
|
||||
This will generate the file `./dist/jsoneditor.js` and
|
||||
`./dist/jsoneditor-minimalist.js` and corresponding source maps.
|
||||
|
||||
- To automatically build when a source file has changed:
|
||||
- For development, start a develop server which automatically reloads
|
||||
when a source file has changed:
|
||||
|
||||
```
|
||||
npm run watch
|
||||
npm start
|
||||
```
|
||||
|
||||
This will update `./jsoneditor.js` and `./jsoneditor.css` in the dist folder
|
||||
on every change, but it will **NOT** update the minified versions as that's
|
||||
an expensive operation.
|
||||
- Run unit tests (Jest):
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
|
||||
## Custom builds
|
||||
|
||||
The source code of JSONEditor consists of CommonJS modules. JSONEditor can be bundled in a customized way using a module bundler like [browserify](http://browserify.org/) or [webpack](http://webpack.github.io/). First, install all dependencies of jsoneditor:
|
||||
|
||||
npm install
|
||||
|
||||
To create a custom bundle of the source code using browserify:
|
||||
|
||||
browserify ./index.js -o ./jsoneditor.custom.js -s JSONEditor
|
||||
|
||||
The Ace editor, used in mode `code`, accounts for about 75% of the total
|
||||
size of the library. To exclude the Ace editor from the bundle:
|
||||
|
||||
browserify ./index.js -o ./jsoneditor.custom.js -s JSONEditor -x brace -x brace/mode/json -x brace/ext/searchbox
|
||||
|
||||
To minify the generated bundle, use [uglifyjs](https://github.com/mishoo/UglifyJS2):
|
||||
|
||||
uglifyjs ./jsoneditor.custom.js -o ./jsoneditor.custom.min.js -m -c
|
||||
## License
|
||||
|
||||
MIT
|
32
bower.json
32
bower.json
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"name": "jsoneditor",
|
||||
"description": "A web-based tool to view, edit and format JSON",
|
||||
"tags": [
|
||||
"json",
|
||||
"editor",
|
||||
"viewer",
|
||||
"formatter"
|
||||
],
|
||||
"homepage": "http://jsoneditoronline.org/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/josdejong/jsoneditor.git"
|
||||
},
|
||||
"main": [
|
||||
"./dist/jsoneditor.min.js",
|
||||
"./dist/jsoneditor.min.css"
|
||||
],
|
||||
"bugs": "https://github.com/josdejong/jsoneditor/issues",
|
||||
"ignore": [
|
||||
"misc",
|
||||
"node_modules",
|
||||
"test",
|
||||
"tools",
|
||||
"gulpfile.js",
|
||||
"npm-debug.log",
|
||||
".idea",
|
||||
".npmignore",
|
||||
".gitignore"
|
||||
],
|
||||
"dependencies": {}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
const webpack = require('webpack')
|
||||
|
||||
const minifyPlugin = new webpack.optimize.UglifyJsPlugin({ sourceMap: true })
|
||||
|
||||
const productionEnvPlugin = new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify('production')
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
entry: './src/jsoneditor/index.vanilla.js',
|
||||
devtool: 'source-map',
|
||||
cache: true,
|
||||
bail: true,
|
||||
output: {
|
||||
library: 'jsoneditor',
|
||||
libraryTarget: 'umd',
|
||||
filename: 'dist/jsoneditor.js'
|
||||
},
|
||||
plugins: [
|
||||
// bannerPlugin,
|
||||
productionEnvPlugin,
|
||||
minifyPlugin
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: { loader: 'babel-loader'}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [ 'style-loader', 'css-loader' ]
|
||||
},
|
||||
{
|
||||
test: /\.svg/,
|
||||
use: {
|
||||
loader: 'svg-url-loader',
|
||||
options: {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// using preact saves in the order of 25 kB
|
||||
resolve: {
|
||||
'alias': {
|
||||
'react': 'preact-compat',
|
||||
'react-dom': 'preact-compat'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
const webpack = require('webpack')
|
||||
const config = require('./webpack.config')
|
||||
const emptyFile = __dirname + '/../src/jsoneditor/utils/empty.js'
|
||||
|
||||
const excludeAcePlugin = new webpack.NormalModuleReplacementPlugin(new RegExp('assets\/ace'), emptyFile)
|
||||
const excludeAjvPlugin = new webpack.NormalModuleReplacementPlugin(new RegExp('^ajv$'), emptyFile)
|
||||
|
||||
const configMinimalist = Object.assign({}, config, {
|
||||
output: Object.assign({}, config.output, {
|
||||
filename: 'dist/jsoneditor-minimalist.js'
|
||||
}),
|
||||
|
||||
plugins: [
|
||||
excludeAcePlugin,
|
||||
excludeAjvPlugin
|
||||
].concat(config.plugins)
|
||||
})
|
||||
|
||||
module.exports = configMinimalist
|
|
@ -1,893 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="216"
|
||||
height="144"
|
||||
id="svg4136"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r"
|
||||
sodipodi:docname="jsoneditor-icons.svg">
|
||||
<title
|
||||
id="title6512">JSON Editor Icons</title>
|
||||
<metadata
|
||||
id="metadata4148">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>JSON Editor Icons</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs4146" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
id="namedview4144"
|
||||
showgrid="true"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="97.217248"
|
||||
inkscape:cy="59.950227"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4136"
|
||||
showguides="false"
|
||||
borderlayer="false"
|
||||
inkscape:showpageshadow="true"
|
||||
showborder="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4640"
|
||||
empspacing="24" />
|
||||
</sodipodi:namedview>
|
||||
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
|
||||
<g
|
||||
id="g4394">
|
||||
<rect
|
||||
x="4"
|
||||
y="4"
|
||||
width="16"
|
||||
height="16"
|
||||
id="svg_1"
|
||||
style="fill:#1aae1c;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0"
|
||||
x="28.000006"
|
||||
y="3.999995"
|
||||
width="16"
|
||||
height="16"
|
||||
id="svg_1-7" />
|
||||
<rect
|
||||
id="rect4165"
|
||||
height="16"
|
||||
width="16"
|
||||
y="3.999995"
|
||||
x="52.000004"
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
x="172.00002"
|
||||
y="3.9999852"
|
||||
width="16"
|
||||
height="16"
|
||||
id="rect4175" />
|
||||
<rect
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
x="196"
|
||||
y="3.999995"
|
||||
width="16"
|
||||
height="16"
|
||||
id="rect4175-3" />
|
||||
<g
|
||||
style="stroke:none"
|
||||
id="g4299">
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
id="svg_1-1"
|
||||
height="1.9999986"
|
||||
width="9.9999924"
|
||||
y="10.999998"
|
||||
x="7.0000048" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
id="svg_1-1-1"
|
||||
height="9.9999838"
|
||||
width="1.9999955"
|
||||
y="7.0000114"
|
||||
x="11.000005" />
|
||||
</g>
|
||||
<g
|
||||
style="stroke:none"
|
||||
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
|
||||
id="g4299-3">
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
id="svg_1-1-0"
|
||||
height="1.9999986"
|
||||
width="9.9999924"
|
||||
y="10.999998"
|
||||
x="7.0000048" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
id="svg_1-1-1-9"
|
||||
height="9.9999838"
|
||||
width="1.9999955"
|
||||
y="7.0000114"
|
||||
x="11.000005" />
|
||||
</g>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
x="55.000004"
|
||||
y="7.0000048"
|
||||
width="6.9999909"
|
||||
height="6.9999905"
|
||||
id="svg_1-7-5" />
|
||||
<rect
|
||||
id="rect4354"
|
||||
height="6.9999905"
|
||||
width="6.9999909"
|
||||
y="10.00001"
|
||||
x="58"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647"
|
||||
x="58.000004"
|
||||
y="10.000005"
|
||||
width="6.9999909"
|
||||
height="6.9999905"
|
||||
id="svg_1-7-5-7" />
|
||||
<g
|
||||
id="g4378">
|
||||
<rect
|
||||
id="svg_1-7-5-3"
|
||||
height="1.9999965"
|
||||
width="7.9999909"
|
||||
y="10.999999"
|
||||
x="198"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
x="198"
|
||||
y="7.0000005"
|
||||
width="11.999995"
|
||||
height="1.9999946"
|
||||
id="rect4374" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
x="198"
|
||||
y="14.999996"
|
||||
width="3.9999928"
|
||||
height="1.9999995"
|
||||
id="rect4376" />
|
||||
</g>
|
||||
<g
|
||||
id="g4383"
|
||||
transform="matrix(1,0,0,-1,-23.999995,23.999995)">
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
x="198"
|
||||
y="10.999999"
|
||||
width="7.9999909"
|
||||
height="1.9999965"
|
||||
id="rect4385" />
|
||||
<rect
|
||||
id="rect4387"
|
||||
height="1.9999946"
|
||||
width="11.999995"
|
||||
y="7.0000005"
|
||||
x="198"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4389"
|
||||
height="1.9999995"
|
||||
width="3.9999928"
|
||||
y="14.999996"
|
||||
x="198"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||
</g>
|
||||
<rect
|
||||
y="3.9999199"
|
||||
x="76"
|
||||
height="16"
|
||||
width="16"
|
||||
id="rect3754-4"
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4351"
|
||||
d="m 85.10447,6.0157384 -0.0156,1.4063 c 3.02669,-0.2402 0.33008,3.6507996 2.48438,4.5780996 -2.18694,1.0938 0.49191,4.9069 -2.45313,4.5781 l -0.0156,1.4219 c 5.70828,0.559 1.03264,-5.1005 4.70313,-5.2656 l 0,-1.4063 c -3.61303,-0.027 1.11893,-5.7069996 -4.70313,-5.3124996 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4351-9"
|
||||
d="m 82.78125,5.9984384 0.0156,1.4063 c -3.02668,-0.2402 -0.33007,3.6506996 -2.48437,4.5780996 2.18694,1.0938 -0.49192,4.9069 2.45312,4.5781 l 0.0156,1.4219 c -5.70827,0.559 -1.03263,-5.1004 -4.70312,-5.2656 l 0,-1.4063 c 3.61303,-0.027 -1.11894,-5.7070996 4.70312,-5.3124996 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
y="3.9999199"
|
||||
x="100"
|
||||
height="16"
|
||||
width="16"
|
||||
id="rect3754-25"
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path2987"
|
||||
d="m 103.719,5.6719384 0,12.7187996 3.03125,0 0,-1.5313 -1.34375,0 0,-9.6249996 1.375,0 0,-1.5625 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path2987-1"
|
||||
d="m 112.2185,5.6721984 0,12.7187996 -3.03125,0 0,-1.5313 1.34375,0 0,-9.6249996 -1.375,0 0,-1.5625 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
y="3.9999199"
|
||||
x="124"
|
||||
height="16"
|
||||
width="16"
|
||||
id="rect3754-73"
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3780"
|
||||
d="m 126.2824,17.602938 1.78957,0 1.14143,-2.8641 5.65364,0 1.14856,2.8641 1.76565,0 -4.78687,-11.1610996 -1.91903,0 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3782"
|
||||
d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
y="3.9999199"
|
||||
x="148"
|
||||
height="16"
|
||||
width="16"
|
||||
id="rect3754-35"
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5008-2"
|
||||
d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5008-2-8"
|
||||
d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
<rect
|
||||
x="4"
|
||||
y="27.999994"
|
||||
width="16"
|
||||
height="16"
|
||||
id="rect4432"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
x="28.000006"
|
||||
y="27.99999"
|
||||
width="16"
|
||||
height="16"
|
||||
id="rect4434" />
|
||||
<rect
|
||||
id="rect4436"
|
||||
height="16"
|
||||
width="16"
|
||||
y="27.99999"
|
||||
x="52.000004"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;stroke:#000000;stroke-width:0"
|
||||
x="172.00002"
|
||||
y="27.999981"
|
||||
width="16"
|
||||
height="16"
|
||||
id="rect4446" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;stroke:#000000;stroke-width:0"
|
||||
x="196"
|
||||
y="27.99999"
|
||||
width="16"
|
||||
height="16"
|
||||
id="rect4448" />
|
||||
<g
|
||||
id="g4466"
|
||||
style="stroke:none"
|
||||
transform="translate(0,23.999995)">
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
id="rect4468"
|
||||
height="1.9999986"
|
||||
width="9.9999924"
|
||||
y="10.999998"
|
||||
x="7.0000048" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
id="rect4470"
|
||||
height="9.9999838"
|
||||
width="1.9999955"
|
||||
y="7.0000114"
|
||||
x="11.000005" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,35.999996)"
|
||||
id="g4472"
|
||||
style="stroke:none">
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
id="rect4474"
|
||||
height="1.9999986"
|
||||
width="9.9999924"
|
||||
y="10.999998"
|
||||
x="7.0000048" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
|
||||
id="rect4476"
|
||||
height="9.9999838"
|
||||
width="1.9999955"
|
||||
y="7.0000114"
|
||||
x="11.000005" />
|
||||
</g>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
x="55.000004"
|
||||
y="31"
|
||||
width="6.9999909"
|
||||
height="6.9999905"
|
||||
id="rect4478" />
|
||||
<rect
|
||||
id="rect4480"
|
||||
height="6.9999905"
|
||||
width="6.9999909"
|
||||
y="34.000008"
|
||||
x="58"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
x="58.000004"
|
||||
y="34.000004"
|
||||
width="6.9999909"
|
||||
height="6.9999905"
|
||||
id="rect4482" />
|
||||
<g
|
||||
id="g4484"
|
||||
transform="translate(0,23.999995)">
|
||||
<rect
|
||||
id="rect4486"
|
||||
height="1.9999965"
|
||||
width="7.9999909"
|
||||
y="10.999999"
|
||||
x="198"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
x="198"
|
||||
y="7.0000005"
|
||||
width="11.999995"
|
||||
height="1.9999946"
|
||||
id="rect4488" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
x="198"
|
||||
y="14.999996"
|
||||
width="3.9999928"
|
||||
height="1.9999995"
|
||||
id="rect4490" />
|
||||
</g>
|
||||
<g
|
||||
id="g4492"
|
||||
transform="matrix(1,0,0,-1,-23.999995,47.99999)">
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
x="198"
|
||||
y="10.999999"
|
||||
width="7.9999909"
|
||||
height="1.9999965"
|
||||
id="rect4494" />
|
||||
<rect
|
||||
id="rect4496"
|
||||
height="1.9999946"
|
||||
width="11.999995"
|
||||
y="7.0000005"
|
||||
x="198"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4498"
|
||||
height="1.9999995"
|
||||
width="3.9999928"
|
||||
y="14.999996"
|
||||
x="198"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||
</g>
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
|
||||
id="rect3754-8"
|
||||
width="16"
|
||||
height="16"
|
||||
x="76"
|
||||
y="27.99992" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 85.10448,30.015537 -0.0156,1.4063 c 3.02668,-0.2402 0.33007,3.6508 2.48438,4.5781 -2.18695,1.0938 0.49191,4.90688 -2.45313,4.57808 l -0.0156,1.4219 c 5.70827,0.559 1.03263,-5.10048 4.70313,-5.26558 l 0,-1.4063 c -3.61304,-0.027 1.11893,-5.707 -4.70313,-5.3125 z"
|
||||
id="path4351-1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 82.78126,29.998237 0.0156,1.4063 c -3.02668,-0.2402 -0.33008,3.6507 -2.48438,4.5781 2.18694,1.0938 -0.49191,4.90688 2.45313,4.57808 l 0.0156,1.4219 c -5.70828,0.559 -1.03264,-5.10038 -4.70313,-5.26558 l 0,-1.4063 c 3.61303,-0.027 -1.11893,-5.7071 4.70313,-5.3125 z"
|
||||
id="path4351-9-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
|
||||
id="rect3754-65"
|
||||
width="16"
|
||||
height="16"
|
||||
x="100"
|
||||
y="27.99992" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 103.719,29.671937 0,12.71878 3.03125,0 0,-1.5313 -1.34375,0 0,-9.62498 1.375,0 0,-1.5625 z"
|
||||
id="path2987-8"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 112.2185,29.671937 0,12.71878 -3.03125,0 0,-1.5313 1.34375,0 0,-9.62498 -1.375,0 0,-1.5625 z"
|
||||
id="path2987-1-9"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
|
||||
id="rect3754-92"
|
||||
width="16"
|
||||
height="16"
|
||||
x="124"
|
||||
y="27.99992" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 126.2824,41.602917 1.78957,0 1.14143,-2.86408 5.65364,0 1.14856,2.86408 1.76565,0 -4.78687,-11.16108 -1.91902,0 z"
|
||||
id="path3780-9"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
<path
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
|
||||
d="m 129.72704,37.478837 4.60852,0.01 -2.30426,-5.5498 z"
|
||||
id="path3782-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
|
||||
id="rect3754-47"
|
||||
width="16"
|
||||
height="16"
|
||||
x="148"
|
||||
y="27.99992" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 156.47656,29.891737 0,2.1797 0.46093,2.3984 1.82813,0 0.39844,-2.3984 0,-2.1797 z"
|
||||
id="path5008-2-1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 152.51562,29.890637 0,2.1797 0.46094,2.3984 1.82812,0 0.39844,-2.3984 0,-2.1797 z"
|
||||
id="path5008-2-8-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<rect
|
||||
id="svg_1-7-2"
|
||||
height="1.9999961"
|
||||
width="11.999996"
|
||||
y="64"
|
||||
x="54"
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
id="svg_1-7-2-2"
|
||||
height="2.9999905"
|
||||
width="2.9999907"
|
||||
y="52"
|
||||
x="80.000008"
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||
x="85.000008"
|
||||
y="52"
|
||||
width="2.9999907"
|
||||
height="2.9999905"
|
||||
id="rect4561" />
|
||||
<rect
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||
x="80.000008"
|
||||
y="58"
|
||||
width="2.9999907"
|
||||
height="2.9999905"
|
||||
id="rect4563" />
|
||||
<rect
|
||||
id="rect4565"
|
||||
height="2.9999905"
|
||||
width="2.9999907"
|
||||
y="58"
|
||||
x="85.000008"
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4567"
|
||||
height="2.9999905"
|
||||
width="2.9999907"
|
||||
y="64"
|
||||
x="80.000008"
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||
x="85.000008"
|
||||
y="64"
|
||||
width="2.9999907"
|
||||
height="2.9999905"
|
||||
id="rect4569" />
|
||||
<circle
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="path4571"
|
||||
cx="110.06081"
|
||||
cy="57.939209"
|
||||
r="4.7438836" />
|
||||
<rect
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||
x="116.64566"
|
||||
y="-31.79752"
|
||||
width="4.229713"
|
||||
height="6.4053884"
|
||||
id="rect4563-2"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
|
||||
<path
|
||||
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 125,56 138.77027,56.095 132,64 Z"
|
||||
id="path4613"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4615"
|
||||
d="M 149,64 162.77027,63.905 156,56 Z"
|
||||
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||
x="54"
|
||||
y="53"
|
||||
width="11.999996"
|
||||
height="1.9999961"
|
||||
id="rect4638" />
|
||||
<rect
|
||||
id="svg_1-7-2-24"
|
||||
height="1.9999957"
|
||||
width="12.99999"
|
||||
y="-56"
|
||||
x="53"
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||
transform="matrix(0,1,-1,0,0,0)" />
|
||||
<rect
|
||||
transform="matrix(0,1,-1,0,0,0)"
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||
x="53"
|
||||
y="-66"
|
||||
width="12.99999"
|
||||
height="1.9999957"
|
||||
id="rect4657" />
|
||||
<rect
|
||||
id="rect4659"
|
||||
height="0.99999291"
|
||||
width="11.999999"
|
||||
y="57"
|
||||
x="54"
|
||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||
x="54"
|
||||
y="88.000122"
|
||||
width="11.999996"
|
||||
height="1.9999961"
|
||||
id="rect4661" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||
x="80.000008"
|
||||
y="76.000122"
|
||||
width="2.9999907"
|
||||
height="2.9999905"
|
||||
id="rect4663" />
|
||||
<rect
|
||||
id="rect4665"
|
||||
height="2.9999905"
|
||||
width="2.9999907"
|
||||
y="76.000122"
|
||||
x="85.000008"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
||||
<rect
|
||||
id="rect4667"
|
||||
height="2.9999905"
|
||||
width="2.9999907"
|
||||
y="82.000122"
|
||||
x="80.000008"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||
x="85.000008"
|
||||
y="82.000122"
|
||||
width="2.9999907"
|
||||
height="2.9999905"
|
||||
id="rect4669" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||
x="80.000008"
|
||||
y="88.000122"
|
||||
width="2.9999907"
|
||||
height="2.9999905"
|
||||
id="rect4671" />
|
||||
<rect
|
||||
id="rect4673"
|
||||
height="2.9999905"
|
||||
width="2.9999907"
|
||||
y="88.000122"
|
||||
x="85.000008"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
||||
<circle
|
||||
r="4.7438836"
|
||||
cy="81.939331"
|
||||
cx="110.06081"
|
||||
id="circle4675"
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
|
||||
id="rect4677"
|
||||
height="6.4053884"
|
||||
width="4.229713"
|
||||
y="-14.826816"
|
||||
x="133.6163"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4679"
|
||||
d="m 125,80.000005 13.77027,0.09499 L 132,87.999992 Z"
|
||||
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 149,88.0002 162.77027,87.9052 156,80.0002 Z"
|
||||
id="path4681"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<rect
|
||||
id="rect4683"
|
||||
height="1.9999961"
|
||||
width="11.999996"
|
||||
y="77.000122"
|
||||
x="54"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
||||
<rect
|
||||
transform="matrix(0,1,-1,0,0,0)"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||
x="77.000122"
|
||||
y="-56"
|
||||
width="12.99999"
|
||||
height="1.9999957"
|
||||
id="rect4685" />
|
||||
<rect
|
||||
id="rect4687"
|
||||
height="1.9999957"
|
||||
width="12.99999"
|
||||
y="-66"
|
||||
x="77.000122"
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||
transform="matrix(0,1,-1,0,0,0)" />
|
||||
<rect
|
||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||
x="54"
|
||||
y="81.000122"
|
||||
width="11.999999"
|
||||
height="0.99999291"
|
||||
id="rect4689" />
|
||||
<rect
|
||||
id="rect4761-1"
|
||||
height="1.9999945"
|
||||
width="15.99999"
|
||||
y="101"
|
||||
x="76.000008"
|
||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4761-0"
|
||||
height="1.9999945"
|
||||
width="15.99999"
|
||||
y="105"
|
||||
x="76.000008"
|
||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4761-7"
|
||||
height="1.9999945"
|
||||
width="9"
|
||||
y="109"
|
||||
x="76.000008"
|
||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4761-1-1"
|
||||
height="1.9999945"
|
||||
width="12"
|
||||
y="125"
|
||||
x="76.000008"
|
||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4761-1-1-4"
|
||||
height="1.9999945"
|
||||
width="10"
|
||||
y="137"
|
||||
x="76.000008"
|
||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4761-1-1-4-4"
|
||||
height="1.9999945"
|
||||
width="10"
|
||||
y="129"
|
||||
x="82"
|
||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
id="rect4761-1-1-4-4-3"
|
||||
height="1.9999945"
|
||||
width="9"
|
||||
y="133"
|
||||
x="82"
|
||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 36.398438,100.0254 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,100.5991 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1452 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533865,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550756,0 6.710442,-2.4113 7.650391,-5.9414 0.939949,-3.5301 -0.618463,-7.2736 -3.710938,-9.0703 -1.159678,-0.6738 -2.431087,-1.0231 -3.701171,-1.0625 z"
|
||||
id="path4138" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 59.722656,99.9629 c -1.270084,0.039 -2.541493,0.3887 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5402 -3.710937,9.0703 0.939949,3.5301 4.09768,5.9414 7.648437,5.9414 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4056 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
|
||||
id="path4138-1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 10.5,100 0,2 -2.4999996,0 L 12,107 l 4,-5 -2.5,0 0,-2 -3,0 z"
|
||||
id="path3055-0-77" />
|
||||
<path
|
||||
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.966;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 4.9850574,108.015 14.0298856,-0.03"
|
||||
id="path5244-5-0-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.966;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 4.9849874,132.015 14.0298866,-0.03"
|
||||
id="path5244-5-0-5-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 36.398438,123.9629 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,124.5366 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1453 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533864,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550757,0 6.710442,-2.4093 7.650391,-5.9394 0.939949,-3.5301 -0.618463,-7.2756 -3.710938,-9.0723 -1.159678,-0.6737 -2.431087,-1.0231 -3.701171,-1.0625 z"
|
||||
id="path4138-12" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 59.722656,123.9629 c -1.270084,0.039 -2.541493,0.3888 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5422 -3.710937,9.0723 0.939949,3.5301 4.09768,5.9394 7.648437,5.9394 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4055 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
|
||||
id="path4138-1-3" />
|
||||
<path
|
||||
id="path6191"
|
||||
d="m 10.5,116 0,-2 -2.4999996,0 L 12,109 l 4,5 -2.5,0 0,2 -3,0 z"
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 10.5,129 0,-2 -2.4999996,0 L 12,122 l 4,5 -2.5,0 0,2 -3,0 z"
|
||||
id="path6193" />
|
||||
<path
|
||||
id="path6195"
|
||||
d="m 10.5,135 0,2 -2.4999996,0 L 12,142 l 4,-5 -2.5,0 0,-2 -3,0 z"
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="path4500"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="11.55581"
|
||||
sodipodi:cy="60.073242"
|
||||
sodipodi:r1="5.1116104"
|
||||
sodipodi:r2="2.5558052"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:arg2="1.0471976"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 16.66742,60.073242 -3.833708,2.213392 -3.8337072,2.213393 0,-4.426785 0,-4.426784 3.8337082,2.213392 z"
|
||||
inkscape:transform-center-x="-1.2779026" />
|
||||
<path
|
||||
inkscape:transform-center-x="1.277902"
|
||||
d="m -31.500004,60.073242 -3.833708,2.213392 -3.833707,2.213393 0,-4.426785 0,-4.426784 3.833707,2.213392 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:arg2="1.0471976"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:r2="2.5558052"
|
||||
sodipodi:r1="5.1116104"
|
||||
sodipodi:cy="60.073242"
|
||||
sodipodi:cx="-36.611614"
|
||||
sodipodi:sides="3"
|
||||
id="path4502"
|
||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
sodipodi:type="star"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:arg2="1.0471976"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:r2="2.5558052"
|
||||
sodipodi:r1="5.1116104"
|
||||
sodipodi:cy="60.073212"
|
||||
sodipodi:cx="11.55581"
|
||||
sodipodi:sides="3"
|
||||
id="path4504"
|
||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
sodipodi:type="star"
|
||||
transform="matrix(0,1,-1,0,72.0074,71.7877)"
|
||||
inkscape:transform-center-y="1.2779029" />
|
||||
<path
|
||||
inkscape:transform-center-y="-1.2779026"
|
||||
transform="matrix(0,-1,-1,0,96,96)"
|
||||
sodipodi:type="star"
|
||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="path4506"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="11.55581"
|
||||
sodipodi:cy="60.073212"
|
||||
sodipodi:r1="5.1116104"
|
||||
sodipodi:r2="2.5558052"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:arg2="1.0471976"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4615-5"
|
||||
d="m 171.82574,65.174193 16.34854,0 -8.17427,-13.348454 z"
|
||||
style="fill:#fbb917;fill-opacity:1;fill-rule:evenodd;stroke:#fbb917;stroke-width:1.65161395;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 179,55 0,6 2,0 0,-6"
|
||||
id="path4300"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 179,62 0,2 2,0 0,-2"
|
||||
id="path4300-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
</svg>
|
Before Width: | Height: | Size: 35 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,929 +0,0 @@
|
|||
/* reset styling (prevent conflicts with bootstrap, materialize.css, etc.) */
|
||||
|
||||
div.jsoneditor input {
|
||||
height: auto;
|
||||
border: inherit;
|
||||
}
|
||||
|
||||
div.jsoneditor input:focus {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
div.jsoneditor table {
|
||||
border-collapse: collapse;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div.jsoneditor td,
|
||||
div.jsoneditor th {
|
||||
padding: 0;
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
vertical-align: inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
|
||||
div.jsoneditor-field,
|
||||
div.jsoneditor-value,
|
||||
div.jsoneditor-readonly {
|
||||
border: 1px solid transparent;
|
||||
min-height: 16px;
|
||||
min-width: 32px;
|
||||
padding: 2px;
|
||||
margin: 1px;
|
||||
word-wrap: break-word;
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* adjust margin of p elements inside editable divs, needed for Opera, IE */
|
||||
|
||||
div.jsoneditor-field p,
|
||||
div.jsoneditor-value p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-value {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
div.jsoneditor-readonly {
|
||||
min-width: 16px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
div.jsoneditor-empty {
|
||||
border-color: lightgray;
|
||||
border-style: dashed;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
div.jsoneditor-field.jsoneditor-empty::after,
|
||||
div.jsoneditor-value.jsoneditor-empty::after {
|
||||
pointer-events: none;
|
||||
color: lightgray;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
div.jsoneditor-field.jsoneditor-empty::after {
|
||||
content: "field";
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-empty::after {
|
||||
content: "value";
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-url,
|
||||
a.jsoneditor-value.jsoneditor-url {
|
||||
color: green;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a.jsoneditor-value.jsoneditor-url {
|
||||
display: inline-block;
|
||||
padding: 2px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
a.jsoneditor-value.jsoneditor-url:hover,
|
||||
a.jsoneditor-value.jsoneditor-url:focus {
|
||||
color: #ee422e;
|
||||
}
|
||||
|
||||
div.jsoneditor td.jsoneditor-separator {
|
||||
padding: 3px 0;
|
||||
vertical-align: top;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
div.jsoneditor-field[contenteditable=true]:focus,
|
||||
div.jsoneditor-field[contenteditable=true]:hover,
|
||||
div.jsoneditor-value[contenteditable=true]:focus,
|
||||
div.jsoneditor-value[contenteditable=true]:hover,
|
||||
div.jsoneditor-field.jsoneditor-highlight,
|
||||
div.jsoneditor-value.jsoneditor-highlight {
|
||||
background-color: #FFFFAB;
|
||||
border: 1px solid yellow;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
div.jsoneditor-field.jsoneditor-highlight-active,
|
||||
div.jsoneditor-field.jsoneditor-highlight-active:focus,
|
||||
div.jsoneditor-field.jsoneditor-highlight-active:hover,
|
||||
div.jsoneditor-value.jsoneditor-highlight-active,
|
||||
div.jsoneditor-value.jsoneditor-highlight-active:focus,
|
||||
div.jsoneditor-value.jsoneditor-highlight-active:hover {
|
||||
background-color: #ffee00;
|
||||
border: 1px solid #ffc700;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-string {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-object,
|
||||
div.jsoneditor-value.jsoneditor-array {
|
||||
min-width: 16px;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-number {
|
||||
color: #ee422e;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-boolean {
|
||||
color: #ff8c00;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-null {
|
||||
color: #004ED0;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-invalid {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background: transparent url("img/jsoneditor-icons.svg");
|
||||
}
|
||||
|
||||
div.jsoneditor-mode-view tr.jsoneditor-expandable td.jsoneditor-tree,
|
||||
div.jsoneditor-mode-form tr.jsoneditor-expandable td.jsoneditor-tree {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-collapsed {
|
||||
background-position: 0 -48px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-expanded {
|
||||
background-position: 0 -72px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-contextmenu {
|
||||
background-position: -48px -72px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-contextmenu:hover,
|
||||
div.jsoneditor-tree button.jsoneditor-contextmenu:focus,
|
||||
div.jsoneditor-tree button.jsoneditor-contextmenu.jsoneditor-selected,
|
||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu {
|
||||
background-position: -48px -48px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree *:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button:focus {
|
||||
/* TODO: nice outline for buttons with focus
|
||||
outline: #97B0F8 solid 2px;
|
||||
box-shadow: 0 0 8px #97B0F8;
|
||||
*/
|
||||
background-color: #f5f5f5;
|
||||
outline: #e5e5e5 solid 1px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-invisible {
|
||||
visibility: hidden;
|
||||
background: none;
|
||||
}
|
||||
|
||||
div.jsoneditor {
|
||||
color: #1A1A1A;
|
||||
border: 1px solid #3883fa;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree table.jsoneditor-tree {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-outer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: -35px 0 0 0;
|
||||
padding: 35px 0 0 0;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
textarea.jsoneditor-text,
|
||||
.ace-jsoneditor {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
textarea.jsoneditor-text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
outline-width: 0;
|
||||
border: none;
|
||||
background-color: white;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
tr.jsoneditor-highlight,
|
||||
tr.jsoneditor-selected {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
tr.jsoneditor-selected button.jsoneditor-dragarea,
|
||||
tr.jsoneditor-selected button.jsoneditor-contextmenu {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea,
|
||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-dragarea {
|
||||
background: url("img/jsoneditor-icons.svg") -72px -72px;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-dragarea:hover,
|
||||
div.jsoneditor-tree button.jsoneditor-dragarea:focus,
|
||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea {
|
||||
background-position: -72px -48px;
|
||||
}
|
||||
|
||||
div.jsoneditor tr,
|
||||
div.jsoneditor th,
|
||||
div.jsoneditor td {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
div.jsoneditor td.jsoneditor-tree {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
div.jsoneditor-field,
|
||||
div.jsoneditor-value,
|
||||
div.jsoneditor td,
|
||||
div.jsoneditor th,
|
||||
div.jsoneditor textarea,
|
||||
.jsoneditor-schema-error {
|
||||
font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #1A1A1A;
|
||||
}
|
||||
|
||||
/* popover */
|
||||
|
||||
.jsoneditor-schema-error {
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
/*font-family: arial, sans-serif;*/
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree .jsoneditor-schema-error {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0 4px 0 0;
|
||||
background: url("img/jsoneditor-icons.svg") -168px -48px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover {
|
||||
background-color: #4c4c4c;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.4);
|
||||
color: #fff;
|
||||
display: none;
|
||||
padding: 7px 10px;
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above {
|
||||
bottom: 32px;
|
||||
left: -98px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below {
|
||||
top: 32px;
|
||||
left: -98px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left {
|
||||
top: -7px;
|
||||
right: 32px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right {
|
||||
top: -7px;
|
||||
left: 32px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover:before {
|
||||
border-right: 7px solid transparent;
|
||||
border-left: 7px solid transparent;
|
||||
content: '';
|
||||
display: block;
|
||||
left: 50%;
|
||||
margin-left: -7px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above:before {
|
||||
border-top: 7px solid #4c4c4c;
|
||||
bottom: -7px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below:before {
|
||||
border-bottom: 7px solid #4c4c4c;
|
||||
top: -7px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left:before {
|
||||
border-left: 7px solid #4c4c4c;
|
||||
border-top: 7px solid transparent;
|
||||
border-bottom: 7px solid transparent;
|
||||
content: '';
|
||||
top: 19px;
|
||||
right: -14px;
|
||||
left: inherit;
|
||||
margin-left: inherit;
|
||||
margin-top: -7px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right:before {
|
||||
border-right: 7px solid #4c4c4c;
|
||||
border-top: 7px solid transparent;
|
||||
border-bottom: 7px solid transparent;
|
||||
content: '';
|
||||
top: 19px;
|
||||
left: -14px;
|
||||
margin-left: inherit;
|
||||
margin-top: -7px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error:hover .jsoneditor-popover,
|
||||
.jsoneditor-schema-error:focus .jsoneditor-popover {
|
||||
display: block;
|
||||
-webkit-animation: fade-in .3s linear 1, move-up .3s linear 1;
|
||||
-moz-animation: fade-in .3s linear 1, move-up .3s linear 1;
|
||||
-ms-animation: fade-in .3s linear 1, move-up .3s linear 1;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-ms-keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*@-webkit-keyframes move-up {*/
|
||||
|
||||
/*from { bottom: 24px; }*/
|
||||
|
||||
/*to { bottom: 32px; }*/
|
||||
|
||||
/*}*/
|
||||
|
||||
/*@-moz-keyframes move-up {*/
|
||||
|
||||
/*from { bottom: 24px; }*/
|
||||
|
||||
/*to { bottom: 32px; }*/
|
||||
|
||||
/*}*/
|
||||
|
||||
/*@-ms-keyframes move-up {*/
|
||||
|
||||
/*from { bottom: 24px; }*/
|
||||
|
||||
/*to { bottom: 32px; }*/
|
||||
|
||||
/*}*/
|
||||
|
||||
/* JSON schema errors displayed at the bottom of the editor in mode text and code */
|
||||
|
||||
.jsoneditor .jsoneditor-text-errors {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: #ffef8b;
|
||||
border-top: 1px solid #ffd700;
|
||||
}
|
||||
|
||||
.jsoneditor .jsoneditor-text-errors td {
|
||||
padding: 3px 6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.jsoneditor-text-errors .jsoneditor-schema-error {
|
||||
border: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0 4px 0 0;
|
||||
background: url("img/jsoneditor-icons.svg") -168px -48px;
|
||||
}
|
||||
/* ContextMenu - main menu */
|
||||
|
||||
div.jsoneditor-contextmenu-root {
|
||||
position: relative;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu {
|
||||
position: absolute;
|
||||
box-sizing: content-box;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul,
|
||||
div.jsoneditor-contextmenu li {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul {
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 124px;
|
||||
background: white;
|
||||
border: 1px solid #d3d3d3;
|
||||
box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3);
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 124px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: #4d4d4d;
|
||||
background: transparent;
|
||||
font-size: 10pt;
|
||||
font-family: arial, sans-serif;
|
||||
box-sizing: border-box;
|
||||
line-height: 26px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Fix button padding in firefox */
|
||||
|
||||
div.jsoneditor-contextmenu ul li button::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button:hover,
|
||||
div.jsoneditor-contextmenu ul li button:focus {
|
||||
color: #1a1a1a;
|
||||
background-color: #f5f5f5;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-default {
|
||||
width: 92px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-expand {
|
||||
float: right;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
border-left: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu div.jsoneditor-icon {
|
||||
float: left;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-image: url("img/jsoneditor-icons.svg");
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button div.jsoneditor-expand {
|
||||
float: right;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0 4px 0 0;
|
||||
background: url("img/jsoneditor-icons.svg") 0 -72px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button:hover div.jsoneditor-expand,
|
||||
div.jsoneditor-contextmenu ul li button:focus div.jsoneditor-expand,
|
||||
div.jsoneditor-contextmenu ul li.jsoneditor-selected div.jsoneditor-expand,
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-expand:hover div.jsoneditor-expand,
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-expand:focus div.jsoneditor-expand {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu div.jsoneditor-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
padding-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-remove > div.jsoneditor-icon {
|
||||
background-position: -24px -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-remove:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-remove:focus > div.jsoneditor-icon {
|
||||
background-position: -24px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-append > div.jsoneditor-icon {
|
||||
background-position: 0 -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-append:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-append:focus > div.jsoneditor-icon {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-insert > div.jsoneditor-icon {
|
||||
background-position: 0 -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-insert:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-insert:focus > div.jsoneditor-icon {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-duplicate > div.jsoneditor-icon {
|
||||
background-position: -48px -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-duplicate:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-duplicate:focus > div.jsoneditor-icon {
|
||||
background-position: -48px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-asc > div.jsoneditor-icon {
|
||||
background-position: -168px -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-asc:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-asc:focus > div.jsoneditor-icon {
|
||||
background-position: -168px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-desc > div.jsoneditor-icon {
|
||||
background-position: -192px -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-desc:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-desc:focus > div.jsoneditor-icon {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
/* ContextMenu - sub menu */
|
||||
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-selected,
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover,
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus {
|
||||
color: white;
|
||||
background-color: #ee422e;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li ul {
|
||||
display: none;
|
||||
position: relative;
|
||||
left: -10px;
|
||||
top: 0;
|
||||
border: none;
|
||||
box-shadow: inset 0 0 10px rgba(128, 128, 128, 0.5);
|
||||
padding: 0 10px;
|
||||
/* TODO: transition is not supported on IE8-9 */
|
||||
-webkit-transition: all 0.3s ease-out;
|
||||
-moz-transition: all 0.3s ease-out;
|
||||
-o-transition: all 0.3s ease-out;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
div.jsoneditor-contextmenu ul li ul li button {
|
||||
padding-left: 24px;
|
||||
animation: all ease-in-out 1s;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li ul li button:hover,
|
||||
div.jsoneditor-contextmenu ul li ul li button:focus {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-string > div.jsoneditor-icon {
|
||||
background-position: -144px -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-string:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-string:focus > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-string.jsoneditor-selected > div.jsoneditor-icon {
|
||||
background-position: -144px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-auto > div.jsoneditor-icon {
|
||||
background-position: -120px -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-auto:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-auto:focus > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-auto.jsoneditor-selected > div.jsoneditor-icon {
|
||||
background-position: -120px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-object > div.jsoneditor-icon {
|
||||
background-position: -72px -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-object:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-object:focus > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-object.jsoneditor-selected > div.jsoneditor-icon {
|
||||
background-position: -72px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-array > div.jsoneditor-icon {
|
||||
background-position: -96px -24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-array:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-array:focus > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-array.jsoneditor-selected > div.jsoneditor-icon {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-modes > div.jsoneditor-icon {
|
||||
background-image: none;
|
||||
width: 6px;
|
||||
}
|
||||
div.jsoneditor-menu {
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
padding: 2px;
|
||||
margin: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
background-color: #3883fa;
|
||||
border-bottom: 1px solid #3883fa;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid transparent;
|
||||
background: transparent url("img/jsoneditor-icons.svg");
|
||||
color: white;
|
||||
opacity: 0.8;
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button:hover,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button:hover {
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
border: 1px solid rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button:focus,
|
||||
div.jsoneditor-menu > button:active,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button:focus,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button:active {
|
||||
background-color: rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button:disabled,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-collapse-all {
|
||||
background-position: 0 -96px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-expand-all {
|
||||
background-position: 0 -120px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-undo {
|
||||
background-position: -24px -96px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-undo:disabled {
|
||||
background-position: -24px -120px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-redo {
|
||||
background-position: -48px -96px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-redo:disabled {
|
||||
background-position: -48px -120px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-compact {
|
||||
background-position: -72px -96px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-format {
|
||||
background-position: -72px -120px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > div.jsoneditor-modes {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button {
|
||||
background-image: none;
|
||||
width: auto;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-separator,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button.jsoneditor-separator {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu a {
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
opacity: 0.8;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu a.jsoneditor-poweredBy {
|
||||
font-size: 8pt;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
table.jsoneditor-search input,
|
||||
table.jsoneditor-search div.jsoneditor-results {
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #1A1A1A;
|
||||
background: transparent;
|
||||
/* For Firefox */
|
||||
}
|
||||
|
||||
table.jsoneditor-search div.jsoneditor-results {
|
||||
color: white;
|
||||
padding-right: 5px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
table.jsoneditor-search div.jsoneditor-frame {
|
||||
border: 1px solid transparent;
|
||||
background-color: white;
|
||||
padding: 0 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
table.jsoneditor-search div.jsoneditor-frame table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.jsoneditor-search input {
|
||||
width: 120px;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin: 1px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button {
|
||||
width: 16px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: url("img/jsoneditor-icons.svg");
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button.jsoneditor-refresh {
|
||||
width: 18px;
|
||||
background-position: -99px -73px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button.jsoneditor-next {
|
||||
cursor: pointer;
|
||||
background-position: -124px -73px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button.jsoneditor-next:hover {
|
||||
background-position: -124px -49px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button.jsoneditor-previous {
|
||||
cursor: pointer;
|
||||
background-position: -148px -73px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button.jsoneditor-previous:hover {
|
||||
background-position: -148px -49px;
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,41 +0,0 @@
|
|||
# Which files do I need?
|
||||
|
||||
Ehhh, that's quite some files in this dist folder. Which files do I need?
|
||||
|
||||
|
||||
## Full version
|
||||
|
||||
If you're not sure which version to use, use the full version.
|
||||
|
||||
Which files are needed when using the full version?
|
||||
|
||||
- jsoneditor.min.js
|
||||
- jsoneditor.map (optional, for debugging purposes only)
|
||||
- jsoneditor.min.css
|
||||
- img/jsoneditor-icons.svg
|
||||
|
||||
|
||||
## Minimalist version
|
||||
|
||||
The minimalist version has excluded the following libraries:
|
||||
|
||||
- `ace` (via `brace`), used for the code editor.
|
||||
- `ajv`, used for JSON schema validation.
|
||||
|
||||
This reduces the the size of the minified and gzipped JavaScript file from
|
||||
about 160 kB to about 40 kB.
|
||||
|
||||
When to use the minimalist version?
|
||||
|
||||
- If you don't need the mode "code" and don't need JSON schema validation.
|
||||
- Or if you want to provide `ace` and/or `ajv` yourself via the configuration
|
||||
options, for example when you already use Ace in other parts of your
|
||||
web application too and don't want to bundle the library twice.
|
||||
|
||||
Which files are needed when using the minimalist version?
|
||||
|
||||
- jsoneditor-minimalist.min.js
|
||||
- jsoneditor-minimalist.map (optional, for debugging purposes only)
|
||||
- jsoneditor.min.css
|
||||
- img/jsoneditor-icons.svg
|
||||
|
75
docs/api.md
75
docs/api.md
|
@ -2,9 +2,9 @@
|
|||
|
||||
## JSONEditor
|
||||
|
||||
### Constructor
|
||||
### Construction
|
||||
|
||||
#### `JSONEditor(container [, options [, json]])`
|
||||
#### `jsoneditor(container [, options])`
|
||||
|
||||
Constructs a new JSONEditor.
|
||||
|
||||
|
@ -19,13 +19,9 @@ Constructs a new JSONEditor.
|
|||
Optional object with options. The available options are described under
|
||||
[Configuration options](#configuration-options).
|
||||
|
||||
- `{JSON} json`
|
||||
|
||||
Initial JSON data to be loaded into the JSONEditor. Alternatively, the method `JSONEditor.set(json)` can be used to load JSON data into the editor.
|
||||
|
||||
*Returns:*
|
||||
|
||||
- `{JSONEditor} editor`
|
||||
- `{Object} editor`
|
||||
|
||||
New instance of a JSONEditor.
|
||||
|
||||
|
@ -41,7 +37,7 @@ Constructs a new JSONEditor.
|
|||
library used for JSON schema validation. Example:
|
||||
|
||||
```js
|
||||
var options = {
|
||||
const options = {
|
||||
ajv: Ajv({ allErrors: true, verbose: true })
|
||||
}
|
||||
```
|
||||
|
@ -76,6 +72,25 @@ Constructs a new JSONEditor.
|
|||
|
||||
Enables history, adds a button Undo and Redo to the menu of the JSONEditor. True by default. Only applicable when `mode` is 'tree' or 'form'.
|
||||
|
||||
- `{Object<String, String[]>} keyBindings`
|
||||
|
||||
Override default key bindings. For example to replace the binding to duplicate a node from `Ctrl+D` to `Ctrl+Shift+D`:
|
||||
|
||||
```js
|
||||
const options = {
|
||||
keyBindings: {
|
||||
duplicate: ['Ctrl+Shift+D', 'Command+Shift+D']
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It's important to define bindings for both Windows and Mac. The meta keys on Windows are `Ctrl`, `Shift`, `Alt`, and on Mac they are respectively `Command`, `Shift`, and `Option`.
|
||||
|
||||
All available key bindings are described on the page [Key Bindings](#key_bindings.md).
|
||||
|
||||
Key bindings are case insensitive.
|
||||
|
||||
|
||||
- `{String} mode`
|
||||
|
||||
Set the editor mode. Available values: 'tree' (default), 'view', 'form', 'code', 'text'. In 'view' mode, the data and datastructure is read-only. In 'form' mode, only the value can be changed, the datastructure is read-only. Mode 'code' requires the Ace editor to be loaded on the page. Mode 'text' shows the data as plain text.
|
||||
|
@ -233,44 +248,44 @@ Get JSON data as string.
|
|||
A tree editor:
|
||||
|
||||
```js
|
||||
var options = {
|
||||
"mode": "tree",
|
||||
"search": true
|
||||
};
|
||||
var editor = new JSONEditor(container, options);
|
||||
var json = {
|
||||
const options = {
|
||||
mode: 'tree',
|
||||
search: true
|
||||
}
|
||||
const editor = new JSONEditor(container, options)
|
||||
let json = {
|
||||
"Array": [1, 2, 3],
|
||||
"Boolean": true,
|
||||
"Null": null,
|
||||
"Number": 123,
|
||||
"Object": {"a": "b", "c": "d"},
|
||||
"String": "Hello World"
|
||||
};
|
||||
editor.set(json);
|
||||
editor.expandAll();
|
||||
}
|
||||
editor.set(json)
|
||||
editor.expandAll()
|
||||
|
||||
var json = editor.get(json);
|
||||
json = editor.get(json)
|
||||
```
|
||||
|
||||
A text editor:
|
||||
|
||||
```js
|
||||
var options = {
|
||||
"mode": "text",
|
||||
"indentation": 2
|
||||
};
|
||||
var editor = new JSONEditor(container, options);
|
||||
var json = {
|
||||
const options = {
|
||||
mode: 'text',
|
||||
indentation: 2
|
||||
}
|
||||
const editor = new JSONEditor(container, options)
|
||||
let json = {
|
||||
"Array": [1, 2, 3],
|
||||
"Boolean": true,
|
||||
"Null": null,
|
||||
"Number": 123,
|
||||
"Object": {"a": "b", "c": "d"},
|
||||
"String": "Hello World"
|
||||
};
|
||||
editor.set(json);
|
||||
}
|
||||
editor.set(json)
|
||||
|
||||
var json = editor.get();
|
||||
json = editor.get()
|
||||
```
|
||||
|
||||
## JSON parsing and stringification
|
||||
|
@ -278,17 +293,17 @@ var json = editor.get();
|
|||
In general to parse or stringify JSON data, the browsers built in JSON parser can be used. To create a formatted string from a JSON object, use:
|
||||
|
||||
```js
|
||||
var formattedString = JSON.stringify(json, null, 2);
|
||||
const formattedString = JSON.stringify(json, null, 2)
|
||||
```
|
||||
|
||||
to create a compacted string from a JSON object, use:
|
||||
|
||||
```js
|
||||
var compactString = JSON.stringify(json);
|
||||
const compactString = JSON.stringify(json)
|
||||
```
|
||||
|
||||
To parse a String to a JSON object, use:
|
||||
|
||||
```js
|
||||
var json = JSON.parse(string);
|
||||
const json = JSON.parse(string)
|
||||
```
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# Key bindings
|
||||
|
||||
## Tree Editor
|
||||
|
||||
Name | Windows key combination | Mac key combination | Description
|
||||
----------------------------- | ----------------------- | -------------------------- | ------------------------------------------------
|
||||
`up`, `down`, `left`, `right` | Alt+Arrows | Option+Arrows | Move the caret up/down/left/right between fields
|
||||
`duplicate` | Ctrl+D | Command+D | Duplicate field
|
||||
`remove` | Ctrl+Del | Command+Del | Remove field
|
||||
`openUrl` | Ctrl+Enter | Command+Enter | Open link when on a field containing an url
|
||||
`insert` | Ctrl+Ins | Command+Ins | Insert a new field with type auto
|
||||
`expand` | Ctrl+E | Command+E | Expand or collapse field
|
||||
`end` | Alt+End | Option+End | Move the caret to the last field
|
||||
`find` | Ctrl+F | Command+F | Find
|
||||
`findNext` | F3, Ctrl+G | F3, Command+G | Find next
|
||||
`findPrevious` | Shift+F3, Ctrl+Shift+G | Shift+F3, Command+Shift+G | Find previous
|
||||
`home` | Alt+Home | Option+Home | Move the caret to the first field
|
||||
`actionMenu` | Ctrl+M | Command+M | Show actions menu
|
||||
`undo` | Ctrl+Z | Command+Z | Undo last action
|
||||
`redo` | Ctrl+Shift+Z | Command+Shift+Z | Redo
|
||||
|
||||
|
||||
## Code Editor
|
||||
|
||||
The code editor is powered by [Ace Editor](http://ace.c9.io/). This editor's
|
||||
shortcut keys are described here:
|
||||
|
||||
https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts
|
||||
|
||||
Additionally, there are shortcut keys to format/compact the code:
|
||||
|
||||
Name | Windows key combination | Mac key combination | Description
|
||||
--------- | ----------------------- | ------------------- | ------------------------------------------------
|
||||
`format` | Ctrl+\ | Command+\ | Format JSON data, set proper indentation
|
||||
`compact` | Ctrl+Shift+\ | Command+Shift+\ | Compact JSON data, remove all whitespace
|
|
@ -1,38 +1,3 @@
|
|||
# Shortcut keys
|
||||
|
||||
## Tree Editor
|
||||
|
||||
Key | Description
|
||||
----------------------- | ------------------------------------------------
|
||||
Alt+Arrows | Move the caret up/down/left/right between fields
|
||||
Ctrl+Shift+Arrow Up/Down| Select multiple fields
|
||||
Shift+Alt+Arrows | Move current field or selected fields up/down/left/right
|
||||
Ctrl+D | Duplicate field
|
||||
Ctrl+Del | Remove field
|
||||
Ctrl+Enter | Open link when on a field containing an url
|
||||
Ctrl+Ins | Insert a new field with type auto
|
||||
Ctrl+Shift+Ins | Append a new field with type auto
|
||||
Ctrl+E | Expand or collapse field
|
||||
Alt+End | Move the caret to the last field
|
||||
Ctrl+F | Find
|
||||
F3, Ctrl+G | Find next
|
||||
Shift+F3, Ctrl+Shift+G | Find previous
|
||||
Alt+Home | Move the caret to the first field
|
||||
Ctrl+M | Show actions menu
|
||||
Ctrl+Z | Undo last action
|
||||
Ctrl+Shift+Z | Redo
|
||||
|
||||
|
||||
## Code Editor
|
||||
|
||||
The code editor is powered by [Ace Editor](http://ace.c9.io/). This editor's
|
||||
shortcut keys are described here:
|
||||
|
||||
https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts
|
||||
|
||||
Additionally, there are shortcut keys to format/compact the code:
|
||||
|
||||
Key | Description
|
||||
----------------------- | ------------------------------------------------
|
||||
Ctrl+\ | Format JSON data, set proper indentation
|
||||
Ctrl+Shift+\ | Compact JSON data, remove all whitespace
|
||||
This page has been renamed to [Key Bindings](#key_bindings.md).
|
||||
|
|
|
@ -2,29 +2,17 @@
|
|||
|
||||
### Install
|
||||
|
||||
with npm:
|
||||
using npm:
|
||||
|
||||
npm install jsoneditor
|
||||
|
||||
with bower:
|
||||
|
||||
bower install jsoneditor
|
||||
|
||||
download:
|
||||
|
||||
[http://jsoneditoronline.org/downloads/](http://jsoneditoronline.org/downloads/)
|
||||
|
||||
The library consists of three files: one javascript file, one css file and an
|
||||
image. Both full and minified version are available.
|
||||
|
||||
## Load
|
||||
|
||||
To implement JSONEditor in a web application, load the javascript and css file
|
||||
To implement JSONEditor in a web application, load the javascript file
|
||||
in the head of the HTML page:
|
||||
|
||||
```html
|
||||
<link href="jsoneditor/dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
|
||||
<script src="jsoneditor/dist/jsoneditor.min.js"></script>
|
||||
```html\
|
||||
<script src="jsoneditor/dist/jsoneditor.js"></script>
|
||||
```
|
||||
|
||||
## Use
|
||||
|
@ -38,11 +26,11 @@ In the body, create an div element with an id and a size:
|
|||
After the page is loaded, load the editor with javascript:
|
||||
|
||||
```js
|
||||
var container = document.getElementById("jsoneditor");
|
||||
var container = document.getElementById("jsoneditor")
|
||||
var options = {
|
||||
mode: 'tree'
|
||||
};
|
||||
var editor = new JSONEditor(container, options);
|
||||
}
|
||||
var editor = jsoneditor(container, options)
|
||||
```
|
||||
|
||||
To set JSON data in the editor:
|
||||
|
@ -55,14 +43,14 @@ var json = {
|
|||
"Number": 123,
|
||||
"Object": {"a": "b", "c": "d"},
|
||||
"String": "Hello World"
|
||||
};
|
||||
editor.set(json);
|
||||
}
|
||||
editor.set(json)
|
||||
```
|
||||
|
||||
To get JSON data from the editor:
|
||||
|
||||
```js
|
||||
var json = editor.get();
|
||||
var json = editor.get()
|
||||
```
|
||||
|
||||
|
||||
|
@ -75,20 +63,19 @@ var json = editor.get();
|
|||
<!-- when using the mode "code", it's important to specify charset utf-8 -->
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<link href="jsoneditor/dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
|
||||
<script src="jsoneditor/dist/jsoneditor.min.js"></script>
|
||||
<script src="jsoneditor/dist/jsoneditor.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<button onclick="setJSON();">Set JSON</button>
|
||||
<button onclick="getJSON();">Get JSON</button>
|
||||
<button onclick="setJSON()">Set JSON</button>
|
||||
<button onclick="getJSON()">Get JSON</button>
|
||||
</p>
|
||||
<div id="jsoneditor" style="width: 400px; height: 400px;"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
var container = document.getElementById("jsoneditor");
|
||||
var editor = new JSONEditor(container);
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var editor = jsoneditor(container)
|
||||
|
||||
// set json
|
||||
function setJSON () {
|
||||
|
@ -99,14 +86,14 @@ var json = editor.get();
|
|||
"Number": 123,
|
||||
"Object": {"a": "b", "c": "d"},
|
||||
"String": "Hello World"
|
||||
};
|
||||
editor.set(json);
|
||||
}
|
||||
editor.set(json)
|
||||
}
|
||||
|
||||
// get json
|
||||
function getJSON() {
|
||||
var json = editor.get();
|
||||
alert(JSON.stringify(json, null, 2));
|
||||
var json = editor.get()
|
||||
alert(JSON.stringify(json, null, 2))
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Basic usage</title>
|
||||
<meta charset="UTF-8">
|
||||
<title>Basic usage | JSONEditor</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
|
@ -22,9 +22,9 @@
|
|||
|
||||
<script>
|
||||
// create the editor
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var options = {};
|
||||
var editor = new JSONEditor(container, options);
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var options = {}
|
||||
var editor = jsoneditor(container, options)
|
||||
|
||||
// set json
|
||||
document.getElementById('setJSON').onclick = function () {
|
||||
|
@ -35,15 +35,15 @@
|
|||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
};
|
||||
editor.set(json);
|
||||
};
|
||||
}
|
||||
editor.set(json)
|
||||
}
|
||||
|
||||
// get json
|
||||
document.getElementById('getJSON').onclick = function () {
|
||||
var json = editor.get();
|
||||
alert(JSON.stringify(json, null, 2));
|
||||
};
|
||||
var json = editor.get()
|
||||
alert(JSON.stringify(json, null, 2))
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Viewer</title>
|
||||
<meta charset="UTF-8">
|
||||
<title>Viewer | JSONEditor</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
|
||||
|
@ -18,16 +18,18 @@
|
|||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This editor is read-only (mode='viewer').
|
||||
This editor is read-only (mode='view').
|
||||
</p>
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var container = document.getElementById('jsoneditor')
|
||||
|
||||
var options = {
|
||||
mode: 'view'
|
||||
};
|
||||
}
|
||||
|
||||
var editor = jsoneditor(container, options)
|
||||
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
|
@ -36,9 +38,9 @@
|
|||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
};
|
||||
}
|
||||
editor.set(json)
|
||||
|
||||
var editor = new JSONEditor(container, options, json);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Switch mode</title>
|
||||
<title>Switch mode | JSONEditor</title>
|
||||
|
||||
<!-- when using the mode "code", it's important to specify charset utf-8 -->
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
|
@ -29,6 +28,7 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Switch mode</h1>
|
||||
<p>
|
||||
Switch editor mode using the mode box.
|
||||
Note that the mode can be changed programmatically as well using the method
|
||||
|
@ -38,18 +38,20 @@
|
|||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var container = document.getElementById('jsoneditor')
|
||||
|
||||
var options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
|
||||
modes: ['text', 'code', 'tree', 'form', 'view'], // allowed modes
|
||||
onError: function (err) {
|
||||
alert(err.toString());
|
||||
alert(err.toString())
|
||||
},
|
||||
onModeChange: function (newMode, oldMode) {
|
||||
console.log('Mode switched from', oldMode, 'to', newMode);
|
||||
onChangeMode: function (mode, prevMode) {
|
||||
console.log('Mode switched from', prevMode, 'to', mode)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var editor = jsoneditor(container, options)
|
||||
|
||||
var json = {
|
||||
"array": [1, 2, 3],
|
||||
|
@ -58,9 +60,9 @@
|
|||
"number": 123,
|
||||
"object": {"a": "b", "c": "d"},
|
||||
"string": "Hello World"
|
||||
};
|
||||
}
|
||||
|
||||
var editor = new JSONEditor(container, options, json);
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Load and save</title>
|
||||
<meta charset="UTF-8">
|
||||
<title>Load and save | JSONEditor</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<script src="https://bgrins.github.io/filereader.js/filereader.js"></script>
|
||||
|
@ -38,36 +38,36 @@
|
|||
|
||||
<script>
|
||||
// create the editor
|
||||
var editor = new JSONEditor(document.getElementById('jsoneditor'));
|
||||
var editor = jsoneditor(document.getElementById('jsoneditor'))
|
||||
|
||||
// Load a JSON document
|
||||
FileReaderJS.setupInput(document.getElementById('loadDocument'), {
|
||||
readAsDefault: 'Text',
|
||||
on: {
|
||||
load: function (event, file) {
|
||||
editor.setText(event.target.result);
|
||||
editor.setText(event.target.result)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Save a JSON document
|
||||
document.getElementById('saveDocument').onclick = function () {
|
||||
// Save Dialog
|
||||
fname = window.prompt("Save as...");
|
||||
fname = window.prompt('Save as...')
|
||||
|
||||
// Check json extension in file name
|
||||
if(fname.indexOf(".")==-1){
|
||||
fname = fname + ".json";
|
||||
}else{
|
||||
if(fname.split('.').pop().toLowerCase() == "json"){
|
||||
if (fname.indexOf('.') == -1) {
|
||||
fname = fname + '.json'
|
||||
} else {
|
||||
if (fname.split('.').pop().toLowerCase() == 'json'){
|
||||
// Nothing to do
|
||||
}else{
|
||||
fname = fname.split('.')[0] + ".json";
|
||||
} else {
|
||||
fname = fname.split('.')[0] + '.json'
|
||||
}
|
||||
}
|
||||
var blob = new Blob([editor.getText()], {type: 'application/json;charset=utf-8'});
|
||||
saveAs(blob, fname);
|
||||
};
|
||||
var blob = new Blob([editor.getText()], {type: 'application/json;charset=utf-8'})
|
||||
saveAs(blob, fname)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Custom editable fields</title>
|
||||
<meta charset="UTF-8">
|
||||
<title>Read-only properties and values | JSONEditor</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Read-only properties and values</h1>
|
||||
<p>
|
||||
In this example:
|
||||
</p>
|
||||
|
@ -28,36 +29,33 @@
|
|||
var container = document.getElementById('jsoneditor');
|
||||
|
||||
var options = {
|
||||
onEditable: function (node) {
|
||||
// node is an object like:
|
||||
// {
|
||||
// field: 'FIELD',
|
||||
// value: 'VALUE',
|
||||
// path: ['PATH', 'TO', 'NODE']
|
||||
// }
|
||||
switch (node.field) {
|
||||
case '_id':
|
||||
return false;
|
||||
|
||||
case 'name':
|
||||
return {
|
||||
field: false,
|
||||
value: true
|
||||
};
|
||||
|
||||
default:
|
||||
return true;
|
||||
// all properties are editable except '_id' and 'name'
|
||||
isPropertyEditable: function (path) {
|
||||
if (path.length === 1 && path[0] === '_id' || path[0] === 'name') {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
// all values are editable except '_id'
|
||||
isValueEditable: function (path) {
|
||||
if (path.length === 1 && path[0] === '_id') {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var json = {
|
||||
_id: 123456,
|
||||
name: 'John',
|
||||
age: 32
|
||||
};
|
||||
}
|
||||
|
||||
var editor = new JSONEditor(container, options, json);
|
||||
var editor = jsoneditor(container, options)
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Custom styling</title>
|
||||
<meta charset="UTF-8">
|
||||
<title>Custom styling | JSONEditor</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
|
@ -22,6 +22,8 @@
|
|||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Custom styling</h1>
|
||||
<p>
|
||||
This example demonstrates how to customize the look of JSONEditor,
|
||||
the editor below has a dark theme. Note that the example isn't worked
|
||||
|
@ -33,10 +35,11 @@
|
|||
|
||||
<script>
|
||||
// create the editor
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var options = {
|
||||
var editor = jsoneditor(document.getElementById('jsoneditor'), {
|
||||
modes: ['text', 'tree']
|
||||
};
|
||||
})
|
||||
|
||||
// load json
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
|
@ -44,8 +47,8 @@
|
|||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
};
|
||||
var editor = new JSONEditor(container, options, json);
|
||||
}
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | JSON schema validation</title>
|
||||
<meta charset="UTF-8">
|
||||
<title>JSON schema validation | JSONEditor</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
|
@ -50,22 +50,24 @@
|
|||
}
|
||||
},
|
||||
"required": ["firstName", "lastName"]
|
||||
};
|
||||
}
|
||||
|
||||
var json = {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
gender: null,
|
||||
age: 28
|
||||
};
|
||||
}
|
||||
|
||||
var options = {
|
||||
schema: schema
|
||||
};
|
||||
modes: ['code', 'tree']
|
||||
}
|
||||
|
||||
// create the editor
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var editor = new JSONEditor(container, options, json);
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var editor = jsoneditor(container, options)
|
||||
editor.setSchema(schema)
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Custom Ace Editor | JSONEditor</title>
|
||||
|
||||
<!-- we use the minimalist jsoneditor, which doesn't have Ace Editor included -->
|
||||
<script src="../dist/jsoneditor-minimalist.js"></script>
|
||||
|
||||
<!-- load your own instance of Ace Editor and all plugins that you need -->
|
||||
<!-- jsoneditor requires ext-searchbox and mode-json -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ace.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ext-searchbox.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/mode-json.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/theme-twilight.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Custom Ace Editor</h1>
|
||||
<p>
|
||||
In this example, the we use the minimalist version of jsoneditor and load
|
||||
and configure Ace editor our selves: we set a different theme and font size.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
// create the editor, set mode to 'code' (powered by ace editor)
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var options = {
|
||||
mode: 'code',
|
||||
onLoadAce: function (aceEditor, container) {
|
||||
// we can adjust configuration of the ace editor,
|
||||
// or create a completely new instance of ace editor.
|
||||
|
||||
// let's set a custom theme and font size
|
||||
aceEditor.setTheme('ace/theme/twilight')
|
||||
aceEditor.setFontSize(16)
|
||||
|
||||
return aceEditor
|
||||
}
|
||||
}
|
||||
var editor = jsoneditor(container, options)
|
||||
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Synchronize two editors | JSONEditor</title>
|
||||
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: arial, sans-serif;
|
||||
}
|
||||
|
||||
#jsoneditor1,
|
||||
#jsoneditor2 {
|
||||
display: inline-block;
|
||||
width: 350px;
|
||||
height: 500px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #e5e5e5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Synchronize two editors</h1>
|
||||
<p>
|
||||
This example demonstrates how to keep two editors synchronized by listening for
|
||||
the <code>onPatch</code> event. Other related events are <code>onChange</code> and <code>onChangeText</code>.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor1"></div>
|
||||
<div id="jsoneditor2"></div>
|
||||
|
||||
<script>
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
|
||||
var editor1 = jsoneditor(document.getElementById('jsoneditor1'), {
|
||||
onPatch: function (patch, revert) {
|
||||
editor2.patch(patch)
|
||||
}
|
||||
})
|
||||
editor1.set(json)
|
||||
|
||||
var editor2 = jsoneditor(document.getElementById('jsoneditor2'), {
|
||||
onPatch: function (patch, revert) {
|
||||
editor1.patch(patch)
|
||||
}
|
||||
})
|
||||
editor2.set(json)
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Custom key bindings | JSONEditor</title>
|
||||
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 120%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Custom key bindings</h1>
|
||||
<p>
|
||||
In this example, the key bindings for <code>format</code> and <code>compact</code> are changed to respectively <code>Ctrl+Alt+1</code> and <code>Ctrl+Alt+2</code> instead of the default <code>Ctrl+\</code> and <code>Ctrl+Shift+\</code>.
|
||||
</p>
|
||||
<p>
|
||||
It's important to define key bindings for both Windows and Mac: <code>Command+Option+1</code> and <code>Command+Option+2</code> in this case.
|
||||
</p>
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var options = {
|
||||
modes: ['code', 'text'],
|
||||
keyBindings: {
|
||||
compact: ['Ctrl+Alt+1', 'Command+Option+1'],
|
||||
format: ['Ctrl+Alt+2', 'Command+Option+2']
|
||||
}
|
||||
}
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
var editor = jsoneditor(container, options)
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -6,22 +6,31 @@ div.jsoneditor-menu {
|
|||
div.jsoneditor-menu {
|
||||
background-color: #4b4b4b;
|
||||
}
|
||||
div.jsoneditor-tree,
|
||||
div.jsoneditor textarea.jsoneditor-text {
|
||||
div.jsoneditor-tree-contents,
|
||||
textarea.jsoneditor-text {
|
||||
background-color: #666666;
|
||||
color: #ffffff;
|
||||
}
|
||||
div.jsoneditor-field,
|
||||
|
||||
.jsoneditor-menu button.jsoneditor-undo:disabled {
|
||||
background-position: -24px -96px;
|
||||
}
|
||||
.jsoneditor-menu button.jsoneditor-redo:disabled {
|
||||
background-position: -48px -96px;
|
||||
}
|
||||
|
||||
div.jsoneditor-property,
|
||||
div.jsoneditor-value {
|
||||
color: #ffffff;
|
||||
}
|
||||
table.jsoneditor-search div.jsoneditor-frame {
|
||||
background: #808080;
|
||||
|
||||
.jsoneditor-property:hover,
|
||||
.jsoneditor-value:hover {
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
tr.jsoneditor-highlight,
|
||||
tr.jsoneditor-selected {
|
||||
background-color: #808080;
|
||||
.jsoneditor-readonly:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
div.jsoneditor-field[contenteditable=true]:focus,
|
||||
|
@ -31,21 +40,17 @@ div.jsoneditor-value[contenteditable=true]:hover,
|
|||
div.jsoneditor-field.jsoneditor-highlight,
|
||||
div.jsoneditor-value.jsoneditor-highlight {
|
||||
background-color: #808080;
|
||||
border-color: #808080;
|
||||
}
|
||||
|
||||
div.jsoneditor-field.highlight-active,
|
||||
div.jsoneditor-field.highlight-active:focus,
|
||||
div.jsoneditor-field.highlight-active:hover,
|
||||
div.jsoneditor-value.highlight-active,
|
||||
div.jsoneditor-value.highlight-active:focus,
|
||||
div.jsoneditor-value.highlight-active:hover {
|
||||
background-color: #b1b1b1;
|
||||
border-color: #b1b1b1;
|
||||
button.jsoneditor-button.jsoneditor-actionmenu {
|
||||
background-position: -50px -50px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button:focus {
|
||||
background-color: #868686;
|
||||
button.jsoneditor-button:hover,
|
||||
button.jsoneditor-button:focus,
|
||||
button.jsoneditor-button:active {
|
||||
background-color: #808080;
|
||||
outline: #808080 solid 1px;
|
||||
}
|
||||
|
||||
/* coloring of JSON in tree mode */
|
||||
|
@ -56,7 +61,7 @@ div.jsoneditor td.jsoneditor-separator {
|
|||
color: #acacac;
|
||||
}
|
||||
div.jsoneditor-value.jsoneditor-string {
|
||||
color: #00ff88;
|
||||
color: #8bb54b;
|
||||
}
|
||||
div.jsoneditor-value.jsoneditor-object,
|
||||
div.jsoneditor-value.jsoneditor-array {
|
||||
|
@ -69,7 +74,7 @@ div.jsoneditor-value.jsoneditor-boolean {
|
|||
color: #ff8048;
|
||||
}
|
||||
div.jsoneditor-value.jsoneditor-null {
|
||||
color: #49a7fc;
|
||||
color: #3B8EEA;
|
||||
}
|
||||
div.jsoneditor-value.jsoneditor-invalid {
|
||||
color: white;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
# production
|
||||
build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env
|
||||
npm-debug.log
|
||||
.vscode
|
||||
.idea
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "react_demo",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"react-scripts": "1.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsoneditor": "file:../..",
|
||||
"react": "16.4.2",
|
||||
"react-dom": "16.4.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>React demo | JSONEditor</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
.app {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
.app h1 {
|
||||
font-size: 120%;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import React, { Component } from 'react'
|
||||
import './App.css'
|
||||
|
||||
// Load the react version of JSONEditor
|
||||
import JSONEditor from 'jsoneditor/react'
|
||||
|
||||
const json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
json
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="app">
|
||||
<h1>JSONEditor React demo</h1>
|
||||
<JSONEditor
|
||||
mode="tree"
|
||||
modes={['text', 'code', 'tree', 'form', 'view']}
|
||||
json={this.state.json}
|
||||
onChange={this.onChange}
|
||||
onChangeText={this.onChangeText}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
onChange = (json) => {
|
||||
console.log('onChange', json)
|
||||
}
|
||||
|
||||
onChangeText = (text) => {
|
||||
console.log('onChangeText', text)
|
||||
|
||||
this.setState({ text })
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
|
@ -0,0 +1,5 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
)
|
|
@ -8,8 +8,7 @@
|
|||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="../../dist/jsoneditor.css">
|
||||
<script data-main="scripts/main" src="scripts/require.js"></script>
|
||||
<script data-main="scripts/main" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.2/require.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
var module = '../../../dist/jsoneditor';
|
||||
require([module], function (JSONEditor) {
|
||||
var module = '../../../dist/jsoneditor'
|
||||
|
||||
require([module], function (jsoneditor) {
|
||||
// create the editor
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var editor = new JSONEditor(container);
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var editor = jsoneditor(container)
|
||||
|
||||
// set json
|
||||
document.getElementById('setJSON').onclick = function () {
|
||||
|
@ -13,13 +14,13 @@ require([module], function (JSONEditor) {
|
|||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
};
|
||||
editor.set(json);
|
||||
};
|
||||
}
|
||||
editor.set(json)
|
||||
}
|
||||
|
||||
// get json
|
||||
document.getElementById('getJSON').onclick = function () {
|
||||
var json = editor.get();
|
||||
alert(JSON.stringify(json, null, 2));
|
||||
};
|
||||
});
|
||||
var json = editor.get()
|
||||
alert(JSON.stringify(json, null, 2))
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
RequireJS 2.1.13 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
|
||||
Available via the MIT or new BSD license.
|
||||
see: http://github.com/jrburke/requirejs for details
|
||||
*/
|
||||
var requirejs,require,define;
|
||||
(function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function T(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function t(b,c){return fa.call(b,c)}function m(b,c){return t(b,c)&&b[c]}function B(b,c){for(var d in b)if(t(b,d)&&c(b[d],d))break}function U(b,c,d,e){c&&B(c,function(c,g){if(d||!t(b,g))e&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof
|
||||
RegExp)?(b[g]||(b[g]={}),U(b[g],c,d,e)):b[g]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function ca(b){throw b;}function da(b){if(!b)return b;var c=ba;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,e){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=e;d&&(c.originalError=d);return c}function ga(b){function c(a,k,b){var f,l,c,d,e,g,i,p,k=k&&k.split("/"),h=j.map,n=h&&h["*"];if(a){a=a.split("/");l=a.length-1;j.nodeIdCompat&&
|
||||
Q.test(a[l])&&(a[l]=a[l].replace(Q,""));"."===a[0].charAt(0)&&k&&(l=k.slice(0,k.length-1),a=l.concat(a));l=a;for(c=0;c<l.length;c++)if(d=l[c],"."===d)l.splice(c,1),c-=1;else if(".."===d&&!(0===c||1==c&&".."===l[2]||".."===l[c-1])&&0<c)l.splice(c-1,2),c-=2;a=a.join("/")}if(b&&h&&(k||n)){l=a.split("/");c=l.length;a:for(;0<c;c-=1){e=l.slice(0,c).join("/");if(k)for(d=k.length;0<d;d-=1)if(b=m(h,k.slice(0,d).join("/")))if(b=m(b,e)){f=b;g=c;break a}!i&&(n&&m(n,e))&&(i=m(n,e),p=c)}!f&&i&&(f=i,g=p);f&&(l.splice(0,
|
||||
g,f),a=l.join("/"))}return(f=m(j.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(k){if(k.getAttribute("data-requiremodule")===a&&k.getAttribute("data-requirecontext")===i.contextName)return k.parentNode.removeChild(k),!0})}function e(a){var k=m(j.paths,a);if(k&&H(k)&&1<k.length)return k.shift(),i.require.undef(a),i.makeRequire(null,{skipMap:!0})([a]),!0}function n(a){var k,c=a?a.indexOf("!"):-1;-1<c&&(k=a.substring(0,c),a=a.substring(c+1,a.length));return[k,a]}function p(a,
|
||||
k,b,f){var l,d,e=null,g=k?k.name:null,j=a,p=!0,h="";a||(p=!1,a="_@r"+(K+=1));a=n(a);e=a[0];a=a[1];e&&(e=c(e,g,f),d=m(r,e));a&&(e?h=d&&d.normalize?d.normalize(a,function(a){return c(a,g,f)}):c(a,g,f):(h=c(a,g,f),a=n(h),e=a[0],h=a[1],b=!0,l=i.nameToUrl(h)));b=e&&!d&&!b?"_unnormalized"+(O+=1):"";return{prefix:e,name:h,parentMap:k,unnormalized:!!b,url:l,originalName:j,isDefine:p,id:(e?e+"!"+h:h)+b}}function s(a){var k=a.id,b=m(h,k);b||(b=h[k]=new i.Module(a));return b}function q(a,k,b){var f=a.id,c=m(h,
|
||||
f);if(t(r,f)&&(!c||c.defineEmitComplete))"defined"===k&&b(r[f]);else if(c=s(a),c.error&&"error"===k)b(c.error);else c.on(k,b)}function w(a,b){var c=a.requireModules,f=!1;if(b)b(a);else if(v(c,function(b){if(b=m(h,b))b.error=a,b.events.error&&(f=!0,b.emit("error",a))}),!f)g.onError(a)}function x(){R.length&&(ha.apply(A,[A.length,0].concat(R)),R=[])}function y(a){delete h[a];delete V[a]}function F(a,b,c){var f=a.map.id;a.error?a.emit("error",a.error):(b[f]=!0,v(a.depMaps,function(f,d){var e=f.id,g=
|
||||
m(h,e);g&&(!a.depMatched[d]&&!c[e])&&(m(b,e)?(a.defineDep(d,r[e]),a.check()):F(g,b,c))}),c[f]=!0)}function D(){var a,b,c=(a=1E3*j.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],l=[],g=!1,h=!0;if(!W){W=!0;B(V,function(a){var i=a.map,j=i.id;if(a.enabled&&(i.isDefine||l.push(a),!a.error))if(!a.inited&&c)e(j)?g=b=!0:(f.push(j),d(j));else if(!a.inited&&(a.fetched&&i.isDefine)&&(g=!0,!i.prefix))return h=!1});if(c&&f.length)return a=C("timeout","Load timeout for modules: "+f,null,f),a.contextName=
|
||||
i.contextName,w(a);h&&v(l,function(a){F(a,{},{})});if((!c||b)&&g)if((z||ea)&&!X)X=setTimeout(function(){X=0;D()},50);W=!1}}function E(a){t(r,a[0])||s(p(a[0],null,!0)).init(a[1],a[2])}function I(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!Y?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||Y)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function J(){var a;for(x();A.length;){a=
|
||||
A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var W,Z,i,L,X,j={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},h={},V={},$={},A=[],r={},S={},aa={},K=1,O=1;L={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?r[a.map.id]=a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?a.module:a.module=
|
||||
{id:a.map.id,uri:a.map.url,config:function(){return m(j.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};Z=function(a){this.events=m($,a.id)||{};this.map=a;this.shim=m(j.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};Z.prototype={init:function(a,b,c,f){f=f||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=
|
||||
!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
|
||||
this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var f=this.exports,l=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&&
|
||||
(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
|
||||
this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f);
|
||||
if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval",
|
||||
"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b,
|
||||
a);this.check()}));this.errback&&q(a,"error",u(this,this.errback))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b,registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,
|
||||
nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,
|
||||
a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n,q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=
|
||||
!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d,e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==
|
||||
e&&(!("."===k||".."===k)||1<e))d=b.substring(e,b.length),b=b.substring(0,e);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return t(r,p(b,a,!1,!0).id)},specified:function(b){b=p(b,a,!1,!0).id;return t(r,b)||t(h,b)}});a||(j.undef=function(b){x();var c=p(b,a,!0),e=m(h,b);d(b);delete r[b];delete S[c.url];delete $[b];T(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&($[b]=e.events),y(b))});return j},enable:function(a){m(h,a.id)&&s(a).enable()},completeLoad:function(a){var b,
|
||||
c,d=m(j.shim,a)||{},g=d.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=m(h,a);if(!b&&!t(r,a)&&c&&!c.inited){if(j.enforceDefine&&(!g||!da(g)))return e(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,d.deps||[],d.exportsFn])}D()},nameToUrl:function(a,b,c){var d,e,h;(d=m(j.pkgs,a))&&(a=d);if(d=m(aa,a))return i.nameToUrl(d,b,c);if(g.jsExtRegExp.test(a))d=a+(b||"");else{d=j.paths;a=a.split("/");for(e=a.length;0<e;e-=1)if(h=a.slice(0,
|
||||
e).join("/"),h=m(d,h)){H(h)&&(h=h[0]);a.splice(0,e,h);break}d=a.join("/");d+=b||(/^data\:|\?/.test(d)||c?"":".js");d=("/"===d.charAt(0)||d.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+d}return j.urlArgs?d+((-1===d.indexOf("?")?"?":"&")+j.urlArgs):d},load:function(a,b){g.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ja.test((a.currentTarget||a.srcElement).readyState))N=null,a=I(a),i.completeLoad(a.id)},onScriptError:function(a){var b=I(a);if(!e(b.id))return w(C("scripterror",
|
||||
"Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var g,x,y,D,I,E,N,J,s,O,ka=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,la=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,Q=/\.js$/,ia=/^\.\//;x=Object.prototype;var K=x.toString,fa=x.hasOwnProperty,ha=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),ea=!z&&"undefined"!==typeof importScripts,ja=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,
|
||||
Y="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},q={},R=[],M=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;q=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(q=require,require=void 0);g=requirejs=function(b,c,d,e){var n,p="_";!H(b)&&"string"!==typeof b&&(n=b,H(c)?(b=c,c=d,d=e):b=[]);n&&n.context&&(p=n.context);(e=m(F,p))||(e=F[p]=g.s.newContext(p));n&&e.configure(n);return e.require(b,c,d)};g.config=function(b){return g(b)};
|
||||
g.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.1.13";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=z;x=g.s={contexts:F,newContext:ga};g({});v(["toUrl","undef","defined","specified"],function(b){g[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=x.head=D.parentNode;g.onError=ca;g.createNode=function(b){var c=
|
||||
b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};g.load=function(b,c,d){var e=b&&b.config||{};if(z)return e=g.createNode(e,c,d),e.setAttribute("data-requirecontext",b.contextName),e.setAttribute("data-requiremodule",c),e.attachEvent&&!(e.attachEvent.toString&&0>e.attachEvent.toString().indexOf("[native code"))&&!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):
|
||||
(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"),s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=
|
||||
O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return N=b}),e=N;e&&(b||
|
||||
(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this);
|
188
gulpfile.js
188
gulpfile.js
|
@ -1,188 +0,0 @@
|
|||
var fs = require('fs');
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var concatCss = require('gulp-concat-css');
|
||||
var minifyCSS = require('gulp-clean-css');
|
||||
var shell = require('gulp-shell');
|
||||
var mkdirp = require('mkdirp');
|
||||
var webpack = require('webpack');
|
||||
var uglify = require('uglify-js');
|
||||
|
||||
var NAME = 'jsoneditor';
|
||||
var NAME_MINIMALIST = 'jsoneditor-minimalist';
|
||||
var ENTRY = './src/js/JSONEditor.js';
|
||||
var HEADER = './src/js/header.js';
|
||||
var IMAGE = './src/css/img/jsoneditor-icons.svg';
|
||||
var DOCS = './src/docs/*';
|
||||
var DIST = './dist';
|
||||
|
||||
// generate banner with today's date and correct version
|
||||
function createBanner() {
|
||||
var today = gutil.date(new Date(), 'yyyy-mm-dd'); // today, formatted as yyyy-mm-dd
|
||||
var version = require('./package.json').version; // math.js version
|
||||
|
||||
return String(fs.readFileSync(HEADER))
|
||||
.replace('@@date', today)
|
||||
.replace('@@version', version);
|
||||
}
|
||||
|
||||
var bannerPlugin = new webpack.BannerPlugin(createBanner(), {
|
||||
entryOnly: true,
|
||||
raw: true
|
||||
});
|
||||
|
||||
// create a single instance of the compiler to allow caching
|
||||
var compiler = webpack({
|
||||
entry: ENTRY,
|
||||
output: {
|
||||
library: 'JSONEditor',
|
||||
libraryTarget: 'umd',
|
||||
path: DIST,
|
||||
filename: NAME + '.js'
|
||||
},
|
||||
plugins: [ bannerPlugin ],
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.json$/, loader: "json" }
|
||||
]
|
||||
},
|
||||
cache: true
|
||||
});
|
||||
|
||||
// create a single instance of the compiler to allow caching
|
||||
var compilerMinimalist = webpack({
|
||||
entry: ENTRY,
|
||||
output: {
|
||||
library: 'JSONEditor',
|
||||
libraryTarget: 'umd',
|
||||
path: DIST,
|
||||
filename: NAME_MINIMALIST + '.js'
|
||||
},
|
||||
plugins: [
|
||||
bannerPlugin,
|
||||
new webpack.IgnorePlugin(new RegExp('^brace$')),
|
||||
new webpack.IgnorePlugin(new RegExp('^ajv'))
|
||||
],
|
||||
cache: true
|
||||
});
|
||||
|
||||
function minify(name) {
|
||||
var result = uglify.minify([DIST + '/' + name + '.js'], {
|
||||
outSourceMap: name + '.map',
|
||||
output: {
|
||||
comments: /@license/
|
||||
}
|
||||
});
|
||||
|
||||
var fileMin = DIST + '/' + name + '.min.js';
|
||||
var fileMap = DIST + '/' + name + '.map';
|
||||
|
||||
fs.writeFileSync(fileMin, result.code);
|
||||
fs.writeFileSync(fileMap, result.map);
|
||||
|
||||
gutil.log('Minified ' + fileMin);
|
||||
gutil.log('Mapped ' + fileMap);
|
||||
}
|
||||
|
||||
// make dist and dist/img folders
|
||||
gulp.task('mkdir', function () {
|
||||
mkdirp.sync(DIST);
|
||||
mkdirp.sync(DIST + '/img');
|
||||
});
|
||||
|
||||
// bundle javascript
|
||||
gulp.task('bundle', ['mkdir'], function (done) {
|
||||
// update the banner contents (has a date in it which should stay up to date)
|
||||
bannerPlugin.banner = createBanner();
|
||||
|
||||
compiler.run(function (err, stats) {
|
||||
if (err) {
|
||||
gutil.log(err);
|
||||
}
|
||||
|
||||
gutil.log('bundled ' + NAME + '.js');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// bundle minimalist version of javascript
|
||||
gulp.task('bundle-minimalist', ['mkdir'], function (done) {
|
||||
// update the banner contents (has a date in it which should stay up to date)
|
||||
bannerPlugin.banner = createBanner();
|
||||
|
||||
compilerMinimalist.run(function (err, stats) {
|
||||
if (err) {
|
||||
gutil.log(err);
|
||||
}
|
||||
|
||||
gutil.log('bundled ' + NAME_MINIMALIST + '.js');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// bundle css
|
||||
gulp.task('bundle-css', ['mkdir'], function () {
|
||||
gulp.src([
|
||||
'src/css/reset.css',
|
||||
'src/css/jsoneditor.css',
|
||||
'src/css/contextmenu.css',
|
||||
'src/css/menu.css',
|
||||
'src/css/searchbox.css'
|
||||
])
|
||||
.pipe(concatCss(NAME + '.css'))
|
||||
.pipe(gulp.dest(DIST))
|
||||
.pipe(concatCss(NAME + '.min.css'))
|
||||
.pipe(minifyCSS())
|
||||
.pipe(gulp.dest(DIST));
|
||||
|
||||
gutil.log('bundled ' + DIST + '/' + NAME + '.css');
|
||||
gutil.log('bundled ' + DIST + '/' + NAME + '.min.css');
|
||||
});
|
||||
|
||||
// create a folder img and copy the icons
|
||||
gulp.task('copy-img', ['mkdir'], function () {
|
||||
gulp.src(IMAGE)
|
||||
.pipe(gulp.dest(DIST +'/img'));
|
||||
gutil.log('Copied images');
|
||||
});
|
||||
|
||||
// create a folder img and copy the icons
|
||||
gulp.task('copy-docs', ['mkdir'], function () {
|
||||
gulp.src(DOCS)
|
||||
.pipe(gulp.dest(DIST));
|
||||
gutil.log('Copied doc');
|
||||
});
|
||||
|
||||
gulp.task('minify', ['bundle'], function () {
|
||||
minify(NAME)
|
||||
});
|
||||
|
||||
gulp.task('minify-minimalist', ['bundle-minimalist'], function () {
|
||||
minify(NAME_MINIMALIST)
|
||||
});
|
||||
|
||||
// TODO: zip file using archiver
|
||||
var pkg = 'jsoneditor-' + require('./package.json').version + '.zip';
|
||||
gulp.task('zip', shell.task([
|
||||
'zip ' + pkg + ' ' + 'README.md NOTICE LICENSE HISTORY.md index.html src dist docs examples -r '
|
||||
]));
|
||||
|
||||
// The watch task (to automatically rebuild when the source code changes)
|
||||
// Does only generate jsoneditor.js and jsoneditor.css, and copy the image
|
||||
// Does NOT minify the code and does NOT generate the minimalist version
|
||||
gulp.task('watch', ['bundle', 'bundle-css', 'copy-img'], function () {
|
||||
gulp.watch(['src/**/*'], ['bundle', 'bundle-css', 'copy-img']);
|
||||
});
|
||||
|
||||
// The default task (called when you run `gulp`)
|
||||
gulp.task('default', [
|
||||
'bundle',
|
||||
'bundle-minimalist',
|
||||
'bundle-css',
|
||||
'copy-img',
|
||||
'copy-docs',
|
||||
'minify',
|
||||
'minify-minimalist'
|
||||
]);
|
2
index.js
2
index.js
|
@ -1 +1 @@
|
|||
module.exports = require('./src/js/JSONEditor');
|
||||
module.exports = require('./dist/jsoneditor')
|
||||
|
|
File diff suppressed because it is too large
Load Diff
87
package.json
87
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "jsoneditor",
|
||||
"version": "5.5.6",
|
||||
"main": "./index",
|
||||
"version": "6.0.0-BETA",
|
||||
"main": "./index.js",
|
||||
"description": "A web-based tool to view, edit, format, and validate JSON",
|
||||
"tags": [
|
||||
"json",
|
||||
|
@ -10,33 +10,78 @@
|
|||
"formatter"
|
||||
],
|
||||
"author": "Jos de Jong <wjosdejong@gmail.com>",
|
||||
"license": "Apache-2.0",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/josdejong/jsoneditor",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/josdejong/jsoneditor.git"
|
||||
},
|
||||
"bugs": "https://github.com/josdejong/jsoneditor/issues",
|
||||
"scripts": {
|
||||
"build": "gulp",
|
||||
"watch": "gulp watch",
|
||||
"test": "mocha test"
|
||||
},
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"ajv": "3.8.8",
|
||||
"brace": "0.8.0",
|
||||
"javascript-natural-sort": "0.7.1"
|
||||
"@fortawesome/fontawesome": "1.1.8",
|
||||
"@fortawesome/fontawesome-free-solid": "5.0.13",
|
||||
"ajv": "6.12.2",
|
||||
"brace": "0.11.1",
|
||||
"javascript-natural-sort": "0.7.1",
|
||||
"jest": "24.9.0",
|
||||
"lodash": "4.17.15",
|
||||
"mitt": "1.2.0",
|
||||
"prop-types": "15.7.2",
|
||||
"react-hammerjs": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gulp": "3.9.1",
|
||||
"gulp-clean-css": "2.0.5",
|
||||
"gulp-concat-css": "2.2.0",
|
||||
"gulp-shell": "0.5.2",
|
||||
"gulp-util": "3.0.7",
|
||||
"json-loader": "0.5.4",
|
||||
"mkdirp": "0.5.1",
|
||||
"mocha": "2.4.5",
|
||||
"uglify-js": "2.6.2",
|
||||
"webpack": "1.12.14"
|
||||
"babel-cli": "6.26.0",
|
||||
"babel-plugin-external-helpers": "6.22.0",
|
||||
"babel-plugin-transform-class-properties": "6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "6.26.0",
|
||||
"babel-plugin-transform-react-jsx": "6.24.1",
|
||||
"babel-preset-env": "1.7.0",
|
||||
"console.table": "0.10.0",
|
||||
"cpy-cli": "3.1.1",
|
||||
"css-loader": "3.5.3",
|
||||
"npm-run-all": "4.1.5",
|
||||
"node-sass-chokidar": "1.4.0",
|
||||
"preact": "10.4.4",
|
||||
"preact-compat": "3.19.0",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-scripts": "3.4.1",
|
||||
"rollup": "2.12.1",
|
||||
"rollup-plugin-babel": "4.4.0",
|
||||
"rollup-plugin-commonjs": "10.1.0",
|
||||
"rollup-plugin-node-resolve": "5.2.0",
|
||||
"rollup-plugin-postcss": "3.1.1",
|
||||
"svg-url-loader": "6.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm-run-all -p watch-css start-js",
|
||||
"build-css": "node-sass-chokidar src/ -o src/",
|
||||
"watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
|
||||
"start-js": "react-scripts start",
|
||||
"build-js": "react-scripts build",
|
||||
"copy-css-lib": "cpy '**/*.css' '**/*.svg' '../../lib' --cwd='src/jsoneditor' --parents",
|
||||
"build-js-lib": "babel src/jsoneditor --out-dir lib --ignore spec.js,test.js",
|
||||
"build-js-bundle": "webpack --config ./config/webpack.config.js",
|
||||
"build-js-minimalist": "webpack --config ./config/webpack.config.minimalist.js",
|
||||
"build": "npm-run-all build-css copy-css-lib build-js-lib build-js-bundle build-js-minimalist",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>JSONEditor demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"short_name": "JSONEditor",
|
||||
"name": "JSONEditor demo",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#3883fa",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
module.exports = require('./lib/index.react')
|
|
@ -1,244 +0,0 @@
|
|||
|
||||
/* ContextMenu - main menu */
|
||||
|
||||
div.jsoneditor-contextmenu-root {
|
||||
position: relative;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu {
|
||||
position: absolute;
|
||||
box-sizing: content-box;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul,
|
||||
div.jsoneditor-contextmenu li {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul {
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 124px;
|
||||
|
||||
background: white;
|
||||
border: 1px solid #d3d3d3;
|
||||
box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3);
|
||||
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 124px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: #4d4d4d;
|
||||
background: transparent;
|
||||
|
||||
font-size: 10pt;
|
||||
font-family: arial, sans-serif;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
line-height: 26px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Fix button padding in firefox */
|
||||
div.jsoneditor-contextmenu ul li button::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button:hover,
|
||||
div.jsoneditor-contextmenu ul li button:focus {
|
||||
color: #1a1a1a;
|
||||
background-color: #f5f5f5;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-default {
|
||||
width: 92px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-expand {
|
||||
float: right;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
border-left: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu div.jsoneditor-icon {
|
||||
float: left;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-image: url('img/jsoneditor-icons.svg');
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button div.jsoneditor-expand {
|
||||
float: right;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0 4px 0 0;
|
||||
background: url('img/jsoneditor-icons.svg') 0 -72px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li button:hover div.jsoneditor-expand,
|
||||
div.jsoneditor-contextmenu ul li button:focus div.jsoneditor-expand,
|
||||
div.jsoneditor-contextmenu ul li.jsoneditor-selected div.jsoneditor-expand,
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-expand:hover div.jsoneditor-expand,
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-expand:focus div.jsoneditor-expand {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu div.jsoneditor-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
padding-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-remove > div.jsoneditor-icon {
|
||||
background-position: -24px -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-remove:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-remove:focus > div.jsoneditor-icon {
|
||||
background-position: -24px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-append > div.jsoneditor-icon {
|
||||
background-position: 0 -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-append:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-append:focus > div.jsoneditor-icon {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-insert > div.jsoneditor-icon {
|
||||
background-position: 0 -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-insert:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-insert:focus > div.jsoneditor-icon {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-duplicate > div.jsoneditor-icon {
|
||||
background-position: -48px -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-duplicate:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-duplicate:focus > div.jsoneditor-icon {
|
||||
background-position: -48px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-asc > div.jsoneditor-icon {
|
||||
background-position: -168px -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-asc:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-asc:focus > div.jsoneditor-icon {
|
||||
background-position: -168px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-desc > div.jsoneditor-icon {
|
||||
background-position: -192px -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-desc:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-sort-desc:focus > div.jsoneditor-icon {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
/* ContextMenu - sub menu */
|
||||
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-selected,
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover,
|
||||
div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus {
|
||||
color: white;
|
||||
background-color: #ee422e;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li ul {
|
||||
display: none;
|
||||
position: relative;
|
||||
left: -10px;
|
||||
top: 0;
|
||||
|
||||
border: none;
|
||||
box-shadow: inset 0 0 10px rgba(128, 128, 128, 0.5);
|
||||
padding: 0 10px;
|
||||
|
||||
/* TODO: transition is not supported on IE8-9 */
|
||||
-webkit-transition: all 0.3s ease-out;
|
||||
-moz-transition: all 0.3s ease-out;
|
||||
-o-transition: all 0.3s ease-out;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li.jsoneditor-selected ul {
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li ul li button {
|
||||
padding-left: 24px;
|
||||
animation: all ease-in-out 1s;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul li ul li button:hover,
|
||||
div.jsoneditor-contextmenu ul li ul li button:focus {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-string > div.jsoneditor-icon {
|
||||
background-position: -144px -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-string:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-string:focus > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-string.jsoneditor-selected > div.jsoneditor-icon{
|
||||
background-position: -144px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-auto > div.jsoneditor-icon {
|
||||
background-position: -120px -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-auto:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-auto:focus > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-auto.jsoneditor-selected > div.jsoneditor-icon {
|
||||
background-position: -120px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-object > div.jsoneditor-icon {
|
||||
background-position: -72px -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-object:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-object:focus > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-object.jsoneditor-selected > div.jsoneditor-icon{
|
||||
background-position: -72px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-array > div.jsoneditor-icon {
|
||||
background-position: -96px -24px;
|
||||
}
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-array:hover > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-array:focus > div.jsoneditor-icon,
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-array.jsoneditor-selected > div.jsoneditor-icon{
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu button.jsoneditor-type-modes > div.jsoneditor-icon {
|
||||
background-image: none;
|
||||
width: 6px;
|
||||
}
|
|
@ -1,449 +0,0 @@
|
|||
|
||||
div.jsoneditor {
|
||||
|
||||
}
|
||||
|
||||
div.jsoneditor-field,
|
||||
div.jsoneditor-value,
|
||||
div.jsoneditor-readonly {
|
||||
border: 1px solid transparent;
|
||||
min-height: 16px;
|
||||
min-width: 32px;
|
||||
padding: 2px;
|
||||
margin: 1px;
|
||||
word-wrap: break-word;
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* adjust margin of p elements inside editable divs, needed for Opera, IE */
|
||||
div.jsoneditor-field p,
|
||||
div.jsoneditor-value p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-value {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
div.jsoneditor-readonly {
|
||||
min-width: 16px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
div.jsoneditor-empty {
|
||||
border-color: lightgray;
|
||||
border-style: dashed;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
div.jsoneditor-field.jsoneditor-empty::after,
|
||||
div.jsoneditor-value.jsoneditor-empty::after {
|
||||
pointer-events: none;
|
||||
color: lightgray;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
div.jsoneditor-field.jsoneditor-empty::after {
|
||||
content: "field";
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-empty::after {
|
||||
content: "value";
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-url,
|
||||
a.jsoneditor-value.jsoneditor-url {
|
||||
color: green;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a.jsoneditor-value.jsoneditor-url {
|
||||
display: inline-block;
|
||||
padding: 2px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
a.jsoneditor-value.jsoneditor-url:hover,
|
||||
a.jsoneditor-value.jsoneditor-url:focus {
|
||||
color: #ee422e;
|
||||
}
|
||||
|
||||
div.jsoneditor td.jsoneditor-separator {
|
||||
padding: 3px 0;
|
||||
vertical-align: top;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
div.jsoneditor-field[contenteditable=true]:focus,
|
||||
div.jsoneditor-field[contenteditable=true]:hover,
|
||||
div.jsoneditor-value[contenteditable=true]:focus,
|
||||
div.jsoneditor-value[contenteditable=true]:hover,
|
||||
div.jsoneditor-field.jsoneditor-highlight,
|
||||
div.jsoneditor-value.jsoneditor-highlight {
|
||||
background-color: #FFFFAB;
|
||||
border: 1px solid yellow;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
div.jsoneditor-field.jsoneditor-highlight-active,
|
||||
div.jsoneditor-field.jsoneditor-highlight-active:focus,
|
||||
div.jsoneditor-field.jsoneditor-highlight-active:hover,
|
||||
div.jsoneditor-value.jsoneditor-highlight-active,
|
||||
div.jsoneditor-value.jsoneditor-highlight-active:focus,
|
||||
div.jsoneditor-value.jsoneditor-highlight-active:hover {
|
||||
background-color: #ffee00;
|
||||
border: 1px solid #ffc700;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-string {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-object,
|
||||
div.jsoneditor-value.jsoneditor-array {
|
||||
min-width: 16px;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-number {
|
||||
color: #ee422e;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-boolean {
|
||||
color: #ff8c00;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-null {
|
||||
color: #004ED0;
|
||||
}
|
||||
|
||||
div.jsoneditor-value.jsoneditor-invalid {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
div.jsoneditor-tree button {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background: transparent url('img/jsoneditor-icons.svg');
|
||||
}
|
||||
|
||||
div.jsoneditor-mode-view tr.jsoneditor-expandable td.jsoneditor-tree,
|
||||
div.jsoneditor-mode-form tr.jsoneditor-expandable td.jsoneditor-tree {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-collapsed {
|
||||
background-position: 0 -48px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-expanded {
|
||||
background-position: 0 -72px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-contextmenu {
|
||||
background-position: -48px -72px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-contextmenu:hover,
|
||||
div.jsoneditor-tree button.jsoneditor-contextmenu:focus,
|
||||
div.jsoneditor-tree button.jsoneditor-contextmenu.jsoneditor-selected,
|
||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu {
|
||||
background-position: -48px -48px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree *:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button:focus {
|
||||
/* TODO: nice outline for buttons with focus
|
||||
outline: #97B0F8 solid 2px;
|
||||
box-shadow: 0 0 8px #97B0F8;
|
||||
*/
|
||||
background-color: #f5f5f5;
|
||||
outline: #e5e5e5 solid 1px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-invisible {
|
||||
visibility: hidden;
|
||||
background: none;
|
||||
}
|
||||
|
||||
div.jsoneditor {
|
||||
color: #1A1A1A;
|
||||
border: 1px solid #3883fa;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
|
||||
div.jsoneditor-tree table.jsoneditor-tree {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-outer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: -35px 0 0 0;
|
||||
padding: 35px 0 0 0;
|
||||
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
textarea.jsoneditor-text,
|
||||
.ace-jsoneditor {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
textarea.jsoneditor-text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
outline-width: 0;
|
||||
border: none;
|
||||
background-color: white;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
tr.jsoneditor-highlight,
|
||||
tr.jsoneditor-selected {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
tr.jsoneditor-selected button.jsoneditor-dragarea,
|
||||
tr.jsoneditor-selected button.jsoneditor-contextmenu {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea,
|
||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-dragarea {
|
||||
background: url('img/jsoneditor-icons.svg') -72px -72px;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree button.jsoneditor-dragarea:hover,
|
||||
div.jsoneditor-tree button.jsoneditor-dragarea:focus,
|
||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea {
|
||||
background-position: -72px -48px;
|
||||
}
|
||||
|
||||
div.jsoneditor tr,
|
||||
div.jsoneditor th,
|
||||
div.jsoneditor td {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
div.jsoneditor td.jsoneditor-tree {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
div.jsoneditor-field,
|
||||
div.jsoneditor-value,
|
||||
div.jsoneditor td,
|
||||
div.jsoneditor th,
|
||||
div.jsoneditor textarea,
|
||||
.jsoneditor-schema-error {
|
||||
font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #1A1A1A;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* popover */
|
||||
.jsoneditor-schema-error {
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
/*font-family: arial, sans-serif;*/
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-tree .jsoneditor-schema-error {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0 4px 0 0;
|
||||
background: url('img/jsoneditor-icons.svg') -168px -48px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover {
|
||||
background-color: #4c4c4c;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.4);
|
||||
color: #fff;
|
||||
display: none;
|
||||
padding: 7px 10px;
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above {
|
||||
bottom: 32px;
|
||||
left: -98px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below {
|
||||
top: 32px;
|
||||
left: -98px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left {
|
||||
top: -7px;
|
||||
right: 32px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right {
|
||||
top: -7px;
|
||||
left: 32px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover:before {
|
||||
border-right: 7px solid transparent;
|
||||
border-left: 7px solid transparent;
|
||||
content: '';
|
||||
display: block;
|
||||
left: 50%;
|
||||
margin-left: -7px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above:before {
|
||||
border-top: 7px solid #4c4c4c;
|
||||
bottom: -7px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below:before {
|
||||
border-bottom: 7px solid #4c4c4c;
|
||||
top: -7px;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left:before {
|
||||
border-left: 7px solid #4c4c4c;
|
||||
border-top: 7px solid transparent;
|
||||
border-bottom: 7px solid transparent;
|
||||
content: '';
|
||||
top: 19px;
|
||||
right: -14px;
|
||||
left: inherit;
|
||||
margin-left: inherit;
|
||||
margin-top: -7px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right:before {
|
||||
border-right: 7px solid #4c4c4c;
|
||||
border-top: 7px solid transparent;
|
||||
border-bottom: 7px solid transparent;
|
||||
content: '';
|
||||
top: 19px;
|
||||
left: -14px;
|
||||
margin-left: inherit;
|
||||
margin-top: -7px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.jsoneditor-schema-error:hover .jsoneditor-popover,
|
||||
.jsoneditor-schema-error:focus .jsoneditor-popover {
|
||||
display: block;
|
||||
-webkit-animation: fade-in .3s linear 1, move-up .3s linear 1;
|
||||
-moz-animation: fade-in .3s linear 1, move-up .3s linear 1;
|
||||
-ms-animation: fade-in .3s linear 1, move-up .3s linear 1;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@-moz-keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@-ms-keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
/*@-webkit-keyframes move-up {*/
|
||||
/*from { bottom: 24px; }*/
|
||||
/*to { bottom: 32px; }*/
|
||||
/*}*/
|
||||
/*@-moz-keyframes move-up {*/
|
||||
/*from { bottom: 24px; }*/
|
||||
/*to { bottom: 32px; }*/
|
||||
/*}*/
|
||||
/*@-ms-keyframes move-up {*/
|
||||
/*from { bottom: 24px; }*/
|
||||
/*to { bottom: 32px; }*/
|
||||
/*}*/
|
||||
|
||||
|
||||
/* JSON schema errors displayed at the bottom of the editor in mode text and code */
|
||||
|
||||
.jsoneditor .jsoneditor-text-errors {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: #ffef8b;
|
||||
border-top: 1px solid #ffd700;
|
||||
}
|
||||
|
||||
.jsoneditor .jsoneditor-text-errors td {
|
||||
padding: 3px 6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.jsoneditor-text-errors .jsoneditor-schema-error {
|
||||
border: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0 4px 0 0;
|
||||
background: url('img/jsoneditor-icons.svg') -168px -48px;
|
||||
}
|
||||
|
110
src/css/menu.css
110
src/css/menu.css
|
@ -1,110 +0,0 @@
|
|||
|
||||
div.jsoneditor-menu {
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
padding: 2px;
|
||||
margin: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
color: white;
|
||||
background-color: #3883fa;
|
||||
border-bottom: 1px solid #3883fa;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid transparent;
|
||||
background: transparent url('img/jsoneditor-icons.svg');
|
||||
color: white;
|
||||
opacity: 0.8;
|
||||
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button:hover,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button:hover {
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
border: 1px solid rgba(255,255,255,0.4);
|
||||
}
|
||||
div.jsoneditor-menu > button:focus,
|
||||
div.jsoneditor-menu > button:active,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button:focus,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button:active {
|
||||
background-color: rgba(255,255,255,0.3);
|
||||
}
|
||||
div.jsoneditor-menu > button:disabled,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-collapse-all {
|
||||
background-position: 0 -96px;
|
||||
}
|
||||
div.jsoneditor-menu > button.jsoneditor-expand-all {
|
||||
background-position: 0 -120px;
|
||||
}
|
||||
div.jsoneditor-menu > button.jsoneditor-undo {
|
||||
background-position: -24px -96px;
|
||||
}
|
||||
div.jsoneditor-menu > button.jsoneditor-undo:disabled {
|
||||
background-position: -24px -120px;
|
||||
}
|
||||
div.jsoneditor-menu > button.jsoneditor-redo {
|
||||
background-position: -48px -96px;
|
||||
}
|
||||
div.jsoneditor-menu > button.jsoneditor-redo:disabled {
|
||||
background-position: -48px -120px;
|
||||
}
|
||||
div.jsoneditor-menu > button.jsoneditor-compact {
|
||||
background-position: -72px -96px;
|
||||
}
|
||||
div.jsoneditor-menu > button.jsoneditor-format {
|
||||
background-position: -72px -120px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > div.jsoneditor-modes {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button {
|
||||
background-image: none;
|
||||
width: auto;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu > button.jsoneditor-separator,
|
||||
div.jsoneditor-menu > div.jsoneditor-modes > button.jsoneditor-separator {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu a {
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
opacity: 0.8;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu a.jsoneditor-poweredBy {
|
||||
font-size: 8pt;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 10px;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/* reset styling (prevent conflicts with bootstrap, materialize.css, etc.) */
|
||||
|
||||
div.jsoneditor input {
|
||||
height: auto;
|
||||
border: inherit;
|
||||
}
|
||||
|
||||
div.jsoneditor input:focus {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
div.jsoneditor table {
|
||||
border-collapse: collapse;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div.jsoneditor td,
|
||||
div.jsoneditor th {
|
||||
padding: 0;
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
vertical-align: inherit;
|
||||
border-radius: inherit;
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
|
||||
table.jsoneditor-search input,
|
||||
table.jsoneditor-search div.jsoneditor-results {
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #1A1A1A;
|
||||
background: transparent; /* For Firefox */
|
||||
}
|
||||
|
||||
table.jsoneditor-search div.jsoneditor-results {
|
||||
color: white;
|
||||
padding-right: 5px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
table.jsoneditor-search div.jsoneditor-frame {
|
||||
border: 1px solid transparent;
|
||||
background-color: white;
|
||||
padding: 0 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
table.jsoneditor-search div.jsoneditor-frame table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.jsoneditor-search input {
|
||||
width: 120px;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin: 1px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button {
|
||||
width: 16px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: url('img/jsoneditor-icons.svg');
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button.jsoneditor-refresh {
|
||||
width: 18px;
|
||||
background-position: -99px -73px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button.jsoneditor-next {
|
||||
cursor: pointer;
|
||||
background-position: -124px -73px;
|
||||
}
|
||||
table.jsoneditor-search button.jsoneditor-next:hover {
|
||||
background-position: -124px -49px;
|
||||
}
|
||||
|
||||
table.jsoneditor-search button.jsoneditor-previous {
|
||||
cursor: pointer;
|
||||
background-position: -148px -73px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
table.jsoneditor-search button.jsoneditor-previous:hover {
|
||||
background-position: -148px -49px;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
body, input, select {
|
||||
font-family: sans-serif;
|
||||
font-size: 11pt; }
|
||||
|
||||
body {
|
||||
height: 2000px; }
|
||||
|
||||
.demo .menu {
|
||||
margin: 20px 0; }
|
||||
.demo .menu button, .demo .menu label {
|
||||
margin-right: 10px; }
|
||||
|
||||
.demo .contents {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
max-width: 800px; }
|
|
@ -0,0 +1,197 @@
|
|||
import React, { Component } from 'react'
|
||||
import JSONEditor from '../jsoneditor/index.react'
|
||||
import { setIn } from '../jsoneditor/utils/immutabilityHelpers'
|
||||
import { largeJson } from './resources/largeJson'
|
||||
|
||||
import './Demo.css'
|
||||
|
||||
const schema = {
|
||||
"title": "Example Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"firstName": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"type": "string"
|
||||
},
|
||||
"gender": {
|
||||
"enum": ["male", "female"]
|
||||
},
|
||||
"age": {
|
||||
"description": "Age in years",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": ["firstName", "lastName"]
|
||||
}
|
||||
|
||||
const json = {
|
||||
'array': [1, 2, 3],
|
||||
'emptyArray': [],
|
||||
'emptyObject': {},
|
||||
'firstName': null,
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd', 'e': [{"first": true}, {"second": true}]},
|
||||
'string': 'Hello World',
|
||||
'unicode': 'A unicode character: \u260E',
|
||||
'url': 'http://jsoneditoronline.org',
|
||||
'largeArray': [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
|
||||
'structureArray': [
|
||||
{name: 'Joe', age: 24},
|
||||
{name: 'Sarah', age: 28},
|
||||
{name: 'Brett', age: 21},
|
||||
{name: 'Emma', age: 31},
|
||||
]
|
||||
}
|
||||
|
||||
function expandAll (path) {
|
||||
return true
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
logging: false,
|
||||
|
||||
editorProps: {
|
||||
json,
|
||||
schema: null,
|
||||
|
||||
onPatch: this.handlePatch,
|
||||
onPatchText: this.handlePatchText,
|
||||
onChange: this.handleChange,
|
||||
onChangeText: this.handleChangeText,
|
||||
onChangeMode: this.handleChangeMode,
|
||||
onError: this.handleError,
|
||||
|
||||
name: 'myObject',
|
||||
mode: 'tree',
|
||||
modes: ['text', 'code', 'tree', 'form', 'view'],
|
||||
keyBindings: {
|
||||
compact: ['Ctrl+\\', 'Command+\\', 'Ctrl+Alt+1', 'Command+Option+1'],
|
||||
format: ['Ctrl+Shift+\\', 'Command+Shift+\\', 'Ctrl+Alt+2', 'Command+Option+2'],
|
||||
duplicate: ['Ctrl+D', 'Ctrl+Shift+D', 'Command+D', 'Command+Shift+D']
|
||||
},
|
||||
indentation: 4,
|
||||
escapeUnicode: true,
|
||||
history: true,
|
||||
search: true,
|
||||
expand: expandAll
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
render() {
|
||||
return <div className="demo">
|
||||
<div className="menu">
|
||||
<button onClick={this.handleSetJson}>Set JSON</button>
|
||||
<button onClick={this.handleGetJson}>Get JSON</button>
|
||||
|
||||
<label>mode:
|
||||
<select value={this.state.editorProps.mode} onChange={this.handleSetMode}>
|
||||
<option value="text">text</option>
|
||||
<option value="code">code</option>
|
||||
<option value="tree">tree</option>
|
||||
<option value="form">form</option>
|
||||
<option value="view">view</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
value={this.state.editorProps.schema !== null}
|
||||
onChange={this.handleToggleJSONSchema} /> JSON Schema
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
value={this.state.logging}
|
||||
onChange={this.handleToggleLogging} /> Log events
|
||||
</label>
|
||||
</div>
|
||||
<div className="contents">
|
||||
<JSONEditor {...this.state.editorProps} />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
handleSetJson = () => {
|
||||
this.setState({
|
||||
editorProps: setIn(this.state.editorProps, ['json'], largeJson)
|
||||
})
|
||||
}
|
||||
|
||||
handleGetJson = () => {
|
||||
const json = this.state.editorProps.json
|
||||
alert(JSON.stringify(json, null, 2))
|
||||
}
|
||||
|
||||
handleSetMode = (event) => {
|
||||
const mode = event.target.value
|
||||
this.setState({
|
||||
editorProps: setIn(this.state.editorProps, ['mode'], mode)
|
||||
})
|
||||
}
|
||||
|
||||
handleToggleLogging = (event) => {
|
||||
const logging = event.target.checked
|
||||
this.setState({ logging })
|
||||
}
|
||||
|
||||
handleToggleJSONSchema = (event) => {
|
||||
const value = event.target.checked ? schema : null
|
||||
this.setState({
|
||||
editorProps: setIn(this.state.editorProps, ['schema'], value)
|
||||
})
|
||||
}
|
||||
|
||||
handleChange = (json) => {
|
||||
this.log('onChange json=', json)
|
||||
|
||||
this.setState({
|
||||
editorProps: setIn(this.state.editorProps, ['json'], json)
|
||||
})
|
||||
}
|
||||
|
||||
handleChangeText = (text) => {
|
||||
this.log('onChangeText', text)
|
||||
}
|
||||
|
||||
handlePatch = (patch, revert) => {
|
||||
this.log('onPatch patch=', patch, ', revert=', revert)
|
||||
window.immutableJSONPatch = patch
|
||||
window.revert = revert
|
||||
}
|
||||
|
||||
handlePatchText = (patch, revert) => {
|
||||
// FIXME: implement onPatchText
|
||||
this.log('onPatchText patch=', patch, ', revert=', revert)
|
||||
}
|
||||
|
||||
handleChangeMode = (mode, prevMode) => {
|
||||
this.log('switched mode from', prevMode, 'to', mode)
|
||||
|
||||
this.setState({
|
||||
editorProps: setIn(this.state.editorProps, ['mode'], mode)
|
||||
})
|
||||
}
|
||||
|
||||
handleError = (err) => {
|
||||
console.error(err)
|
||||
alert(err)
|
||||
}
|
||||
|
||||
log (...args) {
|
||||
if (this.state.logging) {
|
||||
console.log(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
body, input, select {
|
||||
font-family: sans-serif;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 2000px;
|
||||
}
|
||||
|
||||
.demo {
|
||||
.menu {
|
||||
margin: 20px 0;
|
||||
|
||||
button, label {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.contents {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
max-width : 800px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Demo from './Demo';
|
||||
|
||||
|
||||
|
||||
test('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Demo />, div);
|
||||
});
|
|
@ -0,0 +1,179 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>JSONEditor vanilla demo</title>
|
||||
|
||||
<!-- For IE and Edge -->
|
||||
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.1/es6-shim.min.js"></script>-->
|
||||
|
||||
<script src="../../dist/jsoneditor.js"></script>
|
||||
|
||||
<script src="./resources/largeJson.js"></script>
|
||||
|
||||
<style>
|
||||
body, input, select {
|
||||
font-family: sans-serif;
|
||||
font-size: 11pt;
|
||||
}
|
||||
#container {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
max-width : 800px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<button id="setJSON">Set JSON</button>
|
||||
<button id="getJSON">Get JSON</button>
|
||||
|
||||
<button id="toggleSchema">Toggle JSON Schema</button>
|
||||
|
||||
<label for="mode">mode:
|
||||
<select id="mode">
|
||||
<option value="text">text</option>
|
||||
<option value="code">code</option>
|
||||
<option value="tree" selected>tree</option>
|
||||
<option value="form">form</option>
|
||||
<option value="view">view</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" id="logEvents" /> Log events
|
||||
</label>
|
||||
</p>
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
// prevent contentEditable warnings of React
|
||||
// https://github.com/facebook/draft-js/issues/53#issuecomment-188280259
|
||||
console.error = (function() {
|
||||
const error = console.error
|
||||
|
||||
return function(exception) {
|
||||
if ((exception + '').indexOf('Warning: A component is `contentEditable`') !== 0) {
|
||||
error.apply(console, arguments)
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
// create the editor
|
||||
const mode = document.getElementById('mode').value
|
||||
const container = document.getElementById('container')
|
||||
const options = {
|
||||
name: 'myObject',
|
||||
onPatch: function (patch, revert) {
|
||||
log('onPatch patch=', patch, ', revert=', revert)
|
||||
window.immutableJSONPatch = patch
|
||||
window.revert = revert
|
||||
},
|
||||
onPatchText: function (patch, revert) {
|
||||
// FIXME: implement onPatchText
|
||||
log('onPatchText patch=', patch, ', revert=', revert)
|
||||
},
|
||||
onChange: function (json) {
|
||||
log('onChange json=', json)
|
||||
},
|
||||
onChangeText: function (text) {
|
||||
log('onChangeText', text)
|
||||
},
|
||||
onChangeMode: function (mode, prevMode) {
|
||||
log('switched mode from', prevMode, 'to', mode)
|
||||
document.getElementById('mode').value = mode
|
||||
},
|
||||
onError: function (err) {
|
||||
console.error(err)
|
||||
alert(err)
|
||||
},
|
||||
mode: mode,
|
||||
modes: ['text', 'code', 'tree', 'form', 'view'], keyBindings: {
|
||||
compact: ['Ctrl+\\', 'Command+\\', 'Ctrl+Alt+1', 'Command+Option+1'],
|
||||
format: ['Ctrl+Shift+\\', 'Command+Shift+\\', 'Ctrl+Alt+2', 'Command+Option+2'],
|
||||
duplicate: ['Ctrl+D', 'Ctrl+Shift+D', 'Command+D', 'Command+Shift+D']
|
||||
},
|
||||
indentation: 4,
|
||||
escapeUnicode: true,
|
||||
history: true,
|
||||
search: true,
|
||||
|
||||
expand: function (path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
const editor = jsoneditor(container, options)
|
||||
const json = {
|
||||
'array': [1, 2, 3],
|
||||
'emptyArray': [],
|
||||
'emptyObject': {},
|
||||
'firstName': null,
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd', 'e': [{"first": true}, {"second": true}]},
|
||||
'string': 'Hello World',
|
||||
'unicode': 'A unicode character: \u260E',
|
||||
'url': 'http://jsoneditoronline.org'
|
||||
}
|
||||
editor.set(json)
|
||||
|
||||
const schema = {
|
||||
"title": "Example Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"firstName": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"type": "string"
|
||||
},
|
||||
"gender": {
|
||||
"enum": ["male", "female"]
|
||||
},
|
||||
"age": {
|
||||
"description": "Age in years",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": ["firstName", "lastName"]
|
||||
}
|
||||
|
||||
// set json
|
||||
document.getElementById('setJSON').onclick = async function () {
|
||||
editor.set(largeJson, {
|
||||
expand: function (path) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// set schema
|
||||
let hasSchema = false
|
||||
document.getElementById('toggleSchema').onclick = function () {
|
||||
editor.setSchema(hasSchema ? null : schema)
|
||||
hasSchema = !hasSchema
|
||||
}
|
||||
|
||||
// get json
|
||||
document.getElementById('getJSON').onclick = function () {
|
||||
const json = editor.get()
|
||||
alert(JSON.stringify(json, null, 2))
|
||||
}
|
||||
|
||||
// change mode
|
||||
document.getElementById('mode').onchange = function (event) {
|
||||
const mode = event.target.value
|
||||
editor.setMode(mode)
|
||||
}
|
||||
|
||||
function log (...args) {
|
||||
if (document.getElementById('logEvents').checked) {
|
||||
console.log(...args)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
const largeJson = {
|
||||
"version": "1.0",
|
||||
"encoding": "UTF-8",
|
||||
"feed": {
|
||||
|
@ -12603,3 +12603,12 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
|
||||
// export for node.js or web
|
||||
if (typeof module !== 'undefined' && typeof exports !== 'undefined') {
|
||||
exports.largeJson = largeJson
|
||||
}
|
||||
else {
|
||||
window.largeJson = largeJson
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# Which files do I need?
|
||||
|
||||
Ehhh, that's quite some files in this dist folder. Which files do I need?
|
||||
|
||||
|
||||
## Full version
|
||||
|
||||
If you're not sure which version to use, use the full version.
|
||||
|
||||
Which files are needed when using the full version?
|
||||
|
||||
- jsoneditor.min.js
|
||||
- jsoneditor.map (optional, for debugging purposes only)
|
||||
- jsoneditor.min.css
|
||||
- img/jsoneditor-icons.svg
|
||||
|
||||
|
||||
## Minimalist version
|
||||
|
||||
The minimalist version has excluded the following libraries:
|
||||
|
||||
- `ace` (via `brace`), used for the code editor.
|
||||
- `ajv`, used for JSON schema validation.
|
||||
|
||||
This reduces the the size of the minified and gzipped JavaScript file from
|
||||
about 160 kB to about 40 kB.
|
||||
|
||||
When to use the minimalist version?
|
||||
|
||||
- If you don't need the mode "code" and don't need JSON schema validation.
|
||||
- Or if you want to provide `ace` and/or `ajv` yourself via the configuration
|
||||
options, for example when you already use Ace in other parts of your
|
||||
web application too and don't want to bundle the library twice.
|
||||
|
||||
Which files are needed when using the minimalist version?
|
||||
|
||||
- jsoneditor-minimalist.min.js
|
||||
- jsoneditor-minimalist.map (optional, for debugging purposes only)
|
||||
- jsoneditor.min.css
|
||||
- img/jsoneditor-icons.svg
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Demo from './demo/Demo';
|
||||
|
||||
ReactDOM.render(<Demo />, document.getElementById('root'));
|
|
@ -1,457 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* A context menu
|
||||
* @param {Object[]} items Array containing the menu structure
|
||||
* TODO: describe structure
|
||||
* @param {Object} [options] Object with options. Available options:
|
||||
* {function} close Callback called when the
|
||||
* context menu is being closed.
|
||||
* @constructor
|
||||
*/
|
||||
function ContextMenu (items, options) {
|
||||
this.dom = {};
|
||||
|
||||
var me = this;
|
||||
var dom = this.dom;
|
||||
this.anchor = undefined;
|
||||
this.items = items;
|
||||
this.eventListeners = {};
|
||||
this.selection = undefined; // holds the selection before the menu was opened
|
||||
this.onClose = options ? options.close : undefined;
|
||||
|
||||
// create root element
|
||||
var root = document.createElement('div');
|
||||
root.className = 'jsoneditor-contextmenu-root';
|
||||
dom.root = root;
|
||||
|
||||
// create a container element
|
||||
var menu = document.createElement('div');
|
||||
menu.className = 'jsoneditor-contextmenu';
|
||||
dom.menu = menu;
|
||||
root.appendChild(menu);
|
||||
|
||||
// create a list to hold the menu items
|
||||
var list = document.createElement('ul');
|
||||
list.className = 'jsoneditor-menu';
|
||||
menu.appendChild(list);
|
||||
dom.list = list;
|
||||
dom.items = []; // list with all buttons
|
||||
|
||||
// create a (non-visible) button to set the focus to the menu
|
||||
var focusButton = document.createElement('button');
|
||||
dom.focusButton = focusButton;
|
||||
var li = document.createElement('li');
|
||||
li.style.overflow = 'hidden';
|
||||
li.style.height = '0';
|
||||
li.appendChild(focusButton);
|
||||
list.appendChild(li);
|
||||
|
||||
function createMenuItems (list, domItems, items) {
|
||||
items.forEach(function (item) {
|
||||
if (item.type == 'separator') {
|
||||
// create a separator
|
||||
var separator = document.createElement('div');
|
||||
separator.className = 'jsoneditor-separator';
|
||||
li = document.createElement('li');
|
||||
li.appendChild(separator);
|
||||
list.appendChild(li);
|
||||
}
|
||||
else {
|
||||
var domItem = {};
|
||||
|
||||
// create a menu item
|
||||
var li = document.createElement('li');
|
||||
list.appendChild(li);
|
||||
|
||||
// create a button in the menu item
|
||||
var button = document.createElement('button');
|
||||
button.className = item.className;
|
||||
domItem.button = button;
|
||||
if (item.title) {
|
||||
button.title = item.title;
|
||||
}
|
||||
if (item.click) {
|
||||
button.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
me.hide();
|
||||
item.click();
|
||||
};
|
||||
}
|
||||
li.appendChild(button);
|
||||
|
||||
// create the contents of the button
|
||||
if (item.submenu) {
|
||||
// add the icon to the button
|
||||
var divIcon = document.createElement('div');
|
||||
divIcon.className = 'jsoneditor-icon';
|
||||
button.appendChild(divIcon);
|
||||
button.appendChild(document.createTextNode(item.text));
|
||||
|
||||
var buttonSubmenu;
|
||||
if (item.click) {
|
||||
// submenu and a button with a click handler
|
||||
button.className += ' jsoneditor-default';
|
||||
|
||||
var buttonExpand = document.createElement('button');
|
||||
domItem.buttonExpand = buttonExpand;
|
||||
buttonExpand.className = 'jsoneditor-expand';
|
||||
buttonExpand.innerHTML = '<div class="jsoneditor-expand"></div>';
|
||||
li.appendChild(buttonExpand);
|
||||
if (item.submenuTitle) {
|
||||
buttonExpand.title = item.submenuTitle;
|
||||
}
|
||||
|
||||
buttonSubmenu = buttonExpand;
|
||||
}
|
||||
else {
|
||||
// submenu and a button without a click handler
|
||||
var divExpand = document.createElement('div');
|
||||
divExpand.className = 'jsoneditor-expand';
|
||||
button.appendChild(divExpand);
|
||||
|
||||
buttonSubmenu = button;
|
||||
}
|
||||
|
||||
// attach a handler to expand/collapse the submenu
|
||||
buttonSubmenu.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
me._onExpandItem(domItem);
|
||||
buttonSubmenu.focus();
|
||||
};
|
||||
|
||||
// create the submenu
|
||||
var domSubItems = [];
|
||||
domItem.subItems = domSubItems;
|
||||
var ul = document.createElement('ul');
|
||||
domItem.ul = ul;
|
||||
ul.className = 'jsoneditor-menu';
|
||||
ul.style.height = '0';
|
||||
li.appendChild(ul);
|
||||
createMenuItems(ul, domSubItems, item.submenu);
|
||||
}
|
||||
else {
|
||||
// no submenu, just a button with clickhandler
|
||||
button.innerHTML = '<div class="jsoneditor-icon"></div>' + item.text;
|
||||
}
|
||||
|
||||
domItems.push(domItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
createMenuItems(list, this.dom.items, items);
|
||||
|
||||
// TODO: when the editor is small, show the submenu on the right instead of inline?
|
||||
|
||||
// calculate the max height of the menu with one submenu expanded
|
||||
this.maxHeight = 0; // height in pixels
|
||||
items.forEach(function (item) {
|
||||
var height = (items.length + (item.submenu ? item.submenu.length : 0)) * 24;
|
||||
me.maxHeight = Math.max(me.maxHeight, height);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently visible buttons
|
||||
* @return {Array.<HTMLElement>} buttons
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._getVisibleButtons = function () {
|
||||
var buttons = [];
|
||||
var me = this;
|
||||
this.dom.items.forEach(function (item) {
|
||||
buttons.push(item.button);
|
||||
if (item.buttonExpand) {
|
||||
buttons.push(item.buttonExpand);
|
||||
}
|
||||
if (item.subItems && item == me.expandedItem) {
|
||||
item.subItems.forEach(function (subItem) {
|
||||
buttons.push(subItem.button);
|
||||
if (subItem.buttonExpand) {
|
||||
buttons.push(subItem.buttonExpand);
|
||||
}
|
||||
// TODO: change to fully recursive method
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return buttons;
|
||||
};
|
||||
|
||||
// currently displayed context menu, a singleton. We may only have one visible context menu
|
||||
ContextMenu.visibleMenu = undefined;
|
||||
|
||||
/**
|
||||
* Attach the menu to an anchor
|
||||
* @param {HTMLElement} anchor Anchor where the menu will be attached
|
||||
* as sibling.
|
||||
* @param {HTMLElement} [contentWindow] The DIV with with the (scrollable) contents
|
||||
*/
|
||||
ContextMenu.prototype.show = function (anchor, contentWindow) {
|
||||
this.hide();
|
||||
|
||||
// determine whether to display the menu below or above the anchor
|
||||
var showBelow = true;
|
||||
if (contentWindow) {
|
||||
var anchorRect = anchor.getBoundingClientRect();
|
||||
var contentRect = contentWindow.getBoundingClientRect();
|
||||
|
||||
if (anchorRect.bottom + this.maxHeight < contentRect.bottom) {
|
||||
// fits below -> show below
|
||||
}
|
||||
else if (anchorRect.top - this.maxHeight > contentRect.top) {
|
||||
// fits above -> show above
|
||||
showBelow = false;
|
||||
}
|
||||
else {
|
||||
// doesn't fit above nor below -> show below
|
||||
}
|
||||
}
|
||||
|
||||
// position the menu
|
||||
if (showBelow) {
|
||||
// display the menu below the anchor
|
||||
var anchorHeight = anchor.offsetHeight;
|
||||
this.dom.menu.style.left = '0px';
|
||||
this.dom.menu.style.top = anchorHeight + 'px';
|
||||
this.dom.menu.style.bottom = '';
|
||||
}
|
||||
else {
|
||||
// display the menu above the anchor
|
||||
this.dom.menu.style.left = '0px';
|
||||
this.dom.menu.style.top = '';
|
||||
this.dom.menu.style.bottom = '0px';
|
||||
}
|
||||
|
||||
// attach the menu to the parent of the anchor
|
||||
var parent = anchor.parentNode;
|
||||
parent.insertBefore(this.dom.root, parent.firstChild);
|
||||
|
||||
// create and attach event listeners
|
||||
var me = this;
|
||||
var list = this.dom.list;
|
||||
this.eventListeners.mousedown = util.addEventListener(window, 'mousedown', function (event) {
|
||||
// hide menu on click outside of the menu
|
||||
var target = event.target;
|
||||
if ((target != list) && !me._isChildOf(target, list)) {
|
||||
me.hide();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
this.eventListeners.keydown = util.addEventListener(window, 'keydown', function (event) {
|
||||
me._onKeyDown(event);
|
||||
});
|
||||
|
||||
// move focus to the first button in the context menu
|
||||
this.selection = util.getSelection();
|
||||
this.anchor = anchor;
|
||||
setTimeout(function () {
|
||||
me.dom.focusButton.focus();
|
||||
}, 0);
|
||||
|
||||
if (ContextMenu.visibleMenu) {
|
||||
ContextMenu.visibleMenu.hide();
|
||||
}
|
||||
ContextMenu.visibleMenu = this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the context menu if visible
|
||||
*/
|
||||
ContextMenu.prototype.hide = function () {
|
||||
// remove the menu from the DOM
|
||||
if (this.dom.root.parentNode) {
|
||||
this.dom.root.parentNode.removeChild(this.dom.root);
|
||||
if (this.onClose) {
|
||||
this.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
// remove all event listeners
|
||||
// all event listeners are supposed to be attached to document.
|
||||
for (var name in this.eventListeners) {
|
||||
if (this.eventListeners.hasOwnProperty(name)) {
|
||||
var fn = this.eventListeners[name];
|
||||
if (fn) {
|
||||
util.removeEventListener(window, name, fn);
|
||||
}
|
||||
delete this.eventListeners[name];
|
||||
}
|
||||
}
|
||||
|
||||
if (ContextMenu.visibleMenu == this) {
|
||||
ContextMenu.visibleMenu = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand a submenu
|
||||
* Any currently expanded submenu will be hided.
|
||||
* @param {Object} domItem
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._onExpandItem = function (domItem) {
|
||||
var me = this;
|
||||
var alreadyVisible = (domItem == this.expandedItem);
|
||||
|
||||
// hide the currently visible submenu
|
||||
var expandedItem = this.expandedItem;
|
||||
if (expandedItem) {
|
||||
//var ul = expandedItem.ul;
|
||||
expandedItem.ul.style.height = '0';
|
||||
expandedItem.ul.style.padding = '';
|
||||
setTimeout(function () {
|
||||
if (me.expandedItem != expandedItem) {
|
||||
expandedItem.ul.style.display = '';
|
||||
util.removeClassName(expandedItem.ul.parentNode, 'jsoneditor-selected');
|
||||
}
|
||||
}, 300); // timeout duration must match the css transition duration
|
||||
this.expandedItem = undefined;
|
||||
}
|
||||
|
||||
if (!alreadyVisible) {
|
||||
var ul = domItem.ul;
|
||||
ul.style.display = 'block';
|
||||
var height = ul.clientHeight; // force a reflow in Firefox
|
||||
setTimeout(function () {
|
||||
if (me.expandedItem == domItem) {
|
||||
ul.style.height = (ul.childNodes.length * 24) + 'px';
|
||||
ul.style.padding = '5px 10px';
|
||||
}
|
||||
}, 0);
|
||||
util.addClassName(ul.parentNode, 'jsoneditor-selected');
|
||||
this.expandedItem = domItem;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onkeydown event
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._onKeyDown = function (event) {
|
||||
var target = event.target;
|
||||
var keynum = event.which;
|
||||
var handled = false;
|
||||
var buttons, targetIndex, prevButton, nextButton;
|
||||
|
||||
if (keynum == 27) { // ESC
|
||||
// hide the menu on ESC key
|
||||
|
||||
// restore previous selection and focus
|
||||
if (this.selection) {
|
||||
util.setSelection(this.selection);
|
||||
}
|
||||
if (this.anchor) {
|
||||
this.anchor.focus();
|
||||
}
|
||||
|
||||
this.hide();
|
||||
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 9) { // Tab
|
||||
if (!event.shiftKey) { // Tab
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
if (targetIndex == buttons.length - 1) {
|
||||
// move to first button
|
||||
buttons[0].focus();
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else { // Shift+Tab
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
if (targetIndex == 0) {
|
||||
// move to last button
|
||||
buttons[buttons.length - 1].focus();
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (keynum == 37) { // Arrow Left
|
||||
if (target.className == 'jsoneditor-expand') {
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
prevButton = buttons[targetIndex - 1];
|
||||
if (prevButton) {
|
||||
prevButton.focus();
|
||||
}
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 38) { // Arrow Up
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
prevButton = buttons[targetIndex - 1];
|
||||
if (prevButton && prevButton.className == 'jsoneditor-expand') {
|
||||
// skip expand button
|
||||
prevButton = buttons[targetIndex - 2];
|
||||
}
|
||||
if (!prevButton) {
|
||||
// move to last button
|
||||
prevButton = buttons[buttons.length - 1];
|
||||
}
|
||||
if (prevButton) {
|
||||
prevButton.focus();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 39) { // Arrow Right
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
nextButton = buttons[targetIndex + 1];
|
||||
if (nextButton && nextButton.className == 'jsoneditor-expand') {
|
||||
nextButton.focus();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 40) { // Arrow Down
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
nextButton = buttons[targetIndex + 1];
|
||||
if (nextButton && nextButton.className == 'jsoneditor-expand') {
|
||||
// skip expand button
|
||||
nextButton = buttons[targetIndex + 2];
|
||||
}
|
||||
if (!nextButton) {
|
||||
// move to first button
|
||||
nextButton = buttons[0];
|
||||
}
|
||||
if (nextButton) {
|
||||
nextButton.focus();
|
||||
handled = true;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
// TODO: arrow left and right
|
||||
|
||||
if (handled) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Test if an element is a child of a parent element.
|
||||
* @param {Element} child
|
||||
* @param {Element} parent
|
||||
* @return {boolean} isChild
|
||||
*/
|
||||
ContextMenu.prototype._isChildOf = function (child, parent) {
|
||||
var e = child.parentNode;
|
||||
while (e) {
|
||||
if (e == parent) {
|
||||
return true;
|
||||
}
|
||||
e = e.parentNode;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
module.exports = ContextMenu;
|
|
@ -1,86 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* The highlighter can highlight/unhighlight a node, and
|
||||
* animate the visibility of a context menu.
|
||||
* @constructor Highlighter
|
||||
*/
|
||||
function Highlighter () {
|
||||
this.locked = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hightlight given node and its childs
|
||||
* @param {Node} node
|
||||
*/
|
||||
Highlighter.prototype.highlight = function (node) {
|
||||
if (this.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.node != node) {
|
||||
// unhighlight current node
|
||||
if (this.node) {
|
||||
this.node.setHighlight(false);
|
||||
}
|
||||
|
||||
// highlight new node
|
||||
this.node = node;
|
||||
this.node.setHighlight(true);
|
||||
}
|
||||
|
||||
// cancel any current timeout
|
||||
this._cancelUnhighlight();
|
||||
};
|
||||
|
||||
/**
|
||||
* Unhighlight currently highlighted node.
|
||||
* Will be done after a delay
|
||||
*/
|
||||
Highlighter.prototype.unhighlight = function () {
|
||||
if (this.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
var me = this;
|
||||
if (this.node) {
|
||||
this._cancelUnhighlight();
|
||||
|
||||
// do the unhighlighting after a small delay, to prevent re-highlighting
|
||||
// the same node when moving from the drag-icon to the contextmenu-icon
|
||||
// or vice versa.
|
||||
this.unhighlightTimer = setTimeout(function () {
|
||||
me.node.setHighlight(false);
|
||||
me.node = undefined;
|
||||
me.unhighlightTimer = undefined;
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel an unhighlight action (if before the timeout of the unhighlight action)
|
||||
* @private
|
||||
*/
|
||||
Highlighter.prototype._cancelUnhighlight = function () {
|
||||
if (this.unhighlightTimer) {
|
||||
clearTimeout(this.unhighlightTimer);
|
||||
this.unhighlightTimer = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Lock highlighting or unhighlighting nodes.
|
||||
* methods highlight and unhighlight do not work while locked.
|
||||
*/
|
||||
Highlighter.prototype.lock = function () {
|
||||
this.locked = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlock highlighting or unhighlighting nodes
|
||||
*/
|
||||
Highlighter.prototype.unlock = function () {
|
||||
this.locked = false;
|
||||
};
|
||||
|
||||
module.exports = Highlighter;
|
|
@ -1,267 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* @constructor History
|
||||
* Store action history, enables undo and redo
|
||||
* @param {JSONEditor} editor
|
||||
*/
|
||||
function History (editor) {
|
||||
this.editor = editor;
|
||||
this.history = [];
|
||||
this.index = -1;
|
||||
|
||||
this.clear();
|
||||
|
||||
// map with all supported actions
|
||||
this.actions = {
|
||||
'editField': {
|
||||
'undo': function (params) {
|
||||
params.node.updateField(params.oldValue);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.updateField(params.newValue);
|
||||
}
|
||||
},
|
||||
'editValue': {
|
||||
'undo': function (params) {
|
||||
params.node.updateValue(params.oldValue);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.updateValue(params.newValue);
|
||||
}
|
||||
},
|
||||
'changeType': {
|
||||
'undo': function (params) {
|
||||
params.node.changeType(params.oldType);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.changeType(params.newType);
|
||||
}
|
||||
},
|
||||
|
||||
'appendNodes': {
|
||||
'undo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.removeChild(node);
|
||||
});
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.appendChild(node);
|
||||
});
|
||||
}
|
||||
},
|
||||
'insertBeforeNodes': {
|
||||
'undo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.removeChild(node);
|
||||
});
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.insertBefore(node, params.beforeNode);
|
||||
});
|
||||
}
|
||||
},
|
||||
'insertAfterNodes': {
|
||||
'undo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.removeChild(node);
|
||||
});
|
||||
},
|
||||
'redo': function (params) {
|
||||
var afterNode = params.afterNode;
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.insertAfter(params.node, afterNode);
|
||||
afterNode = node;
|
||||
});
|
||||
}
|
||||
},
|
||||
'removeNodes': {
|
||||
'undo': function (params) {
|
||||
var parent = params.parent;
|
||||
var beforeNode = parent.childs[params.index] || parent.append;
|
||||
params.nodes.forEach(function (node) {
|
||||
parent.insertBefore(node, beforeNode);
|
||||
});
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.removeChild(node);
|
||||
});
|
||||
}
|
||||
},
|
||||
'duplicateNodes': {
|
||||
'undo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.removeChild(node);
|
||||
});
|
||||
},
|
||||
'redo': function (params) {
|
||||
var afterNode = params.afterNode;
|
||||
params.nodes.forEach(function (node) {
|
||||
params.parent.insertAfter(node, afterNode);
|
||||
afterNode = node;
|
||||
});
|
||||
}
|
||||
},
|
||||
'moveNodes': {
|
||||
'undo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.oldBeforeNode.parent.moveBefore(node, params.oldBeforeNode);
|
||||
});
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.nodes.forEach(function (node) {
|
||||
params.newBeforeNode.parent.moveBefore(node, params.newBeforeNode);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
'sort': {
|
||||
'undo': function (params) {
|
||||
var node = params.node;
|
||||
node.hideChilds();
|
||||
node.sort = params.oldSort;
|
||||
node.childs = params.oldChilds;
|
||||
node.showChilds();
|
||||
},
|
||||
'redo': function (params) {
|
||||
var node = params.node;
|
||||
node.hideChilds();
|
||||
node.sort = params.newSort;
|
||||
node.childs = params.newChilds;
|
||||
node.showChilds();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: restore the original caret position and selection with each undo
|
||||
// TODO: implement history for actions "expand", "collapse", "scroll", "setDocument"
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The method onChange is executed when the History is changed, and can
|
||||
* be overloaded.
|
||||
*/
|
||||
History.prototype.onChange = function () {};
|
||||
|
||||
/**
|
||||
* Add a new action to the history
|
||||
* @param {String} action The executed action. Available actions: "editField",
|
||||
* "editValue", "changeType", "appendNode",
|
||||
* "removeNode", "duplicateNode", "moveNode"
|
||||
* @param {Object} params Object containing parameters describing the change.
|
||||
* The parameters in params depend on the action (for
|
||||
* example for "editValue" the Node, old value, and new
|
||||
* value are provided). params contains all information
|
||||
* needed to undo or redo the action.
|
||||
*/
|
||||
History.prototype.add = function (action, params) {
|
||||
this.index++;
|
||||
this.history[this.index] = {
|
||||
'action': action,
|
||||
'params': params,
|
||||
'timestamp': new Date()
|
||||
};
|
||||
|
||||
// remove redo actions which are invalid now
|
||||
if (this.index < this.history.length - 1) {
|
||||
this.history.splice(this.index + 1, this.history.length - this.index - 1);
|
||||
}
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear history
|
||||
*/
|
||||
History.prototype.clear = function () {
|
||||
this.history = [];
|
||||
this.index = -1;
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is an action available for undo
|
||||
* @return {Boolean} canUndo
|
||||
*/
|
||||
History.prototype.canUndo = function () {
|
||||
return (this.index >= 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is an action available for redo
|
||||
* @return {Boolean} canRedo
|
||||
*/
|
||||
History.prototype.canRedo = function () {
|
||||
return (this.index < this.history.length - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo the last action
|
||||
*/
|
||||
History.prototype.undo = function () {
|
||||
if (this.canUndo()) {
|
||||
var obj = this.history[this.index];
|
||||
if (obj) {
|
||||
var action = this.actions[obj.action];
|
||||
if (action && action.undo) {
|
||||
action.undo(obj.params);
|
||||
if (obj.params.oldSelection) {
|
||||
this.editor.setSelection(obj.params.oldSelection);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.error(new Error('unknown action "' + obj.action + '"'));
|
||||
}
|
||||
}
|
||||
this.index--;
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redo the last action
|
||||
*/
|
||||
History.prototype.redo = function () {
|
||||
if (this.canRedo()) {
|
||||
this.index++;
|
||||
|
||||
var obj = this.history[this.index];
|
||||
if (obj) {
|
||||
var action = this.actions[obj.action];
|
||||
if (action && action.redo) {
|
||||
action.redo(obj.params);
|
||||
if (obj.params.newSelection) {
|
||||
this.editor.setSelection(obj.params.newSelection);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.error(new Error('unknown action "' + obj.action + '"'));
|
||||
}
|
||||
}
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy history
|
||||
*/
|
||||
History.prototype.destroy = function () {
|
||||
this.editor = null;
|
||||
|
||||
this.history = [];
|
||||
this.index = -1;
|
||||
};
|
||||
|
||||
module.exports = History;
|
|
@ -1,385 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Ajv;
|
||||
try {
|
||||
Ajv = require('ajv');
|
||||
}
|
||||
catch (err) {
|
||||
// no problem... when we need Ajv we will throw a neat exception
|
||||
}
|
||||
|
||||
var treemode = require('./treemode');
|
||||
var textmode = require('./textmode');
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* @constructor JSONEditor
|
||||
* @param {Element} container Container element
|
||||
* @param {Object} [options] Object with options. available options:
|
||||
* {String} mode Editor mode. Available values:
|
||||
* 'tree' (default), 'view',
|
||||
* 'form', 'text', and 'code'.
|
||||
* {function} onChange Callback method, triggered
|
||||
* on change of contents
|
||||
* {function} onError Callback method, triggered
|
||||
* when an error occurs
|
||||
* {Boolean} search Enable search box.
|
||||
* True by default
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {Boolean} history Enable history (undo/redo).
|
||||
* True by default
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {String} name Field name for the root node.
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {Number} indentation Number of indentation
|
||||
* spaces. 4 by default.
|
||||
* Only applicable for
|
||||
* modes 'text' and 'code'
|
||||
* {boolean} escapeUnicode If true, unicode
|
||||
* characters are escaped.
|
||||
* false by default.
|
||||
* {boolean} sortObjectKeys If true, object keys are
|
||||
* sorted before display.
|
||||
* false by default.
|
||||
* @param {Object | undefined} json JSON object
|
||||
*/
|
||||
function JSONEditor (container, options, json) {
|
||||
if (!(this instanceof JSONEditor)) {
|
||||
throw new Error('JSONEditor constructor called without "new".');
|
||||
}
|
||||
|
||||
// check for unsupported browser (IE8 and older)
|
||||
var ieVersion = util.getInternetExplorerVersion();
|
||||
if (ieVersion != -1 && ieVersion < 9) {
|
||||
throw new Error('Unsupported browser, IE9 or newer required. ' +
|
||||
'Please install the newest version of your browser.');
|
||||
}
|
||||
|
||||
if (options) {
|
||||
// check for deprecated options
|
||||
if (options.error) {
|
||||
console.warn('Option "error" has been renamed to "onError"');
|
||||
options.onError = options.error;
|
||||
delete options.error;
|
||||
}
|
||||
if (options.change) {
|
||||
console.warn('Option "change" has been renamed to "onChange"');
|
||||
options.onChange = options.change;
|
||||
delete options.change;
|
||||
}
|
||||
if (options.editable) {
|
||||
console.warn('Option "editable" has been renamed to "onEditable"');
|
||||
options.onEditable = options.editable;
|
||||
delete options.editable;
|
||||
}
|
||||
|
||||
// validate options
|
||||
if (options) {
|
||||
var VALID_OPTIONS = [
|
||||
'ace', 'theme',
|
||||
'ajv', 'schema',
|
||||
'onChange', 'onEditable', 'onError', 'onModeChange',
|
||||
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation', 'sortObjectKeys'
|
||||
];
|
||||
|
||||
Object.keys(options).forEach(function (option) {
|
||||
if (VALID_OPTIONS.indexOf(option) === -1) {
|
||||
console.warn('Unknown option "' + option + '". This option will be ignored');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (arguments.length) {
|
||||
this._create(container, options, json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for all registered modes. Example:
|
||||
* {
|
||||
* tree: {
|
||||
* mixin: TreeEditor,
|
||||
* data: 'json'
|
||||
* },
|
||||
* text: {
|
||||
* mixin: TextEditor,
|
||||
* data: 'text'
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @type { Object.<String, {mixin: Object, data: String} > }
|
||||
*/
|
||||
JSONEditor.modes = {};
|
||||
|
||||
// debounce interval for JSON schema vaidation in milliseconds
|
||||
JSONEditor.prototype.DEBOUNCE_INTERVAL = 150;
|
||||
|
||||
/**
|
||||
* Create the JSONEditor
|
||||
* @param {Element} container Container element
|
||||
* @param {Object} [options] See description in constructor
|
||||
* @param {Object | undefined} json JSON object
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._create = function (container, options, json) {
|
||||
this.container = container;
|
||||
this.options = options || {};
|
||||
this.json = json || {};
|
||||
|
||||
var mode = this.options.mode || 'tree';
|
||||
this.setMode(mode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
||||
*/
|
||||
JSONEditor.prototype.destroy = function () {};
|
||||
|
||||
/**
|
||||
* Set JSON object in editor
|
||||
* @param {Object | undefined} json JSON data
|
||||
*/
|
||||
JSONEditor.prototype.set = function (json) {
|
||||
this.json = json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get JSON from the editor
|
||||
* @returns {Object} json
|
||||
*/
|
||||
JSONEditor.prototype.get = function () {
|
||||
return this.json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set string containing JSON for the editor
|
||||
* @param {String | undefined} jsonText
|
||||
*/
|
||||
JSONEditor.prototype.setText = function (jsonText) {
|
||||
this.json = util.parse(jsonText);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get stringified JSON contents from the editor
|
||||
* @returns {String} jsonText
|
||||
*/
|
||||
JSONEditor.prototype.getText = function () {
|
||||
return JSON.stringify(this.json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a field name for the root node.
|
||||
* @param {String | undefined} name
|
||||
*/
|
||||
JSONEditor.prototype.setName = function (name) {
|
||||
if (!this.options) {
|
||||
this.options = {};
|
||||
}
|
||||
this.options.name = name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the field name for the root node.
|
||||
* @return {String | undefined} name
|
||||
*/
|
||||
JSONEditor.prototype.getName = function () {
|
||||
return this.options && this.options.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the mode of the editor.
|
||||
* JSONEditor will be extended with all methods needed for the chosen mode.
|
||||
* @param {String} mode Available modes: 'tree' (default), 'view', 'form',
|
||||
* 'text', and 'code'.
|
||||
*/
|
||||
JSONEditor.prototype.setMode = function (mode) {
|
||||
var container = this.container;
|
||||
var options = util.extend({}, this.options);
|
||||
var oldMode = options.mode;
|
||||
var data;
|
||||
var name;
|
||||
|
||||
options.mode = mode;
|
||||
var config = JSONEditor.modes[mode];
|
||||
if (config) {
|
||||
try {
|
||||
var asText = (config.data == 'text');
|
||||
name = this.getName();
|
||||
data = this[asText ? 'getText' : 'get'](); // get text or json
|
||||
|
||||
this.destroy();
|
||||
util.clear(this);
|
||||
util.extend(this, config.mixin);
|
||||
this.create(container, options);
|
||||
|
||||
this.setName(name);
|
||||
this[asText ? 'setText' : 'set'](data); // set text or json
|
||||
|
||||
if (typeof config.load === 'function') {
|
||||
try {
|
||||
config.load.call(this);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof options.onModeChange === 'function' && mode !== oldMode) {
|
||||
try {
|
||||
options.onModeChange(mode, oldMode);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
this._onError(err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error('Unknown mode "' + options.mode + '"');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current mode
|
||||
* @return {string}
|
||||
*/
|
||||
JSONEditor.prototype.getMode = function () {
|
||||
return this.options.mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Throw an error. If an error callback is configured in options.error, this
|
||||
* callback will be invoked. Else, a regular error is thrown.
|
||||
* @param {Error} err
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._onError = function(err) {
|
||||
if (this.options && typeof this.options.onError === 'function') {
|
||||
this.options.onError(err);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a JSON schema for validation of the JSON object.
|
||||
* To remove the schema, call JSONEditor.setSchema(null)
|
||||
* @param {Object | null} schema
|
||||
*/
|
||||
JSONEditor.prototype.setSchema = function (schema) {
|
||||
// compile a JSON schema validator if a JSON schema is provided
|
||||
if (schema) {
|
||||
var ajv;
|
||||
try {
|
||||
// grab ajv from options if provided, else create a new instance
|
||||
ajv = this.options.ajv || Ajv({ allErrors: true, verbose: true });
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('Failed to create an instance of Ajv, JSON Schema validation is not available. Please use a JSONEditor bundle including Ajv, or pass an instance of Ajv as via the configuration option `ajv`.');
|
||||
}
|
||||
|
||||
if (ajv) {
|
||||
this.validateSchema = ajv.compile(schema);
|
||||
|
||||
// add schema to the options, so that when switching to an other mode,
|
||||
// the set schema is not lost
|
||||
this.options.schema = schema;
|
||||
|
||||
// validate now
|
||||
this.validate();
|
||||
}
|
||||
|
||||
this.refresh(); // update DOM
|
||||
}
|
||||
else {
|
||||
// remove current schema
|
||||
this.validateSchema = null;
|
||||
this.options.schema = null;
|
||||
this.validate(); // to clear current error messages
|
||||
this.refresh(); // update DOM
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate current JSON object against the configured JSON schema
|
||||
* Throws an exception when no JSON schema is configured
|
||||
*/
|
||||
JSONEditor.prototype.validate = function () {
|
||||
// must be implemented by treemode and textmode
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh the rendered contents
|
||||
*/
|
||||
JSONEditor.prototype.refresh = function () {
|
||||
// can be implemented by treemode and textmode
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a plugin with one ore multiple modes for the JSON Editor.
|
||||
*
|
||||
* A mode is described as an object with properties:
|
||||
*
|
||||
* - `mode: String` The name of the mode.
|
||||
* - `mixin: Object` An object containing the mixin functions which
|
||||
* will be added to the JSONEditor. Must contain functions
|
||||
* create, get, getText, set, and setText. May have
|
||||
* additional functions.
|
||||
* When the JSONEditor switches to a mixin, all mixin
|
||||
* functions are added to the JSONEditor, and then
|
||||
* the function `create(container, options)` is executed.
|
||||
* - `data: 'text' | 'json'` The type of data that will be used to load the mixin.
|
||||
* - `[load: function]` An optional function called after the mixin
|
||||
* has been loaded.
|
||||
*
|
||||
* @param {Object | Array} mode A mode object or an array with multiple mode objects.
|
||||
*/
|
||||
JSONEditor.registerMode = function (mode) {
|
||||
var i, prop;
|
||||
|
||||
if (util.isArray(mode)) {
|
||||
// multiple modes
|
||||
for (i = 0; i < mode.length; i++) {
|
||||
JSONEditor.registerMode(mode[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// validate the new mode
|
||||
if (!('mode' in mode)) throw new Error('Property "mode" missing');
|
||||
if (!('mixin' in mode)) throw new Error('Property "mixin" missing');
|
||||
if (!('data' in mode)) throw new Error('Property "data" missing');
|
||||
var name = mode.mode;
|
||||
if (name in JSONEditor.modes) {
|
||||
throw new Error('Mode "' + name + '" already registered');
|
||||
}
|
||||
|
||||
// validate the mixin
|
||||
if (typeof mode.mixin.create !== 'function') {
|
||||
throw new Error('Required function "create" missing on mixin');
|
||||
}
|
||||
var reserved = ['setMode', 'registerMode', 'modes'];
|
||||
for (i = 0; i < reserved.length; i++) {
|
||||
prop = reserved[i];
|
||||
if (prop in mode.mixin) {
|
||||
throw new Error('Reserved property "' + prop + '" not allowed in mixin');
|
||||
}
|
||||
}
|
||||
|
||||
JSONEditor.modes[name] = mode;
|
||||
}
|
||||
};
|
||||
|
||||
// register tree and text modes
|
||||
JSONEditor.registerMode(treemode);
|
||||
JSONEditor.registerMode(textmode);
|
||||
|
||||
module.exports = JSONEditor;
|
|
@ -1,114 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ContextMenu = require('./ContextMenu');
|
||||
|
||||
/**
|
||||
* Create a select box to be used in the editor menu's, which allows to switch mode
|
||||
* @param {HTMLElement} container
|
||||
* @param {String[]} modes Available modes: 'code', 'form', 'text', 'tree', 'view'
|
||||
* @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view'
|
||||
* @param {function(mode: string)} onSwitch Callback invoked on switch
|
||||
* @constructor
|
||||
*/
|
||||
function ModeSwitcher(container, modes, current, onSwitch) {
|
||||
// available modes
|
||||
var availableModes = {
|
||||
code: {
|
||||
'text': 'Code',
|
||||
'title': 'Switch to code highlighter',
|
||||
'click': function () {
|
||||
onSwitch('code')
|
||||
}
|
||||
},
|
||||
form: {
|
||||
'text': 'Form',
|
||||
'title': 'Switch to form editor',
|
||||
'click': function () {
|
||||
onSwitch('form');
|
||||
}
|
||||
},
|
||||
text: {
|
||||
'text': 'Text',
|
||||
'title': 'Switch to plain text editor',
|
||||
'click': function () {
|
||||
onSwitch('text');
|
||||
}
|
||||
},
|
||||
tree: {
|
||||
'text': 'Tree',
|
||||
'title': 'Switch to tree editor',
|
||||
'click': function () {
|
||||
onSwitch('tree');
|
||||
}
|
||||
},
|
||||
view: {
|
||||
'text': 'View',
|
||||
'title': 'Switch to tree view',
|
||||
'click': function () {
|
||||
onSwitch('view');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// list the selected modes
|
||||
var items = [];
|
||||
for (var i = 0; i < modes.length; i++) {
|
||||
var mode = modes[i];
|
||||
var item = availableModes[mode];
|
||||
if (!item) {
|
||||
throw new Error('Unknown mode "' + mode + '"');
|
||||
}
|
||||
|
||||
item.className = 'jsoneditor-type-modes' + ((current == mode) ? ' jsoneditor-selected' : '');
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
// retrieve the title of current mode
|
||||
var currentMode = availableModes[current];
|
||||
if (!currentMode) {
|
||||
throw new Error('Unknown mode "' + current + '"');
|
||||
}
|
||||
var currentTitle = currentMode.text;
|
||||
|
||||
// create the html element
|
||||
var box = document.createElement('button');
|
||||
box.className = 'jsoneditor-modes jsoneditor-separator';
|
||||
box.innerHTML = currentTitle + ' ▾';
|
||||
box.title = 'Switch editor mode';
|
||||
box.onclick = function () {
|
||||
var menu = new ContextMenu(items);
|
||||
menu.show(box);
|
||||
};
|
||||
|
||||
var frame = document.createElement('div');
|
||||
frame.className = 'jsoneditor-modes';
|
||||
frame.style.position = 'relative';
|
||||
frame.appendChild(box);
|
||||
|
||||
container.appendChild(frame);
|
||||
|
||||
this.dom = {
|
||||
container: container,
|
||||
box: box,
|
||||
frame: frame
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set focus to switcher
|
||||
*/
|
||||
ModeSwitcher.prototype.focus = function () {
|
||||
this.dom.box.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the ModeSwitcher, remove from DOM
|
||||
*/
|
||||
ModeSwitcher.prototype.destroy = function () {
|
||||
if (this.dom && this.dom.frame && this.dom.frame.parentNode) {
|
||||
this.dom.frame.parentNode.removeChild(this.dom.frame);
|
||||
}
|
||||
this.dom = null;
|
||||
};
|
||||
|
||||
module.exports = ModeSwitcher;
|
3526
src/js/Node.js
3526
src/js/Node.js
File diff suppressed because it is too large
Load Diff
|
@ -1,312 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @constructor SearchBox
|
||||
* Create a search box in given HTML container
|
||||
* @param {JSONEditor} editor The JSON Editor to attach to
|
||||
* @param {Element} container HTML container element of where to
|
||||
* create the search box
|
||||
*/
|
||||
function SearchBox (editor, container) {
|
||||
var searchBox = this;
|
||||
|
||||
this.editor = editor;
|
||||
this.timeout = undefined;
|
||||
this.delay = 200; // ms
|
||||
this.lastText = undefined;
|
||||
|
||||
this.dom = {};
|
||||
this.dom.container = container;
|
||||
|
||||
var table = document.createElement('table');
|
||||
this.dom.table = table;
|
||||
table.className = 'jsoneditor-search';
|
||||
container.appendChild(table);
|
||||
var tbody = document.createElement('tbody');
|
||||
this.dom.tbody = tbody;
|
||||
table.appendChild(tbody);
|
||||
var tr = document.createElement('tr');
|
||||
tbody.appendChild(tr);
|
||||
|
||||
var td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
var results = document.createElement('div');
|
||||
this.dom.results = results;
|
||||
results.className = 'jsoneditor-results';
|
||||
td.appendChild(results);
|
||||
|
||||
td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
var divInput = document.createElement('div');
|
||||
this.dom.input = divInput;
|
||||
divInput.className = 'jsoneditor-frame';
|
||||
divInput.title = 'Search fields and values';
|
||||
td.appendChild(divInput);
|
||||
|
||||
// table to contain the text input and search button
|
||||
var tableInput = document.createElement('table');
|
||||
divInput.appendChild(tableInput);
|
||||
var tbodySearch = document.createElement('tbody');
|
||||
tableInput.appendChild(tbodySearch);
|
||||
tr = document.createElement('tr');
|
||||
tbodySearch.appendChild(tr);
|
||||
|
||||
var refreshSearch = document.createElement('button');
|
||||
refreshSearch.className = 'jsoneditor-refresh';
|
||||
td = document.createElement('td');
|
||||
td.appendChild(refreshSearch);
|
||||
tr.appendChild(td);
|
||||
|
||||
var search = document.createElement('input');
|
||||
this.dom.search = search;
|
||||
search.oninput = function (event) {
|
||||
searchBox._onDelayedSearch(event);
|
||||
};
|
||||
search.onchange = function (event) { // For IE 9
|
||||
searchBox._onSearch();
|
||||
};
|
||||
search.onkeydown = function (event) {
|
||||
searchBox._onKeyDown(event);
|
||||
};
|
||||
search.onkeyup = function (event) {
|
||||
searchBox._onKeyUp(event);
|
||||
};
|
||||
refreshSearch.onclick = function (event) {
|
||||
search.select();
|
||||
};
|
||||
|
||||
// TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819
|
||||
td = document.createElement('td');
|
||||
td.appendChild(search);
|
||||
tr.appendChild(td);
|
||||
|
||||
var searchNext = document.createElement('button');
|
||||
searchNext.title = 'Next result (Enter)';
|
||||
searchNext.className = 'jsoneditor-next';
|
||||
searchNext.onclick = function () {
|
||||
searchBox.next();
|
||||
};
|
||||
td = document.createElement('td');
|
||||
td.appendChild(searchNext);
|
||||
tr.appendChild(td);
|
||||
|
||||
var searchPrevious = document.createElement('button');
|
||||
searchPrevious.title = 'Previous result (Shift+Enter)';
|
||||
searchPrevious.className = 'jsoneditor-previous';
|
||||
searchPrevious.onclick = function () {
|
||||
searchBox.previous();
|
||||
};
|
||||
td = document.createElement('td');
|
||||
td.appendChild(searchPrevious);
|
||||
tr.appendChild(td);
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the next search result
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result
|
||||
* focus is false by default.
|
||||
*/
|
||||
SearchBox.prototype.next = function(focus) {
|
||||
if (this.results != undefined) {
|
||||
var index = (this.resultIndex != undefined) ? this.resultIndex + 1 : 0;
|
||||
if (index > this.results.length - 1) {
|
||||
index = 0;
|
||||
}
|
||||
this._setActiveResult(index, focus);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Go to the prevous search result
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result
|
||||
* focus is false by default.
|
||||
*/
|
||||
SearchBox.prototype.previous = function(focus) {
|
||||
if (this.results != undefined) {
|
||||
var max = this.results.length - 1;
|
||||
var index = (this.resultIndex != undefined) ? this.resultIndex - 1 : max;
|
||||
if (index < 0) {
|
||||
index = max;
|
||||
}
|
||||
this._setActiveResult(index, focus);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set new value for the current active result
|
||||
* @param {Number} index
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result.
|
||||
* focus is false by default.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._setActiveResult = function(index, focus) {
|
||||
// de-activate current active result
|
||||
if (this.activeResult) {
|
||||
var prevNode = this.activeResult.node;
|
||||
var prevElem = this.activeResult.elem;
|
||||
if (prevElem == 'field') {
|
||||
delete prevNode.searchFieldActive;
|
||||
}
|
||||
else {
|
||||
delete prevNode.searchValueActive;
|
||||
}
|
||||
prevNode.updateDom();
|
||||
}
|
||||
|
||||
if (!this.results || !this.results[index]) {
|
||||
// out of range, set to undefined
|
||||
this.resultIndex = undefined;
|
||||
this.activeResult = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
this.resultIndex = index;
|
||||
|
||||
// set new node active
|
||||
var node = this.results[this.resultIndex].node;
|
||||
var elem = this.results[this.resultIndex].elem;
|
||||
if (elem == 'field') {
|
||||
node.searchFieldActive = true;
|
||||
}
|
||||
else {
|
||||
node.searchValueActive = true;
|
||||
}
|
||||
this.activeResult = this.results[this.resultIndex];
|
||||
node.updateDom();
|
||||
|
||||
// TODO: not so nice that the focus is only set after the animation is finished
|
||||
node.scrollTo(function () {
|
||||
if (focus) {
|
||||
node.focus(elem);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel any running onDelayedSearch.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._clearDelay = function() {
|
||||
if (this.timeout != undefined) {
|
||||
clearTimeout(this.timeout);
|
||||
delete this.timeout;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a timer to execute a search after a short delay.
|
||||
* Used for reducing the number of searches while typing.
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onDelayedSearch = function (event) {
|
||||
// execute the search after a short delay (reduces the number of
|
||||
// search actions while typing in the search text box)
|
||||
this._clearDelay();
|
||||
var searchBox = this;
|
||||
this.timeout = setTimeout(function (event) {
|
||||
searchBox._onSearch();
|
||||
},
|
||||
this.delay);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onSearch event
|
||||
* @param {boolean} [forceSearch] If true, search will be executed again even
|
||||
* when the search text is not changed.
|
||||
* Default is false.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onSearch = function (forceSearch) {
|
||||
this._clearDelay();
|
||||
|
||||
var value = this.dom.search.value;
|
||||
var text = (value.length > 0) ? value : undefined;
|
||||
if (text != this.lastText || forceSearch) {
|
||||
// only search again when changed
|
||||
this.lastText = text;
|
||||
this.results = this.editor.search(text);
|
||||
this._setActiveResult(undefined);
|
||||
|
||||
// display search results
|
||||
if (text != undefined) {
|
||||
var resultCount = this.results.length;
|
||||
switch (resultCount) {
|
||||
case 0: this.dom.results.innerHTML = 'no results'; break;
|
||||
case 1: this.dom.results.innerHTML = '1 result'; break;
|
||||
default: this.dom.results.innerHTML = resultCount + ' results'; break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.dom.results.innerHTML = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onKeyDown event in the input box
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onKeyDown = function (event) {
|
||||
var keynum = event.which;
|
||||
if (keynum == 27) { // ESC
|
||||
this.dom.search.value = ''; // clear search
|
||||
this._onSearch();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
else if (keynum == 13) { // Enter
|
||||
if (event.ctrlKey) {
|
||||
// force to search again
|
||||
this._onSearch(true);
|
||||
}
|
||||
else if (event.shiftKey) {
|
||||
// move to the previous search result
|
||||
this.previous();
|
||||
}
|
||||
else {
|
||||
// move to the next search result
|
||||
this.next();
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onKeyUp event in the input box
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onKeyUp = function (event) {
|
||||
var keynum = event.keyCode;
|
||||
if (keynum != 27 && keynum != 13) { // !show and !Enter
|
||||
this._onDelayedSearch(event); // For IE 9
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the search results
|
||||
*/
|
||||
SearchBox.prototype.clear = function () {
|
||||
this.dom.search.value = '';
|
||||
this._onSearch();
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the search box
|
||||
*/
|
||||
SearchBox.prototype.destroy = function () {
|
||||
this.editor = null;
|
||||
this.dom.container.removeChild(this.dom.table);
|
||||
this.dom = null;
|
||||
|
||||
this.results = null;
|
||||
this.activeResult = null;
|
||||
|
||||
this._clearDelay();
|
||||
|
||||
};
|
||||
|
||||
module.exports = SearchBox;
|
|
@ -1,9 +0,0 @@
|
|||
// load brace
|
||||
var ace = require('brace');
|
||||
|
||||
// load required ace modules
|
||||
require('brace/mode/json');
|
||||
require('brace/ext/searchbox');
|
||||
require('./theme-jsoneditor');
|
||||
|
||||
module.exports = ace;
|
|
@ -1,144 +0,0 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
ace.define('ace/theme/jsoneditor', ['require', 'exports', 'module', 'ace/lib/dom'], function(acequire, exports, module) {
|
||||
|
||||
exports.isDark = false;
|
||||
exports.cssClass = "ace-jsoneditor";
|
||||
exports.cssText = ".ace-jsoneditor .ace_gutter {\
|
||||
background: #ebebeb;\
|
||||
color: #333\
|
||||
}\
|
||||
\
|
||||
.ace-jsoneditor.ace_editor {\
|
||||
font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;\
|
||||
line-height: 1.3;\
|
||||
}\
|
||||
.ace-jsoneditor .ace_print-margin {\
|
||||
width: 1px;\
|
||||
background: #e8e8e8\
|
||||
}\
|
||||
.ace-jsoneditor .ace_scroller {\
|
||||
background-color: #FFFFFF\
|
||||
}\
|
||||
.ace-jsoneditor .ace_text-layer {\
|
||||
color: gray\
|
||||
}\
|
||||
.ace-jsoneditor .ace_variable {\
|
||||
color: #1a1a1a\
|
||||
}\
|
||||
.ace-jsoneditor .ace_cursor {\
|
||||
border-left: 2px solid #000000\
|
||||
}\
|
||||
.ace-jsoneditor .ace_overwrite-cursors .ace_cursor {\
|
||||
border-left: 0px;\
|
||||
border-bottom: 1px solid #000000\
|
||||
}\
|
||||
.ace-jsoneditor .ace_marker-layer .ace_selection {\
|
||||
background: lightgray\
|
||||
}\
|
||||
.ace-jsoneditor.ace_multiselect .ace_selection.ace_start {\
|
||||
box-shadow: 0 0 3px 0px #FFFFFF;\
|
||||
border-radius: 2px\
|
||||
}\
|
||||
.ace-jsoneditor .ace_marker-layer .ace_step {\
|
||||
background: rgb(255, 255, 0)\
|
||||
}\
|
||||
.ace-jsoneditor .ace_marker-layer .ace_bracket {\
|
||||
margin: -1px 0 0 -1px;\
|
||||
border: 1px solid #BFBFBF\
|
||||
}\
|
||||
.ace-jsoneditor .ace_marker-layer .ace_active-line {\
|
||||
background: #FFFBD1\
|
||||
}\
|
||||
.ace-jsoneditor .ace_gutter-active-line {\
|
||||
background-color : #dcdcdc\
|
||||
}\
|
||||
.ace-jsoneditor .ace_marker-layer .ace_selected-word {\
|
||||
border: 1px solid lightgray\
|
||||
}\
|
||||
.ace-jsoneditor .ace_invisible {\
|
||||
color: #BFBFBF\
|
||||
}\
|
||||
.ace-jsoneditor .ace_keyword,\
|
||||
.ace-jsoneditor .ace_meta,\
|
||||
.ace-jsoneditor .ace_support.ace_constant.ace_property-value {\
|
||||
color: #AF956F\
|
||||
}\
|
||||
.ace-jsoneditor .ace_keyword.ace_operator {\
|
||||
color: #484848\
|
||||
}\
|
||||
.ace-jsoneditor .ace_keyword.ace_other.ace_unit {\
|
||||
color: #96DC5F\
|
||||
}\
|
||||
.ace-jsoneditor .ace_constant.ace_language {\
|
||||
color: darkorange\
|
||||
}\
|
||||
.ace-jsoneditor .ace_constant.ace_numeric {\
|
||||
color: red\
|
||||
}\
|
||||
.ace-jsoneditor .ace_constant.ace_character.ace_entity {\
|
||||
color: #BF78CC\
|
||||
}\
|
||||
.ace-jsoneditor .ace_invalid {\
|
||||
color: #FFFFFF;\
|
||||
background-color: #FF002A;\
|
||||
}\
|
||||
.ace-jsoneditor .ace_fold {\
|
||||
background-color: #AF956F;\
|
||||
border-color: #000000\
|
||||
}\
|
||||
.ace-jsoneditor .ace_storage,\
|
||||
.ace-jsoneditor .ace_support.ace_class,\
|
||||
.ace-jsoneditor .ace_support.ace_function,\
|
||||
.ace-jsoneditor .ace_support.ace_other,\
|
||||
.ace-jsoneditor .ace_support.ace_type {\
|
||||
color: #C52727\
|
||||
}\
|
||||
.ace-jsoneditor .ace_string {\
|
||||
color: green\
|
||||
}\
|
||||
.ace-jsoneditor .ace_comment {\
|
||||
color: #BCC8BA\
|
||||
}\
|
||||
.ace-jsoneditor .ace_entity.ace_name.ace_tag,\
|
||||
.ace-jsoneditor .ace_entity.ace_other.ace_attribute-name {\
|
||||
color: #606060\
|
||||
}\
|
||||
.ace-jsoneditor .ace_markup.ace_underline {\
|
||||
text-decoration: underline\
|
||||
}\
|
||||
.ace-jsoneditor .ace_indent-guide {\
|
||||
background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y\
|
||||
}";
|
||||
|
||||
var dom = acequire("../lib/dom");
|
||||
dom.importCssString(exports.cssText, exports.cssClass);
|
||||
});
|
|
@ -1,228 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var util = require('./util');
|
||||
var ContextMenu = require('./ContextMenu');
|
||||
|
||||
/**
|
||||
* A factory function to create an AppendNode, which depends on a Node
|
||||
* @param {Node} Node
|
||||
*/
|
||||
function appendNodeFactory(Node) {
|
||||
/**
|
||||
* @constructor AppendNode
|
||||
* @extends Node
|
||||
* @param {TreeEditor} editor
|
||||
* Create a new AppendNode. This is a special node which is created at the
|
||||
* end of the list with childs for an object or array
|
||||
*/
|
||||
function AppendNode (editor) {
|
||||
/** @type {TreeEditor} */
|
||||
this.editor = editor;
|
||||
this.dom = {};
|
||||
}
|
||||
|
||||
AppendNode.prototype = new Node();
|
||||
|
||||
/**
|
||||
* Return a table row with an append button.
|
||||
* @return {Element} dom TR element
|
||||
*/
|
||||
AppendNode.prototype.getDom = function () {
|
||||
// TODO: implement a new solution for the append node
|
||||
var dom = this.dom;
|
||||
|
||||
if (dom.tr) {
|
||||
return dom.tr;
|
||||
}
|
||||
|
||||
this._updateEditability();
|
||||
|
||||
// a row for the append button
|
||||
var trAppend = document.createElement('tr');
|
||||
trAppend.node = this;
|
||||
dom.tr = trAppend;
|
||||
|
||||
// TODO: consistent naming
|
||||
|
||||
if (this.editor.options.mode === 'tree') {
|
||||
// a cell for the dragarea column
|
||||
dom.tdDrag = document.createElement('td');
|
||||
|
||||
// create context menu
|
||||
var tdMenu = document.createElement('td');
|
||||
dom.tdMenu = tdMenu;
|
||||
var menu = document.createElement('button');
|
||||
menu.className = 'jsoneditor-contextmenu';
|
||||
menu.title = 'Click to open the actions menu (Ctrl+M)';
|
||||
dom.menu = menu;
|
||||
tdMenu.appendChild(dom.menu);
|
||||
}
|
||||
|
||||
// a cell for the contents (showing text 'empty')
|
||||
var tdAppend = document.createElement('td');
|
||||
var domText = document.createElement('div');
|
||||
domText.innerHTML = '(empty)';
|
||||
domText.className = 'jsoneditor-readonly';
|
||||
tdAppend.appendChild(domText);
|
||||
dom.td = tdAppend;
|
||||
dom.text = domText;
|
||||
|
||||
this.updateDom();
|
||||
|
||||
return trAppend;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the HTML dom of the Node
|
||||
*/
|
||||
AppendNode.prototype.updateDom = function () {
|
||||
var dom = this.dom;
|
||||
var tdAppend = dom.td;
|
||||
if (tdAppend) {
|
||||
tdAppend.style.paddingLeft = (this.getLevel() * 24 + 26) + 'px';
|
||||
// TODO: not so nice hard coded offset
|
||||
}
|
||||
|
||||
var domText = dom.text;
|
||||
if (domText) {
|
||||
domText.innerHTML = '(empty ' + this.parent.type + ')';
|
||||
}
|
||||
|
||||
// attach or detach the contents of the append node:
|
||||
// hide when the parent has childs, show when the parent has no childs
|
||||
var trAppend = dom.tr;
|
||||
if (!this.isVisible()) {
|
||||
if (dom.tr.firstChild) {
|
||||
if (dom.tdDrag) {
|
||||
trAppend.removeChild(dom.tdDrag);
|
||||
}
|
||||
if (dom.tdMenu) {
|
||||
trAppend.removeChild(dom.tdMenu);
|
||||
}
|
||||
trAppend.removeChild(tdAppend);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!dom.tr.firstChild) {
|
||||
if (dom.tdDrag) {
|
||||
trAppend.appendChild(dom.tdDrag);
|
||||
}
|
||||
if (dom.tdMenu) {
|
||||
trAppend.appendChild(dom.tdMenu);
|
||||
}
|
||||
trAppend.appendChild(tdAppend);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the AppendNode is currently visible.
|
||||
* the AppendNode is visible when its parent has no childs (i.e. is empty).
|
||||
* @return {boolean} isVisible
|
||||
*/
|
||||
AppendNode.prototype.isVisible = function () {
|
||||
return (this.parent.childs.length == 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a contextmenu for this node
|
||||
* @param {HTMLElement} anchor The element to attach the menu to.
|
||||
* @param {function} [onClose] Callback method called when the context menu
|
||||
* is being closed.
|
||||
*/
|
||||
AppendNode.prototype.showContextMenu = function (anchor, onClose) {
|
||||
var node = this;
|
||||
var titles = Node.TYPE_TITLES;
|
||||
var items = [
|
||||
// create append button
|
||||
{
|
||||
'text': 'Append',
|
||||
'title': 'Append a new field with type \'auto\' (Ctrl+Shift+Ins)',
|
||||
'submenuTitle': 'Select the type of the field to be appended',
|
||||
'className': 'jsoneditor-insert',
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'auto');
|
||||
},
|
||||
'submenu': [
|
||||
{
|
||||
'text': 'Auto',
|
||||
'className': 'jsoneditor-type-auto',
|
||||
'title': titles.auto,
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'auto');
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'Array',
|
||||
'className': 'jsoneditor-type-array',
|
||||
'title': titles.array,
|
||||
'click': function () {
|
||||
node._onAppend('', []);
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'Object',
|
||||
'className': 'jsoneditor-type-object',
|
||||
'title': titles.object,
|
||||
'click': function () {
|
||||
node._onAppend('', {});
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'String',
|
||||
'className': 'jsoneditor-type-string',
|
||||
'title': titles.string,
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'string');
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
var menu = new ContextMenu(items, {close: onClose});
|
||||
menu.show(anchor, this.editor.content);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle an event. The event is catched centrally by the editor
|
||||
* @param {Event} event
|
||||
*/
|
||||
AppendNode.prototype.onEvent = function (event) {
|
||||
var type = event.type;
|
||||
var target = event.target || event.srcElement;
|
||||
var dom = this.dom;
|
||||
|
||||
// highlight the append nodes parent
|
||||
var menu = dom.menu;
|
||||
if (target == menu) {
|
||||
if (type == 'mouseover') {
|
||||
this.editor.highlighter.highlight(this.parent);
|
||||
}
|
||||
else if (type == 'mouseout') {
|
||||
this.editor.highlighter.unhighlight();
|
||||
}
|
||||
}
|
||||
|
||||
// context menu events
|
||||
if (type == 'click' && target == dom.menu) {
|
||||
var highlighter = this.editor.highlighter;
|
||||
highlighter.highlight(this.parent);
|
||||
highlighter.lock();
|
||||
util.addClassName(dom.menu, 'jsoneditor-selected');
|
||||
this.showContextMenu(dom.menu, function () {
|
||||
util.removeClassName(dom.menu, 'jsoneditor-selected');
|
||||
highlighter.unlock();
|
||||
highlighter.unhighlight();
|
||||
});
|
||||
}
|
||||
|
||||
if (type == 'keydown') {
|
||||
this.onKeyDown(event);
|
||||
}
|
||||
};
|
||||
|
||||
return AppendNode;
|
||||
}
|
||||
|
||||
module.exports = appendNodeFactory;
|
|
@ -1,418 +0,0 @@
|
|||
/* Jison generated parser */
|
||||
var jsonlint = (function(){
|
||||
var parser = {trace: function trace() { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},
|
||||
productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],
|
||||
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
case 1: // replace escaped characters with actual character
|
||||
this.$ = yytext.replace(/\\(\\|")/g, "$"+"1")
|
||||
.replace(/\\n/g,'\n')
|
||||
.replace(/\\r/g,'\r')
|
||||
.replace(/\\t/g,'\t')
|
||||
.replace(/\\v/g,'\v')
|
||||
.replace(/\\f/g,'\f')
|
||||
.replace(/\\b/g,'\b');
|
||||
|
||||
break;
|
||||
case 2:this.$ = Number(yytext);
|
||||
break;
|
||||
case 3:this.$ = null;
|
||||
break;
|
||||
case 4:this.$ = true;
|
||||
break;
|
||||
case 5:this.$ = false;
|
||||
break;
|
||||
case 6:return this.$ = $$[$0-1];
|
||||
break;
|
||||
case 13:this.$ = {};
|
||||
break;
|
||||
case 14:this.$ = $$[$0-1];
|
||||
break;
|
||||
case 15:this.$ = [$$[$0-2], $$[$0]];
|
||||
break;
|
||||
case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1];
|
||||
break;
|
||||
case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1];
|
||||
break;
|
||||
case 18:this.$ = [];
|
||||
break;
|
||||
case 19:this.$ = $$[$0-1];
|
||||
break;
|
||||
case 20:this.$ = [$$[$0]];
|
||||
break;
|
||||
case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]);
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}],
|
||||
defaultActions: {16:[2,6]},
|
||||
parseError: function parseError(str, hash) {
|
||||
throw new Error(str);
|
||||
},
|
||||
parse: function parse(input) {
|
||||
var self = this,
|
||||
stack = [0],
|
||||
vstack = [null], // semantic value stack
|
||||
lstack = [], // location stack
|
||||
table = this.table,
|
||||
yytext = '',
|
||||
yylineno = 0,
|
||||
yyleng = 0,
|
||||
recovering = 0,
|
||||
TERROR = 2,
|
||||
EOF = 1;
|
||||
|
||||
//this.reductionCount = this.shiftCount = 0;
|
||||
|
||||
this.lexer.setInput(input);
|
||||
this.lexer.yy = this.yy;
|
||||
this.yy.lexer = this.lexer;
|
||||
if (typeof this.lexer.yylloc == 'undefined')
|
||||
this.lexer.yylloc = {};
|
||||
var yyloc = this.lexer.yylloc;
|
||||
lstack.push(yyloc);
|
||||
|
||||
if (typeof this.yy.parseError === 'function')
|
||||
this.parseError = this.yy.parseError;
|
||||
|
||||
function popStack (n) {
|
||||
stack.length = stack.length - 2*n;
|
||||
vstack.length = vstack.length - n;
|
||||
lstack.length = lstack.length - n;
|
||||
}
|
||||
|
||||
function lex() {
|
||||
var token;
|
||||
token = self.lexer.lex() || 1; // $end = 1
|
||||
// if token isn't its numeric value, convert
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
|
||||
while (true) {
|
||||
// retreive state number from top of stack
|
||||
state = stack[stack.length-1];
|
||||
|
||||
// use default actions if available
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state];
|
||||
} else {
|
||||
if (symbol == null)
|
||||
symbol = lex();
|
||||
// read action for current state and first input
|
||||
action = table[state] && table[state][symbol];
|
||||
}
|
||||
|
||||
// handle parse error
|
||||
_handle_error:
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
|
||||
if (!recovering) {
|
||||
// Report error
|
||||
expected = [];
|
||||
for (p in table[state]) if (this.terminals_[p] && p > 2) {
|
||||
expected.push("'"+this.terminals_[p]+"'");
|
||||
}
|
||||
var errStr = '';
|
||||
if (this.lexer.showPosition) {
|
||||
errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
|
||||
} else {
|
||||
errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
|
||||
(symbol == 1 /*EOF*/ ? "end of input" :
|
||||
("'"+(this.terminals_[symbol] || symbol)+"'"));
|
||||
}
|
||||
this.parseError(errStr,
|
||||
{text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
|
||||
}
|
||||
|
||||
// just recovered from another error
|
||||
if (recovering == 3) {
|
||||
if (symbol == EOF) {
|
||||
throw new Error(errStr || 'Parsing halted.');
|
||||
}
|
||||
|
||||
// discard current lookahead and grab another
|
||||
yyleng = this.lexer.yyleng;
|
||||
yytext = this.lexer.yytext;
|
||||
yylineno = this.lexer.yylineno;
|
||||
yyloc = this.lexer.yylloc;
|
||||
symbol = lex();
|
||||
}
|
||||
|
||||
// try to recover from error
|
||||
while (1) {
|
||||
// check for error recovery rule in this state
|
||||
if ((TERROR.toString()) in table[state]) {
|
||||
break;
|
||||
}
|
||||
if (state == 0) {
|
||||
throw new Error(errStr || 'Parsing halted.');
|
||||
}
|
||||
popStack(1);
|
||||
state = stack[stack.length-1];
|
||||
}
|
||||
|
||||
preErrorSymbol = symbol; // save the lookahead token
|
||||
symbol = TERROR; // insert generic error symbol as new lookahead
|
||||
state = stack[stack.length-1];
|
||||
action = table[state] && table[state][TERROR];
|
||||
recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
|
||||
}
|
||||
|
||||
// this shouldn't happen, unless resolve defaults are off
|
||||
if (action[0] instanceof Array && action.length > 1) {
|
||||
throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
|
||||
}
|
||||
|
||||
switch (action[0]) {
|
||||
|
||||
case 1: // shift
|
||||
//this.shiftCount++;
|
||||
|
||||
stack.push(symbol);
|
||||
vstack.push(this.lexer.yytext);
|
||||
lstack.push(this.lexer.yylloc);
|
||||
stack.push(action[1]); // push state
|
||||
symbol = null;
|
||||
if (!preErrorSymbol) { // normal execution/no error
|
||||
yyleng = this.lexer.yyleng;
|
||||
yytext = this.lexer.yytext;
|
||||
yylineno = this.lexer.yylineno;
|
||||
yyloc = this.lexer.yylloc;
|
||||
if (recovering > 0)
|
||||
recovering--;
|
||||
} else { // error just occurred, resume old lookahead f/ before error
|
||||
symbol = preErrorSymbol;
|
||||
preErrorSymbol = null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // reduce
|
||||
//this.reductionCount++;
|
||||
|
||||
len = this.productions_[action[1]][1];
|
||||
|
||||
// perform semantic action
|
||||
yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
|
||||
// default location, uses first token for firsts, last for lasts
|
||||
yyval._$ = {
|
||||
first_line: lstack[lstack.length-(len||1)].first_line,
|
||||
last_line: lstack[lstack.length-1].last_line,
|
||||
first_column: lstack[lstack.length-(len||1)].first_column,
|
||||
last_column: lstack[lstack.length-1].last_column
|
||||
};
|
||||
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
|
||||
|
||||
if (typeof r !== 'undefined') {
|
||||
return r;
|
||||
}
|
||||
|
||||
// pop off stack
|
||||
if (len) {
|
||||
stack = stack.slice(0,-1*len*2);
|
||||
vstack = vstack.slice(0, -1*len);
|
||||
lstack = lstack.slice(0, -1*len);
|
||||
}
|
||||
|
||||
stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
|
||||
vstack.push(yyval.$);
|
||||
lstack.push(yyval._$);
|
||||
// goto new state = table[STATE][NONTERMINAL]
|
||||
newState = table[stack[stack.length-2]][stack[stack.length-1]];
|
||||
stack.push(newState);
|
||||
break;
|
||||
|
||||
case 3: // accept
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}};
|
||||
/* Jison generated lexer */
|
||||
var lexer = (function(){
|
||||
var lexer = ({EOF:1,
|
||||
parseError:function parseError(str, hash) {
|
||||
if (this.yy.parseError) {
|
||||
this.yy.parseError(str, hash);
|
||||
} else {
|
||||
throw new Error(str);
|
||||
}
|
||||
},
|
||||
setInput:function (input) {
|
||||
this._input = input;
|
||||
this._more = this._less = this.done = false;
|
||||
this.yylineno = this.yyleng = 0;
|
||||
this.yytext = this.matched = this.match = '';
|
||||
this.conditionStack = ['INITIAL'];
|
||||
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
|
||||
return this;
|
||||
},
|
||||
input:function () {
|
||||
var ch = this._input[0];
|
||||
this.yytext+=ch;
|
||||
this.yyleng++;
|
||||
this.match+=ch;
|
||||
this.matched+=ch;
|
||||
var lines = ch.match(/\n/);
|
||||
if (lines) this.yylineno++;
|
||||
this._input = this._input.slice(1);
|
||||
return ch;
|
||||
},
|
||||
unput:function (ch) {
|
||||
this._input = ch + this._input;
|
||||
return this;
|
||||
},
|
||||
more:function () {
|
||||
this._more = true;
|
||||
return this;
|
||||
},
|
||||
less:function (n) {
|
||||
this._input = this.match.slice(n) + this._input;
|
||||
},
|
||||
pastInput:function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length);
|
||||
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
|
||||
},
|
||||
upcomingInput:function () {
|
||||
var next = this.match;
|
||||
if (next.length < 20) {
|
||||
next += this._input.substr(0, 20-next.length);
|
||||
}
|
||||
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
|
||||
},
|
||||
showPosition:function () {
|
||||
var pre = this.pastInput();
|
||||
var c = new Array(pre.length + 1).join("-");
|
||||
return pre + this.upcomingInput() + "\n" + c+"^";
|
||||
},
|
||||
next:function () {
|
||||
if (this.done) {
|
||||
return this.EOF;
|
||||
}
|
||||
if (!this._input) this.done = true;
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index,
|
||||
col,
|
||||
lines;
|
||||
if (!this._more) {
|
||||
this.yytext = '';
|
||||
this.match = '';
|
||||
}
|
||||
var rules = this._currentRules();
|
||||
for (var i=0;i < rules.length; i++) {
|
||||
tempMatch = this._input.match(this.rules[rules[i]]);
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch;
|
||||
index = i;
|
||||
if (!this.options.flex) break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
lines = match[0].match(/\n.*/g);
|
||||
if (lines) this.yylineno += lines.length;
|
||||
this.yylloc = {first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno+1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
|
||||
this.yytext += match[0];
|
||||
this.match += match[0];
|
||||
this.yyleng = this.yytext.length;
|
||||
this._more = false;
|
||||
this._input = this._input.slice(match[0].length);
|
||||
this.matched += match[0];
|
||||
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
|
||||
if (this.done && this._input) this.done = false;
|
||||
if (token) return token;
|
||||
else return;
|
||||
}
|
||||
if (this._input === "") {
|
||||
return this.EOF;
|
||||
} else {
|
||||
this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
|
||||
{text: "", token: null, line: this.yylineno});
|
||||
}
|
||||
},
|
||||
lex:function lex() {
|
||||
var r = this.next();
|
||||
if (typeof r !== 'undefined') {
|
||||
return r;
|
||||
} else {
|
||||
return this.lex();
|
||||
}
|
||||
},
|
||||
begin:function begin(condition) {
|
||||
this.conditionStack.push(condition);
|
||||
},
|
||||
popState:function popState() {
|
||||
return this.conditionStack.pop();
|
||||
},
|
||||
_currentRules:function _currentRules() {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
|
||||
},
|
||||
topState:function () {
|
||||
return this.conditionStack[this.conditionStack.length-2];
|
||||
},
|
||||
pushState:function begin(condition) {
|
||||
this.begin(condition);
|
||||
}});
|
||||
lexer.options = {};
|
||||
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
||||
|
||||
var YYSTATE=YY_START
|
||||
switch($avoiding_name_collisions) {
|
||||
case 0:/* skip whitespace */
|
||||
break;
|
||||
case 1:return 6
|
||||
break;
|
||||
case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4
|
||||
break;
|
||||
case 3:return 17
|
||||
break;
|
||||
case 4:return 18
|
||||
break;
|
||||
case 5:return 23
|
||||
break;
|
||||
case 6:return 24
|
||||
break;
|
||||
case 7:return 22
|
||||
break;
|
||||
case 8:return 21
|
||||
break;
|
||||
case 9:return 10
|
||||
break;
|
||||
case 10:return 11
|
||||
break;
|
||||
case 11:return 8
|
||||
break;
|
||||
case 12:return 14
|
||||
break;
|
||||
case 13:return 'INVALID'
|
||||
break;
|
||||
}
|
||||
};
|
||||
lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/];
|
||||
lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}};
|
||||
|
||||
|
||||
;
|
||||
return lexer;})()
|
||||
parser.lexer = lexer;
|
||||
return parser;
|
||||
})();
|
||||
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
|
||||
exports.parser = jsonlint;
|
||||
exports.parse = jsonlint.parse.bind(jsonlint);
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*!
|
||||
* jsoneditor.js
|
||||
*
|
||||
* @brief
|
||||
* JSONEditor is a web-based tool to view, edit, format, and validate JSON.
|
||||
* It has various modes such as a tree editor, a code editor, and a plain text
|
||||
* editor.
|
||||
*
|
||||
* Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 8+
|
||||
*
|
||||
* @license
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy
|
||||
* of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*
|
||||
* Copyright (c) 2011-2016 Jos de Jong, http://jsoneditoronline.org
|
||||
*
|
||||
* @author Jos de Jong, <wjosdejong@gmail.com>
|
||||
* @version @@version
|
||||
* @date @@date
|
||||
*/
|
|
@ -1,489 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ace;
|
||||
try {
|
||||
ace = require('./ace');
|
||||
}
|
||||
catch (err) {
|
||||
// failed to load ace, no problem, we will fall back to plain text
|
||||
}
|
||||
|
||||
var ModeSwitcher = require('./ModeSwitcher');
|
||||
var util = require('./util');
|
||||
|
||||
// create a mixin with the functions for text mode
|
||||
var textmode = {};
|
||||
|
||||
var MAX_ERRORS = 3; // maximum number of displayed errors at the bottom
|
||||
|
||||
/**
|
||||
* Create a text editor
|
||||
* @param {Element} container
|
||||
* @param {Object} [options] Object with options. available options:
|
||||
* {String} mode Available values:
|
||||
* "text" (default)
|
||||
* or "code".
|
||||
* {Number} indentation Number of indentation
|
||||
* spaces. 2 by default.
|
||||
* {function} onChange Callback method
|
||||
* triggered on change
|
||||
* {function} onModeChange Callback method
|
||||
* triggered after setMode
|
||||
* {Object} ace A custom instance of
|
||||
* Ace editor.
|
||||
* {boolean} escapeUnicode If true, unicode
|
||||
* characters are escaped.
|
||||
* false by default.
|
||||
* @private
|
||||
*/
|
||||
textmode.create = function (container, options) {
|
||||
// read options
|
||||
options = options || {};
|
||||
this.options = options;
|
||||
|
||||
// indentation
|
||||
if (options.indentation) {
|
||||
this.indentation = Number(options.indentation);
|
||||
}
|
||||
else {
|
||||
this.indentation = 2; // number of spaces
|
||||
}
|
||||
|
||||
// grab ace from options if provided
|
||||
var _ace = options.ace ? options.ace : ace;
|
||||
|
||||
// determine mode
|
||||
this.mode = (options.mode == 'code') ? 'code' : 'text';
|
||||
if (this.mode == 'code') {
|
||||
// verify whether Ace editor is available and supported
|
||||
if (typeof _ace === 'undefined') {
|
||||
this.mode = 'text';
|
||||
console.warn('Failed to load Ace editor, falling back to plain text mode. Please use a JSONEditor bundle including Ace, or pass Ace as via the configuration option `ace`.');
|
||||
}
|
||||
}
|
||||
|
||||
// determine theme
|
||||
this.theme = options.theme || 'ace/theme/jsoneditor';
|
||||
|
||||
var me = this;
|
||||
this.container = container;
|
||||
this.dom = {};
|
||||
this.aceEditor = undefined; // ace code editor
|
||||
this.textarea = undefined; // plain text editor (fallback when Ace is not available)
|
||||
this.validateSchema = null;
|
||||
|
||||
// create a debounced validate function
|
||||
this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL);
|
||||
|
||||
this.width = container.clientWidth;
|
||||
this.height = container.clientHeight;
|
||||
|
||||
this.frame = document.createElement('div');
|
||||
this.frame.className = 'jsoneditor jsoneditor-mode-' + this.options.mode;
|
||||
this.frame.onclick = function (event) {
|
||||
// prevent default submit action when the editor is located inside a form
|
||||
event.preventDefault();
|
||||
};
|
||||
this.frame.onkeydown = function (event) {
|
||||
me._onKeyDown(event);
|
||||
};
|
||||
|
||||
// create menu
|
||||
this.menu = document.createElement('div');
|
||||
this.menu.className = 'jsoneditor-menu';
|
||||
this.frame.appendChild(this.menu);
|
||||
|
||||
// create format button
|
||||
var buttonFormat = document.createElement('button');
|
||||
buttonFormat.className = 'jsoneditor-format';
|
||||
buttonFormat.title = 'Format JSON data, with proper indentation and line feeds (Ctrl+\\)';
|
||||
this.menu.appendChild(buttonFormat);
|
||||
buttonFormat.onclick = function () {
|
||||
try {
|
||||
me.format();
|
||||
me._onChange();
|
||||
}
|
||||
catch (err) {
|
||||
me._onError(err);
|
||||
}
|
||||
};
|
||||
|
||||
// create compact button
|
||||
var buttonCompact = document.createElement('button');
|
||||
buttonCompact.className = 'jsoneditor-compact';
|
||||
buttonCompact.title = 'Compact JSON data, remove all whitespaces (Ctrl+Shift+\\)';
|
||||
this.menu.appendChild(buttonCompact);
|
||||
buttonCompact.onclick = function () {
|
||||
try {
|
||||
me.compact();
|
||||
me._onChange();
|
||||
}
|
||||
catch (err) {
|
||||
me._onError(err);
|
||||
}
|
||||
};
|
||||
|
||||
// create mode box
|
||||
if (this.options && this.options.modes && this.options.modes.length) {
|
||||
this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) {
|
||||
// switch mode and restore focus
|
||||
me.setMode(mode);
|
||||
me.modeSwitcher.focus();
|
||||
});
|
||||
}
|
||||
|
||||
this.content = document.createElement('div');
|
||||
this.content.className = 'jsoneditor-outer';
|
||||
this.frame.appendChild(this.content);
|
||||
|
||||
this.container.appendChild(this.frame);
|
||||
|
||||
if (this.mode == 'code') {
|
||||
this.editorDom = document.createElement('div');
|
||||
this.editorDom.style.height = '100%'; // TODO: move to css
|
||||
this.editorDom.style.width = '100%'; // TODO: move to css
|
||||
this.content.appendChild(this.editorDom);
|
||||
|
||||
var aceEditor = _ace.edit(this.editorDom);
|
||||
aceEditor.$blockScrolling = Infinity;
|
||||
aceEditor.setTheme(this.theme);
|
||||
aceEditor.setShowPrintMargin(false);
|
||||
aceEditor.setFontSize(13);
|
||||
aceEditor.getSession().setMode('ace/mode/json');
|
||||
aceEditor.getSession().setTabSize(this.indentation);
|
||||
aceEditor.getSession().setUseSoftTabs(true);
|
||||
aceEditor.getSession().setUseWrapMode(true);
|
||||
aceEditor.commands.bindKey('Ctrl-L', null); // disable Ctrl+L (is used by the browser to select the address bar)
|
||||
aceEditor.commands.bindKey('Command-L', null); // disable Ctrl+L (is used by the browser to select the address bar)
|
||||
this.aceEditor = aceEditor;
|
||||
|
||||
// TODO: deprecated since v5.0.0. Cleanup backward compatibility some day
|
||||
if (!this.hasOwnProperty('editor')) {
|
||||
Object.defineProperty(this, 'editor', {
|
||||
get: function () {
|
||||
console.warn('Property "editor" has been renamed to "aceEditor".');
|
||||
return me.aceEditor;
|
||||
},
|
||||
set: function (aceEditor) {
|
||||
console.warn('Property "editor" has been renamed to "aceEditor".');
|
||||
me.aceEditor = aceEditor;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var poweredBy = document.createElement('a');
|
||||
poweredBy.appendChild(document.createTextNode('powered by ace'));
|
||||
poweredBy.href = 'http://ace.ajax.org';
|
||||
poweredBy.target = '_blank';
|
||||
poweredBy.className = 'jsoneditor-poweredBy';
|
||||
poweredBy.onclick = function () {
|
||||
// TODO: this anchor falls below the margin of the content,
|
||||
// therefore the normal a.href does not work. We use a click event
|
||||
// for now, but this should be fixed.
|
||||
window.open(poweredBy.href, poweredBy.target);
|
||||
};
|
||||
this.menu.appendChild(poweredBy);
|
||||
|
||||
// register onchange event
|
||||
aceEditor.on('change', this._onChange.bind(this));
|
||||
}
|
||||
else {
|
||||
// load a plain text textarea
|
||||
var textarea = document.createElement('textarea');
|
||||
textarea.className = 'jsoneditor-text';
|
||||
textarea.spellcheck = false;
|
||||
this.content.appendChild(textarea);
|
||||
this.textarea = textarea;
|
||||
|
||||
// register onchange event
|
||||
if (this.textarea.oninput === null) {
|
||||
this.textarea.oninput = this._onChange.bind(this);
|
||||
}
|
||||
else {
|
||||
// oninput is undefined. For IE8-
|
||||
this.textarea.onchange = this._onChange.bind(this);
|
||||
}
|
||||
}
|
||||
|
||||
this.setSchema(this.options.schema);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a change:
|
||||
* - Validate JSON schema
|
||||
* - Send a callback to the onChange listener if provided
|
||||
* @private
|
||||
*/
|
||||
textmode._onChange = function () {
|
||||
// validate JSON schema (if configured)
|
||||
this._debouncedValidate();
|
||||
|
||||
// trigger the onChange callback
|
||||
if (this.options.onChange) {
|
||||
try {
|
||||
this.options.onChange();
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Error in onChange callback: ', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for keydown. Handles shortcut keys
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
textmode._onKeyDown = function (event) {
|
||||
var keynum = event.which || event.keyCode;
|
||||
var handled = false;
|
||||
|
||||
if (keynum == 220 && event.ctrlKey) {
|
||||
if (event.shiftKey) { // Ctrl+Shift+\
|
||||
this.compact();
|
||||
this._onChange();
|
||||
}
|
||||
else { // Ctrl+\
|
||||
this.format();
|
||||
this._onChange();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
||||
*/
|
||||
textmode.destroy = function () {
|
||||
// remove old ace editor
|
||||
if (this.aceEditor) {
|
||||
this.aceEditor.destroy();
|
||||
this.aceEditor = null;
|
||||
}
|
||||
|
||||
if (this.frame && this.container && this.frame.parentNode == this.container) {
|
||||
this.container.removeChild(this.frame);
|
||||
}
|
||||
|
||||
if (this.modeSwitcher) {
|
||||
this.modeSwitcher.destroy();
|
||||
this.modeSwitcher = null;
|
||||
}
|
||||
|
||||
this.textarea = null;
|
||||
|
||||
this._debouncedValidate = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compact the code in the formatter
|
||||
*/
|
||||
textmode.compact = function () {
|
||||
var json = this.get();
|
||||
var text = JSON.stringify(json);
|
||||
this.setText(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format the code in the formatter
|
||||
*/
|
||||
textmode.format = function () {
|
||||
var json = this.get();
|
||||
var text = JSON.stringify(json, null, this.indentation);
|
||||
this.setText(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set focus to the formatter
|
||||
*/
|
||||
textmode.focus = function () {
|
||||
if (this.textarea) {
|
||||
this.textarea.focus();
|
||||
}
|
||||
if (this.aceEditor) {
|
||||
this.aceEditor.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resize the formatter
|
||||
*/
|
||||
textmode.resize = function () {
|
||||
if (this.aceEditor) {
|
||||
var force = false;
|
||||
this.aceEditor.resize(force);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set json data in the formatter
|
||||
* @param {Object} json
|
||||
*/
|
||||
textmode.set = function(json) {
|
||||
this.setText(JSON.stringify(json, null, this.indentation));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get json data from the formatter
|
||||
* @return {Object} json
|
||||
*/
|
||||
textmode.get = function() {
|
||||
var text = this.getText();
|
||||
var json;
|
||||
|
||||
try {
|
||||
json = util.parse(text); // this can throw an error
|
||||
}
|
||||
catch (err) {
|
||||
// try to sanitize json, replace JavaScript notation with JSON notation
|
||||
text = util.sanitize(text);
|
||||
|
||||
// try to parse again
|
||||
json = util.parse(text); // this can throw an error
|
||||
}
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text contents of the editor
|
||||
* @return {String} jsonText
|
||||
*/
|
||||
textmode.getText = function() {
|
||||
if (this.textarea) {
|
||||
return this.textarea.value;
|
||||
}
|
||||
if (this.aceEditor) {
|
||||
return this.aceEditor.getValue();
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text contents of the editor
|
||||
* @param {String} jsonText
|
||||
*/
|
||||
textmode.setText = function(jsonText) {
|
||||
var text;
|
||||
|
||||
if (this.options.escapeUnicode === true) {
|
||||
text = util.escapeUnicodeChars(jsonText);
|
||||
}
|
||||
else {
|
||||
text = jsonText;
|
||||
}
|
||||
|
||||
if (this.textarea) {
|
||||
this.textarea.value = text;
|
||||
}
|
||||
if (this.aceEditor) {
|
||||
// prevent emitting onChange events while setting new text
|
||||
var originalOnChange = this.options.onChange;
|
||||
this.options.onChange = null;
|
||||
|
||||
this.aceEditor.setValue(text, -1);
|
||||
|
||||
this.options.onChange = originalOnChange;
|
||||
}
|
||||
|
||||
// validate JSON schema
|
||||
this.validate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate current JSON object against the configured JSON schema
|
||||
* Throws an exception when no JSON schema is configured
|
||||
*/
|
||||
textmode.validate = function () {
|
||||
// clear all current errors
|
||||
if (this.dom.validationErrors) {
|
||||
this.dom.validationErrors.parentNode.removeChild(this.dom.validationErrors);
|
||||
this.dom.validationErrors = null;
|
||||
|
||||
this.content.style.marginBottom = '';
|
||||
this.content.style.paddingBottom = '';
|
||||
}
|
||||
|
||||
var doValidate = false;
|
||||
var errors = [];
|
||||
var json;
|
||||
try {
|
||||
json = this.get(); // this can fail when there is no valid json
|
||||
doValidate = true;
|
||||
}
|
||||
catch (err) {
|
||||
// no valid JSON, don't validate
|
||||
}
|
||||
|
||||
// only validate the JSON when parsing the JSON succeeded
|
||||
if (doValidate && this.validateSchema) {
|
||||
var valid = this.validateSchema(json);
|
||||
if (!valid) {
|
||||
errors = this.validateSchema.errors.map(function (error) {
|
||||
return util.improveSchemaError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
// limit the number of displayed errors
|
||||
var limit = errors.length > MAX_ERRORS;
|
||||
if (limit) {
|
||||
errors = errors.slice(0, MAX_ERRORS);
|
||||
var hidden = this.validateSchema.errors.length - MAX_ERRORS;
|
||||
errors.push('(' + hidden + ' more errors...)')
|
||||
}
|
||||
|
||||
var validationErrors = document.createElement('div');
|
||||
validationErrors.innerHTML = '<table class="jsoneditor-text-errors">' +
|
||||
'<tbody>' +
|
||||
errors.map(function (error) {
|
||||
var message;
|
||||
if (typeof error === 'string') {
|
||||
message = '<td colspan="2"><pre>' + error + '</pre></td>';
|
||||
}
|
||||
else {
|
||||
message = '<td>' + error.dataPath + '</td>' +
|
||||
'<td>' + error.message + '</td>';
|
||||
}
|
||||
|
||||
return '<tr><td><button class="jsoneditor-schema-error"></button></td>' + message + '</tr>'
|
||||
}).join('') +
|
||||
'</tbody>' +
|
||||
'</table>';
|
||||
|
||||
this.dom.validationErrors = validationErrors;
|
||||
this.frame.appendChild(validationErrors);
|
||||
|
||||
var height = validationErrors.clientHeight;
|
||||
this.content.style.marginBottom = (-height) + 'px';
|
||||
this.content.style.paddingBottom = height + 'px';
|
||||
}
|
||||
|
||||
// update the height of the ace editor
|
||||
if (this.aceEditor) {
|
||||
var force = false;
|
||||
this.aceEditor.resize(force);
|
||||
}
|
||||
};
|
||||
|
||||
// define modes
|
||||
module.exports = [
|
||||
{
|
||||
mode: 'text',
|
||||
mixin: textmode,
|
||||
data: 'text',
|
||||
load: textmode.format
|
||||
},
|
||||
{
|
||||
mode: 'code',
|
||||
mixin: textmode,
|
||||
data: 'text',
|
||||
load: textmode.format
|
||||
}
|
||||
];
|
1194
src/js/treemode.js
1194
src/js/treemode.js
File diff suppressed because it is too large
Load Diff
772
src/js/util.js
772
src/js/util.js
|
@ -1,772 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var jsonlint = require('./assets/jsonlint/jsonlint');
|
||||
|
||||
/**
|
||||
* Parse JSON using the parser built-in in the browser.
|
||||
* On exception, the jsonString is validated and a detailed error is thrown.
|
||||
* @param {String} jsonString
|
||||
* @return {JSON} json
|
||||
*/
|
||||
exports.parse = function parse(jsonString) {
|
||||
try {
|
||||
return JSON.parse(jsonString);
|
||||
}
|
||||
catch (err) {
|
||||
// try to throw a more detailed error message using validate
|
||||
exports.validate(jsonString);
|
||||
|
||||
// rethrow the original error
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sanitize a JSON-like string containing. For example changes JavaScript
|
||||
* notation into JSON notation.
|
||||
* This function for example changes a string like "{a: 2, 'b': {c: 'd'}"
|
||||
* into '{"a": 2, "b": {"c": "d"}'
|
||||
* @param {string} jsString
|
||||
* @returns {string} json
|
||||
*/
|
||||
exports.sanitize = function (jsString) {
|
||||
// escape all single and double quotes inside strings
|
||||
var chars = [];
|
||||
var i = 0;
|
||||
|
||||
//If JSON starts with a function (characters/digits/"_-"), remove this function.
|
||||
//This is useful for "stripping" JSONP objects to become JSON
|
||||
//For example: /* some comment */ function_12321321 ( [{"a":"b"}] ); => [{"a":"b"}]
|
||||
var match = jsString.match(/^\s*(\/\*(.|[\r\n])*?\*\/)?\s*[\da-zA-Z_$]+\s*\(([\s\S]*)\)\s*;?\s*$/);
|
||||
if (match) {
|
||||
jsString = match[3];
|
||||
}
|
||||
|
||||
// helper functions to get the current/prev/next character
|
||||
function curr () { return jsString.charAt(i); }
|
||||
function next() { return jsString.charAt(i + 1); }
|
||||
function prev() { return jsString.charAt(i - 1); }
|
||||
|
||||
// get the last parsed non-whitespace character
|
||||
function lastNonWhitespace () {
|
||||
var p = chars.length - 1;
|
||||
|
||||
while (p >= 0) {
|
||||
var pp = chars[p];
|
||||
if (pp !== ' ' && pp !== '\n' && pp !== '\r' && pp !== '\t') { // non whitespace
|
||||
return pp;
|
||||
}
|
||||
p--;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// skip a block comment '/* ... */'
|
||||
function skipBlockComment () {
|
||||
i += 2;
|
||||
while (i < jsString.length && (curr() !== '*' || next() !== '/')) {
|
||||
i++;
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// skip a comment '// ...'
|
||||
function skipComment () {
|
||||
i += 2;
|
||||
while (i < jsString.length && (curr() !== '\n')) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// parse single or double quoted string
|
||||
function parseString(quote) {
|
||||
chars.push('"');
|
||||
i++;
|
||||
var c = curr();
|
||||
while (i < jsString.length && c !== quote) {
|
||||
if (c === '"' && prev() !== '\\') {
|
||||
// unescaped double quote, escape it
|
||||
chars.push('\\');
|
||||
}
|
||||
|
||||
// handle escape character
|
||||
if (c === '\\') {
|
||||
i++;
|
||||
c = curr();
|
||||
|
||||
// remove the escape character when followed by a single quote ', not needed
|
||||
if (c !== '\'') {
|
||||
chars.push('\\');
|
||||
}
|
||||
}
|
||||
chars.push(c);
|
||||
|
||||
i++;
|
||||
c = curr();
|
||||
}
|
||||
if (c === quote) {
|
||||
chars.push('"');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// parse an unquoted key
|
||||
function parseKey() {
|
||||
var specialValues = ['null', 'true', 'false'];
|
||||
var key = '';
|
||||
var c = curr();
|
||||
|
||||
var regexp = /[a-zA-Z_$\d]/; // letter, number, underscore, dollar character
|
||||
while (regexp.test(c)) {
|
||||
key += c;
|
||||
i++;
|
||||
c = curr();
|
||||
}
|
||||
|
||||
if (specialValues.indexOf(key) === -1) {
|
||||
chars.push('"' + key + '"');
|
||||
}
|
||||
else {
|
||||
chars.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
while(i < jsString.length) {
|
||||
var c = curr();
|
||||
|
||||
if (c === '/' && next() === '*') {
|
||||
skipBlockComment();
|
||||
}
|
||||
else if (c === '/' && next() === '/') {
|
||||
skipComment();
|
||||
}
|
||||
else if (c === '\'' || c === '"') {
|
||||
parseString(c);
|
||||
}
|
||||
else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) {
|
||||
// an unquoted object key (like a in '{a:2}')
|
||||
parseKey();
|
||||
}
|
||||
else {
|
||||
chars.push(c);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return chars.join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Escape unicode characters.
|
||||
* For example input '\u2661' (length 1) will output '\\u2661' (length 5).
|
||||
* @param {string} text
|
||||
* @return {string}
|
||||
*/
|
||||
exports.escapeUnicodeChars = function (text) {
|
||||
// see https://www.wikiwand.com/en/UTF-16
|
||||
// note: we leave surrogate pairs as two individual chars,
|
||||
// as JSON doesn't interpret them as a single unicode char.
|
||||
return text.replace(/[\u007F-\uFFFF]/g, function(c) {
|
||||
return '\\u'+('0000' + c.charCodeAt(0).toString(16)).slice(-4);
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a string containing a JSON object
|
||||
* This method uses JSONLint to validate the String. If JSONLint is not
|
||||
* available, the built-in JSON parser of the browser is used.
|
||||
* @param {String} jsonString String with an (invalid) JSON object
|
||||
* @throws Error
|
||||
*/
|
||||
exports.validate = function validate(jsonString) {
|
||||
if (typeof(jsonlint) != 'undefined') {
|
||||
jsonlint.parse(jsonString);
|
||||
}
|
||||
else {
|
||||
JSON.parse(jsonString);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extend object a with the properties of object b
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {Object} a
|
||||
*/
|
||||
exports.extend = function extend(a, b) {
|
||||
for (var prop in b) {
|
||||
if (b.hasOwnProperty(prop)) {
|
||||
a[prop] = b[prop];
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all properties from object a
|
||||
* @param {Object} a
|
||||
* @return {Object} a
|
||||
*/
|
||||
exports.clear = function clear (a) {
|
||||
for (var prop in a) {
|
||||
if (a.hasOwnProperty(prop)) {
|
||||
delete a[prop];
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the type of an object
|
||||
* @param {*} object
|
||||
* @return {String} type
|
||||
*/
|
||||
exports.type = function type (object) {
|
||||
if (object === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (object === undefined) {
|
||||
return 'undefined';
|
||||
}
|
||||
if ((object instanceof Number) || (typeof object === 'number')) {
|
||||
return 'number';
|
||||
}
|
||||
if ((object instanceof String) || (typeof object === 'string')) {
|
||||
return 'string';
|
||||
}
|
||||
if ((object instanceof Boolean) || (typeof object === 'boolean')) {
|
||||
return 'boolean';
|
||||
}
|
||||
if ((object instanceof RegExp) || (typeof object === 'regexp')) {
|
||||
return 'regexp';
|
||||
}
|
||||
if (exports.isArray(object)) {
|
||||
return 'array';
|
||||
}
|
||||
|
||||
return 'object';
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether a text contains a url (matches when a string starts
|
||||
* with 'http://*' or 'https://*' and has no whitespace characters)
|
||||
* @param {String} text
|
||||
*/
|
||||
var isUrlRegex = /^https?:\/\/\S+$/;
|
||||
exports.isUrl = function isUrl (text) {
|
||||
return (typeof text == 'string' || text instanceof String) &&
|
||||
isUrlRegex.test(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Tes whether given object is an Array
|
||||
* @param {*} obj
|
||||
* @returns {boolean} returns true when obj is an array
|
||||
*/
|
||||
exports.isArray = function (obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the absolute left value of a DOM element
|
||||
* @param {Element} elem A dom element, for example a div
|
||||
* @return {Number} left The absolute left position of this element
|
||||
* in the browser page.
|
||||
*/
|
||||
exports.getAbsoluteLeft = function getAbsoluteLeft(elem) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
return rect.left + window.pageXOffset || document.scrollLeft || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the absolute top value of a DOM element
|
||||
* @param {Element} elem A dom element, for example a div
|
||||
* @return {Number} top The absolute top position of this element
|
||||
* in the browser page.
|
||||
*/
|
||||
exports.getAbsoluteTop = function getAbsoluteTop(elem) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
return rect.top + window.pageYOffset || document.scrollTop || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* add a className to the given elements style
|
||||
* @param {Element} elem
|
||||
* @param {String} className
|
||||
*/
|
||||
exports.addClassName = function addClassName(elem, className) {
|
||||
var classes = elem.className.split(' ');
|
||||
if (classes.indexOf(className) == -1) {
|
||||
classes.push(className); // add the class to the array
|
||||
elem.className = classes.join(' ');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* add a className to the given elements style
|
||||
* @param {Element} elem
|
||||
* @param {String} className
|
||||
*/
|
||||
exports.removeClassName = function removeClassName(elem, className) {
|
||||
var classes = elem.className.split(' ');
|
||||
var index = classes.indexOf(className);
|
||||
if (index != -1) {
|
||||
classes.splice(index, 1); // remove the class from the array
|
||||
elem.className = classes.join(' ');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Strip the formatting from the contents of a div
|
||||
* the formatting from the div itself is not stripped, only from its childs.
|
||||
* @param {Element} divElement
|
||||
*/
|
||||
exports.stripFormatting = function stripFormatting(divElement) {
|
||||
var childs = divElement.childNodes;
|
||||
for (var i = 0, iMax = childs.length; i < iMax; i++) {
|
||||
var child = childs[i];
|
||||
|
||||
// remove the style
|
||||
if (child.style) {
|
||||
// TODO: test if child.attributes does contain style
|
||||
child.removeAttribute('style');
|
||||
}
|
||||
|
||||
// remove all attributes
|
||||
var attributes = child.attributes;
|
||||
if (attributes) {
|
||||
for (var j = attributes.length - 1; j >= 0; j--) {
|
||||
var attribute = attributes[j];
|
||||
if (attribute.specified === true) {
|
||||
child.removeAttribute(attribute.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recursively strip childs
|
||||
exports.stripFormatting(child);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set focus to the end of an editable div
|
||||
* code from Nico Burns
|
||||
* http://stackoverflow.com/users/140293/nico-burns
|
||||
* http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity
|
||||
* @param {Element} contentEditableElement A content editable div
|
||||
*/
|
||||
exports.setEndOfContentEditable = function setEndOfContentEditable(contentEditableElement) {
|
||||
var range, selection;
|
||||
if(document.createRange) {
|
||||
range = document.createRange();//Create a range (a range is a like the selection but invisible)
|
||||
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
|
||||
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
|
||||
selection = window.getSelection();//get the selection object (allows you to change selection)
|
||||
selection.removeAllRanges();//remove any selections already made
|
||||
selection.addRange(range);//make the range you have just created the visible selection
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select all text of a content editable div.
|
||||
* http://stackoverflow.com/a/3806004/1262753
|
||||
* @param {Element} contentEditableElement A content editable div
|
||||
*/
|
||||
exports.selectContentEditable = function selectContentEditable(contentEditableElement) {
|
||||
if (!contentEditableElement || contentEditableElement.nodeName != 'DIV') {
|
||||
return;
|
||||
}
|
||||
|
||||
var sel, range;
|
||||
if (window.getSelection && document.createRange) {
|
||||
range = document.createRange();
|
||||
range.selectNodeContents(contentEditableElement);
|
||||
sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get text selection
|
||||
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
|
||||
* @return {Range | TextRange | null} range
|
||||
*/
|
||||
exports.getSelection = function getSelection() {
|
||||
if (window.getSelection) {
|
||||
var sel = window.getSelection();
|
||||
if (sel.getRangeAt && sel.rangeCount) {
|
||||
return sel.getRangeAt(0);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set text selection
|
||||
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
|
||||
* @param {Range | TextRange | null} range
|
||||
*/
|
||||
exports.setSelection = function setSelection(range) {
|
||||
if (range) {
|
||||
if (window.getSelection) {
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get selected text range
|
||||
* @return {Object} params object containing parameters:
|
||||
* {Number} startOffset
|
||||
* {Number} endOffset
|
||||
* {Element} container HTML element holding the
|
||||
* selected text element
|
||||
* Returns null if no text selection is found
|
||||
*/
|
||||
exports.getSelectionOffset = function getSelectionOffset() {
|
||||
var range = exports.getSelection();
|
||||
|
||||
if (range && 'startOffset' in range && 'endOffset' in range &&
|
||||
range.startContainer && (range.startContainer == range.endContainer)) {
|
||||
return {
|
||||
startOffset: range.startOffset,
|
||||
endOffset: range.endOffset,
|
||||
container: range.startContainer.parentNode
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set selected text range in given element
|
||||
* @param {Object} params An object containing:
|
||||
* {Element} container
|
||||
* {Number} startOffset
|
||||
* {Number} endOffset
|
||||
*/
|
||||
exports.setSelectionOffset = function setSelectionOffset(params) {
|
||||
if (document.createRange && window.getSelection) {
|
||||
var selection = window.getSelection();
|
||||
if(selection) {
|
||||
var range = document.createRange();
|
||||
|
||||
if (!params.container.firstChild) {
|
||||
params.container.appendChild(document.createTextNode(''));
|
||||
}
|
||||
|
||||
// TODO: do not suppose that the first child of the container is a textnode,
|
||||
// but recursively find the textnodes
|
||||
range.setStart(params.container.firstChild, params.startOffset);
|
||||
range.setEnd(params.container.firstChild, params.endOffset);
|
||||
|
||||
exports.setSelection(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the inner text of an HTML element (for example a div element)
|
||||
* @param {Element} element
|
||||
* @param {Object} [buffer]
|
||||
* @return {String} innerText
|
||||
*/
|
||||
exports.getInnerText = function getInnerText(element, buffer) {
|
||||
var first = (buffer == undefined);
|
||||
if (first) {
|
||||
buffer = {
|
||||
'text': '',
|
||||
'flush': function () {
|
||||
var text = this.text;
|
||||
this.text = '';
|
||||
return text;
|
||||
},
|
||||
'set': function (text) {
|
||||
this.text = text;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// text node
|
||||
if (element.nodeValue) {
|
||||
return buffer.flush() + element.nodeValue;
|
||||
}
|
||||
|
||||
// divs or other HTML elements
|
||||
if (element.hasChildNodes()) {
|
||||
var childNodes = element.childNodes;
|
||||
var innerText = '';
|
||||
|
||||
for (var i = 0, iMax = childNodes.length; i < iMax; i++) {
|
||||
var child = childNodes[i];
|
||||
|
||||
if (child.nodeName == 'DIV' || child.nodeName == 'P') {
|
||||
var prevChild = childNodes[i - 1];
|
||||
var prevName = prevChild ? prevChild.nodeName : undefined;
|
||||
if (prevName && prevName != 'DIV' && prevName != 'P' && prevName != 'BR') {
|
||||
innerText += '\n';
|
||||
buffer.flush();
|
||||
}
|
||||
innerText += exports.getInnerText(child, buffer);
|
||||
buffer.set('\n');
|
||||
}
|
||||
else if (child.nodeName == 'BR') {
|
||||
innerText += buffer.flush();
|
||||
buffer.set('\n');
|
||||
}
|
||||
else {
|
||||
innerText += exports.getInnerText(child, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return innerText;
|
||||
}
|
||||
else {
|
||||
if (element.nodeName == 'P' && exports.getInternetExplorerVersion() != -1) {
|
||||
// On Internet Explorer, a <p> with hasChildNodes()==false is
|
||||
// rendered with a new line. Note that a <p> with
|
||||
// hasChildNodes()==true is rendered without a new line
|
||||
// Other browsers always ensure there is a <br> inside the <p>,
|
||||
// and if not, the <p> does not render a new line
|
||||
return buffer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
// br or unknown
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the version of Internet Explorer or a -1
|
||||
* (indicating the use of another browser).
|
||||
* Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx
|
||||
* @return {Number} Internet Explorer version, or -1 in case of an other browser
|
||||
*/
|
||||
exports.getInternetExplorerVersion = function getInternetExplorerVersion() {
|
||||
if (_ieVersion == -1) {
|
||||
var rv = -1; // Return value assumes failure.
|
||||
if (navigator.appName == 'Microsoft Internet Explorer')
|
||||
{
|
||||
var ua = navigator.userAgent;
|
||||
var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
|
||||
if (re.exec(ua) != null) {
|
||||
rv = parseFloat( RegExp.$1 );
|
||||
}
|
||||
}
|
||||
|
||||
_ieVersion = rv;
|
||||
}
|
||||
|
||||
return _ieVersion;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the current browser is Firefox
|
||||
* @returns {boolean} isFirefox
|
||||
*/
|
||||
exports.isFirefox = function isFirefox () {
|
||||
return (navigator.userAgent.indexOf("Firefox") != -1);
|
||||
};
|
||||
|
||||
/**
|
||||
* cached internet explorer version
|
||||
* @type {Number}
|
||||
* @private
|
||||
*/
|
||||
var _ieVersion = -1;
|
||||
|
||||
/**
|
||||
* Add and event listener. Works for all browsers
|
||||
* @param {Element} element An html element
|
||||
* @param {string} action The action, for example "click",
|
||||
* without the prefix "on"
|
||||
* @param {function} listener The callback function to be executed
|
||||
* @param {boolean} [useCapture] false by default
|
||||
* @return {function} the created event listener
|
||||
*/
|
||||
exports.addEventListener = function addEventListener(element, action, listener, useCapture) {
|
||||
if (element.addEventListener) {
|
||||
if (useCapture === undefined)
|
||||
useCapture = false;
|
||||
|
||||
if (action === "mousewheel" && exports.isFirefox()) {
|
||||
action = "DOMMouseScroll"; // For Firefox
|
||||
}
|
||||
|
||||
element.addEventListener(action, listener, useCapture);
|
||||
return listener;
|
||||
} else if (element.attachEvent) {
|
||||
// Old IE browsers
|
||||
var f = function () {
|
||||
return listener.call(element, window.event);
|
||||
};
|
||||
element.attachEvent("on" + action, f);
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an event listener from an element
|
||||
* @param {Element} element An html dom element
|
||||
* @param {string} action The name of the event, for example "mousedown"
|
||||
* @param {function} listener The listener function
|
||||
* @param {boolean} [useCapture] false by default
|
||||
*/
|
||||
exports.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
|
||||
if (element.removeEventListener) {
|
||||
if (useCapture === undefined)
|
||||
useCapture = false;
|
||||
|
||||
if (action === "mousewheel" && exports.isFirefox()) {
|
||||
action = "DOMMouseScroll"; // For Firefox
|
||||
}
|
||||
|
||||
element.removeEventListener(action, listener, useCapture);
|
||||
} else if (element.detachEvent) {
|
||||
// Old IE browsers
|
||||
element.detachEvent("on" + action, listener);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a JSON path like '.items[3].name' into an array
|
||||
* @param {string} jsonPath
|
||||
* @return {Array}
|
||||
*/
|
||||
exports.parsePath = function parsePath(jsonPath) {
|
||||
var prop, remainder;
|
||||
|
||||
if (jsonPath.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// find a match like '.prop'
|
||||
var match = jsonPath.match(/^\.(\w+)/);
|
||||
if (match) {
|
||||
prop = match[1];
|
||||
remainder = jsonPath.substr(prop.length + 1);
|
||||
}
|
||||
else if (jsonPath[0] === '[') {
|
||||
// find a match like
|
||||
var end = jsonPath.indexOf(']');
|
||||
if (end === -1) {
|
||||
throw new SyntaxError('Character ] expected in path');
|
||||
}
|
||||
if (end === 1) {
|
||||
throw new SyntaxError('Index expected after [');
|
||||
}
|
||||
|
||||
var value = jsonPath.substring(1, end);
|
||||
prop = value === '*' ? value : JSON.parse(value); // parse string and number
|
||||
remainder = jsonPath.substr(end + 1);
|
||||
}
|
||||
else {
|
||||
throw new SyntaxError('Failed to parse path');
|
||||
}
|
||||
|
||||
return [prop].concat(parsePath(remainder))
|
||||
};
|
||||
|
||||
/**
|
||||
* Improve the error message of a JSON schema error
|
||||
* @param {Object} error
|
||||
* @return {Object} The error
|
||||
*/
|
||||
exports.improveSchemaError = function (error) {
|
||||
if (error.keyword === 'enum' && Array.isArray(error.schema)) {
|
||||
var enums = error.schema;
|
||||
if (enums) {
|
||||
enums = enums.map(function (value) {
|
||||
return JSON.stringify(value);
|
||||
});
|
||||
|
||||
if (enums.length > 5) {
|
||||
var more = ['(' + (enums.length - 5) + ' more...)'];
|
||||
enums = enums.slice(0, 5);
|
||||
enums.push(more);
|
||||
}
|
||||
error.message = 'should be equal to one of: ' + enums.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
if (error.keyword === 'additionalProperties') {
|
||||
error.message = 'should NOT have additional property: ' + error.params.additionalProperty;
|
||||
}
|
||||
|
||||
return error;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the child rect fits completely inside the parent rect.
|
||||
* @param {ClientRect} parent
|
||||
* @param {ClientRect} child
|
||||
* @param {number} margin
|
||||
*/
|
||||
exports.insideRect = function (parent, child, margin) {
|
||||
var _margin = margin !== undefined ? margin : 0;
|
||||
return child.left - _margin >= parent.left
|
||||
&& child.right + _margin <= parent.right
|
||||
&& child.top - _margin >= parent.top
|
||||
&& child.bottom + _margin <= parent.bottom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function, that, as long as it continues to be invoked, will not
|
||||
* be triggered. The function will be called after it stops being called for
|
||||
* N milliseconds.
|
||||
*
|
||||
* Source: https://davidwalsh.name/javascript-debounce-function
|
||||
*
|
||||
* @param {function} func
|
||||
* @param {number} wait Number in milliseconds
|
||||
* @param {boolean} [immediate=false] If `immediate` is passed, trigger the
|
||||
* function on the leading edge, instead
|
||||
* of the trailing.
|
||||
* @return {function} Return the debounced function
|
||||
*/
|
||||
exports.debounce = function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this, args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines the difference between two texts.
|
||||
* Can only detect one removed or inserted block of characters.
|
||||
* @param {string} oldText
|
||||
* @param {string} newText
|
||||
* @return {{start: number, end: number}} Returns the start and end
|
||||
* of the changed part in newText.
|
||||
*/
|
||||
exports.textDiff = function textDiff(oldText, newText) {
|
||||
var len = newText.length;
|
||||
var start = 0;
|
||||
var oldEnd = oldText.length;
|
||||
var newEnd = newText.length;
|
||||
|
||||
while (newText.charAt(start) === oldText.charAt(start)
|
||||
&& start < len) {
|
||||
start++;
|
||||
}
|
||||
|
||||
while (newText.charAt(newEnd - 1) === oldText.charAt(oldEnd - 1)
|
||||
&& newEnd > start && oldEnd > 0) {
|
||||
newEnd--;
|
||||
oldEnd--;
|
||||
}
|
||||
|
||||
return {start: start, end: newEnd};
|
||||
};
|
|
@ -0,0 +1,513 @@
|
|||
import last from 'lodash/last'
|
||||
import initial from 'lodash/initial'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import first from 'lodash/first'
|
||||
import { findRootPath, toJSON } from './eson'
|
||||
import { getIn } from './utils/immutabilityHelpers'
|
||||
import { duplicateInText, findUniqueName } from './utils/stringUtils'
|
||||
import { isObject, stringConvert } from './utils/typeUtils'
|
||||
import { compareAsc, compareDesc } from './utils/arrayUtils'
|
||||
import { compileJSONPointer, parseJSONPointer } from './jsonPointer'
|
||||
|
||||
/**
|
||||
* Create a JSONPatch to change the value of a property or item
|
||||
* @param {JSON} eson
|
||||
* @param {Path} path
|
||||
* @param {*} value
|
||||
* @return {Array}
|
||||
*/
|
||||
export function changeValue (eson, path, value) {
|
||||
// console.log('changeValue', data, value)
|
||||
|
||||
return [{
|
||||
op: 'replace',
|
||||
path: compileJSONPointer(path),
|
||||
value
|
||||
}]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch to change a property name
|
||||
* @param {JSON} json
|
||||
* @param {Path} parentPath
|
||||
* @param {string} oldProp
|
||||
* @param {string} newProp
|
||||
* @return {Array}
|
||||
*/
|
||||
export function changeProperty (json, parentPath, oldProp, newProp) {
|
||||
// console.log('changeProperty', parentPath, oldProp, newProp)
|
||||
const parent = getIn(json, parentPath)
|
||||
|
||||
// prevent duplicate property names
|
||||
const uniqueNewProp = findUniqueName(newProp, parent)
|
||||
|
||||
return [{
|
||||
op: 'move',
|
||||
from: compileJSONPointer(parentPath.concat(oldProp)),
|
||||
path: compileJSONPointer(parentPath.concat(uniqueNewProp))
|
||||
}]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch to change the type of a property or item
|
||||
* @param {JSON} json
|
||||
* @param {Path} path
|
||||
* @param {ESONType} type
|
||||
* @return {Array}
|
||||
*/
|
||||
export function changeType (json, path, type) {
|
||||
const oldValue = getIn(json, path)
|
||||
const newValue = convertType(oldValue, type)
|
||||
|
||||
// console.log('changeType', path, type, oldValue, newValue)
|
||||
|
||||
return [{
|
||||
op: 'replace',
|
||||
path: compileJSONPointer(path),
|
||||
value: newValue
|
||||
}]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch for a duplicate action.
|
||||
*
|
||||
* This function needs the current data in order to be able to determine
|
||||
* a unique property name for the duplicated node in case of duplicating
|
||||
* and object property
|
||||
*
|
||||
* @param {JSON} json
|
||||
* @param {Selection} selection
|
||||
* @return {Array}
|
||||
*/
|
||||
export function duplicate (json, selection) {
|
||||
if (!isEmpty(selection.multi)) {
|
||||
const rootPath = findRootPath(selection)
|
||||
const root = getIn(json, rootPath)
|
||||
const paths = selection.multi.map(parseJSONPointer)
|
||||
|
||||
if (Array.isArray(root)) {
|
||||
const lastPath = last(paths)
|
||||
const offset = lastPath ? (parseInt(last(lastPath), 10) + 1) : 0
|
||||
|
||||
return paths.map((path, index) => ({
|
||||
op: 'copy',
|
||||
from: compileJSONPointer(path),
|
||||
path: compileJSONPointer(rootPath.concat(index + offset))
|
||||
}))
|
||||
}
|
||||
else { // 'object'
|
||||
return paths.map(path => {
|
||||
const prop = last(path)
|
||||
const newProp = findUniqueName(prop, root)
|
||||
|
||||
return {
|
||||
op: 'copy',
|
||||
from: compileJSONPointer(path),
|
||||
path: compileJSONPointer(rootPath.concat(newProp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (selection.type === 'caret') {
|
||||
if (selection.anchorOffset === selection.focusOffset) {
|
||||
// no text selected -> duplicate the current node
|
||||
return duplicate(json, {
|
||||
type: 'multi',
|
||||
multi: [selection.path]
|
||||
})
|
||||
}
|
||||
else {
|
||||
// no text selected -> duplicate selected text
|
||||
if (selection.input === 'property') {
|
||||
const path = parseJSONPointer(selection.path)
|
||||
const parentPath = initial(path)
|
||||
const oldProperty = last(path)
|
||||
const newProperty = duplicateInText(oldProperty, selection.anchorOffset, selection.focusOffset)
|
||||
|
||||
return changeProperty(json, parentPath, oldProperty, newProperty)
|
||||
}
|
||||
else { // selection.input === 'value'
|
||||
const oldValue = String(toJSON(getIn(json, parseJSONPointer(selection.path))))
|
||||
const newValue = duplicateInText(oldValue, selection.anchorOffset, selection.focusOffset)
|
||||
|
||||
return changeValue(json, parseJSONPointer(selection.path), newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch for an insert action.
|
||||
*
|
||||
* This function needs the current data in order to be able to determine
|
||||
* a unique property name for the inserted node in case of duplicating
|
||||
* and object property
|
||||
*
|
||||
* @param {JSON} json
|
||||
* @param {Path} path
|
||||
* @param {Array.<{name?: string, value: JSON}>} values
|
||||
* @return {Array}
|
||||
*/
|
||||
export function insertBefore (json, path, values) { // TODO: find a better name and define datastructure for values
|
||||
// TODO: refactor. path should be parent path
|
||||
const parentPath = initial(path)
|
||||
const parent = getIn(json, parentPath)
|
||||
|
||||
if (Array.isArray(parent)) {
|
||||
const startIndex = parseInt(last(path), 10)
|
||||
return values.map((entry, offset) => ({
|
||||
op: 'add',
|
||||
path: compileJSONPointer(parentPath.concat(startIndex + offset)),
|
||||
value: entry.value
|
||||
}))
|
||||
}
|
||||
else { // 'object'
|
||||
return values.map(entry => {
|
||||
const newProp = findUniqueName(entry.name, parent)
|
||||
return {
|
||||
op: 'add',
|
||||
path: compileJSONPointer(parentPath.concat(newProp)),
|
||||
value: entry.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch for an insert action.
|
||||
*
|
||||
* This function needs the current data in order to be able to determine
|
||||
* a unique property name for the inserted node in case of duplicating
|
||||
* and object property
|
||||
*
|
||||
* @param {JSON} json
|
||||
* @param {Path} path
|
||||
* @param {Array.<{name?: string, value: JSON}>} values
|
||||
* @return {Array}
|
||||
*/
|
||||
export function insertAfter (json, path, values) { // TODO: find a better name and define datastructure for values
|
||||
// TODO: refactor. path should be parent path
|
||||
const parentPath = initial(path)
|
||||
const parent = getIn(json, parentPath)
|
||||
|
||||
if (Array.isArray(parent)) {
|
||||
const startIndex = parseInt(last(path), 10)
|
||||
return values.map((entry, offset) => ({
|
||||
op: 'add',
|
||||
path: compileJSONPointer(parentPath.concat(startIndex + 1 + offset)), // +1 to insert after
|
||||
value: entry.value
|
||||
}))
|
||||
}
|
||||
else { // 'object'
|
||||
return values.map(entry => {
|
||||
const newProp = findUniqueName(entry.name, parent)
|
||||
return {
|
||||
op: 'add',
|
||||
path: compileJSONPointer(parentPath.concat(newProp)),
|
||||
value: entry.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert values at the start of an Object or Array
|
||||
* @param {JSON} json
|
||||
* @param {Path} parentPath
|
||||
* @param {Array.<{name?: string, value: JSON}>} values
|
||||
* @return {Array}
|
||||
*/
|
||||
export function insertInside (json, parentPath, values) {
|
||||
const parent = getIn(json, parentPath)
|
||||
|
||||
if (Array.isArray(parent)) {
|
||||
return insertBefore(json, parentPath.concat('0'), values)
|
||||
}
|
||||
else if (parent && typeof parent === 'object') {
|
||||
// TODO: refactor. path should be parent path
|
||||
return insertBefore(json, parentPath.concat('foobar'), values)
|
||||
}
|
||||
else {
|
||||
throw new Error('Cannot insert in a value, only in an Object or Array')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch for an insert action.
|
||||
*
|
||||
* This function needs the current data in order to be able to determine
|
||||
* a unique property name for the inserted node in case of duplicating
|
||||
* and object property
|
||||
*
|
||||
* @param {JSON} json
|
||||
* @param {Selection} selection
|
||||
* @param {Array.<{name?: string, value: JSON}>} values
|
||||
* @return {Array}
|
||||
*/
|
||||
export function replace (json, selection, values) { // TODO: find a better name and define datastructure for values
|
||||
const rootPath = findRootPath(selection)
|
||||
const root = getIn(json, rootPath)
|
||||
const paths = selection.multi
|
||||
? selection.multi.map(parseJSONPointer)
|
||||
: []
|
||||
|
||||
if (Array.isArray(root)) {
|
||||
const firstPath = first(paths)
|
||||
const offset = firstPath ? parseInt(last(firstPath), 10) : 0
|
||||
|
||||
const removeActions = removeAll(paths)
|
||||
const insertActions = values.map((entry, index) => ({
|
||||
op: 'add',
|
||||
path: compileJSONPointer(rootPath.concat(index + offset)),
|
||||
value: entry.value
|
||||
}))
|
||||
|
||||
return removeActions.concat(insertActions)
|
||||
}
|
||||
else { // root is Object
|
||||
const removeActions = removeAll(paths)
|
||||
const insertActions = values.map(entry => {
|
||||
const newProp = findUniqueName(entry.name, root)
|
||||
return {
|
||||
op: 'add',
|
||||
path: compileJSONPointer(rootPath.concat(newProp)),
|
||||
value: entry.value
|
||||
}
|
||||
})
|
||||
|
||||
return removeActions.concat(insertActions)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch for an append action.
|
||||
*
|
||||
* This function needs the current data in order to be able to determine
|
||||
* a unique property name for the inserted node in case of duplicating
|
||||
* and object property
|
||||
*
|
||||
* @param {JSON} json
|
||||
* @param {Path} parentPath
|
||||
* @param {ESONType} type
|
||||
* @return {Array}
|
||||
*/
|
||||
export function append (json, parentPath, type) {
|
||||
// console.log('append', parentPath, value)
|
||||
|
||||
const parent = getIn(json, parentPath)
|
||||
const value = createEntry(type)
|
||||
|
||||
if (Array.isArray(parent)) {
|
||||
return [{
|
||||
op: 'add',
|
||||
path: compileJSONPointer(parentPath.concat('-')),
|
||||
value
|
||||
}]
|
||||
}
|
||||
else { // 'object'
|
||||
const newProp = findUniqueName('', parent)
|
||||
|
||||
return [{
|
||||
op: 'add',
|
||||
path: compileJSONPointer(parentPath.concat(newProp)),
|
||||
value
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch for a remove action
|
||||
* @param {Path} path
|
||||
* @return {JSONPatchDocument}
|
||||
*/
|
||||
export function remove (path) {
|
||||
return [{
|
||||
op: 'remove',
|
||||
path: compileJSONPointer(path)
|
||||
}]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSONPatch for a multiple remove action
|
||||
* @param {Path[]} paths
|
||||
* @return {JSONPatchDocument}
|
||||
*/
|
||||
export function removeAll (paths) {
|
||||
return paths
|
||||
.map(path => ({
|
||||
op: 'remove',
|
||||
path: compileJSONPointer(path)
|
||||
}))
|
||||
.reverse() // reverse is needed for arrays: delete the last index first
|
||||
}
|
||||
// TODO: test removeAll
|
||||
|
||||
/**
|
||||
* Create a JSONPatch to order the items of an array or the properties of an object in ascending
|
||||
* or descending order. In case of ESON, this will maintain state of expanded items
|
||||
* @param {JSON} json
|
||||
* @param {Path} path
|
||||
* @param {'asc' | 'desc' | null} [order=null] If not provided, will toggle current ordering
|
||||
* @return {Array}
|
||||
*/
|
||||
export function sort (json, path, order = null) {
|
||||
const compare = order === 'desc' ? compareDesc : compareAsc
|
||||
const reverseCompare = (a, b) => -compare(a, b)
|
||||
const object = getIn(json, path)
|
||||
|
||||
if (Array.isArray(object)) {
|
||||
const createAction = ({item, fromIndex, toIndex}) => ({
|
||||
op: 'move',
|
||||
from: compileJSONPointer(path.concat(String(fromIndex))),
|
||||
path: compileJSONPointer(path.concat(String(toIndex)))
|
||||
})
|
||||
|
||||
const actions = sortWithComparator(object, compare).map(createAction)
|
||||
|
||||
// when no order is provided, test whether ordering ascending
|
||||
// changed anything. If not, sort descending
|
||||
if (!order && isEmpty(actions)) {
|
||||
return sortWithComparator(object, reverseCompare).map(createAction)
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
else { // object is an Object, we don't allow sorting properties
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort an array with items using given comparator, and generate move actions
|
||||
* (json patch) to apply the ordering.
|
||||
*
|
||||
* @param {Array} items
|
||||
* @param {function (a, b)} comparator Accepts to values,
|
||||
* returns 1 when a is larger than b
|
||||
* returns 0 when a is equal to b
|
||||
* returns -1 when a is smaller than b
|
||||
* @return {Array.<{item: *, beforeItem: *, fromIndex: number, toIndex: number}>}
|
||||
* Returns an array with move actions that need to be
|
||||
* performed to order the items of the array.
|
||||
* This can be turned into json-patch actions
|
||||
*/
|
||||
function sortWithComparator (items, comparator) {
|
||||
const orderedItems = items.slice()
|
||||
|
||||
let moveActions = []
|
||||
for (let i = 0; i < orderedItems.length; i++) {
|
||||
let firstIndex = i
|
||||
for (let j = i; j < orderedItems.length; j++) {
|
||||
if (comparator(orderedItems[firstIndex], orderedItems[j]) > 0) {
|
||||
firstIndex = j
|
||||
}
|
||||
}
|
||||
|
||||
if (i !== firstIndex) {
|
||||
const item = orderedItems[firstIndex]
|
||||
orderedItems.splice(firstIndex, 1)
|
||||
orderedItems.unshift(item)
|
||||
|
||||
moveActions.push({
|
||||
item,
|
||||
fromIndex: firstIndex,
|
||||
toIndex: i
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return moveActions
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSON entry
|
||||
* @param {ESONType} type
|
||||
* @return {Array | Object | string}
|
||||
*/
|
||||
export function createEntry (type) {
|
||||
if (type === 'array') {
|
||||
return []
|
||||
}
|
||||
else if (type === 'object') {
|
||||
return {}
|
||||
}
|
||||
else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a JSON object into a different type. When possible, data is retained
|
||||
* @param {*} value
|
||||
* @param {ESONType} type
|
||||
* @return {*}
|
||||
*/
|
||||
export function convertType (value, type) {
|
||||
// convert contents from old value to new value where possible
|
||||
if (type === 'value') {
|
||||
if (typeof value === 'string') {
|
||||
return stringConvert(value)
|
||||
}
|
||||
else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'string') {
|
||||
if (!isObject(value) && !Array.isArray(value)) {
|
||||
return value + ''
|
||||
}
|
||||
else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'object') {
|
||||
let object = {}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item, index) => object[index] = item)
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
if (type === 'array') {
|
||||
let array = []
|
||||
|
||||
if (isObject(value)) {
|
||||
Object.keys(value).forEach(key => {
|
||||
array.push(value[key])
|
||||
})
|
||||
}
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
throw new Error(`Unknown type '${type}'`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the patched nodes and create a selection
|
||||
* @param {JSONPatchDocument} operations
|
||||
* @return {Selection | null}
|
||||
*/
|
||||
export function getSelectionFromPatch (operations) {
|
||||
const paths = operations
|
||||
.filter(operation => operation.op !== 'remove' && operation.op !== 'test')
|
||||
.map(operation => operation.path)
|
||||
|
||||
if (!isEmpty(paths)) {
|
||||
console.log('selectPatchedPaths', paths)
|
||||
|
||||
return {
|
||||
type: 'multi', multi: paths }
|
||||
}
|
||||
|
||||
// TODO: after a remove, select after?
|
||||
|
||||
return null
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
'use strict'
|
||||
|
||||
import { sort } from './actions'
|
||||
import { createAssertEqualEson } from './utils/assertEqualEson'
|
||||
import { ID, syncEson } from './eson'
|
||||
import { immutableJSONPatch } from './immutableJSONPatch'
|
||||
|
||||
const assertEqualEson = createAssertEqualEson(expect)
|
||||
|
||||
// TODO: test changeValue
|
||||
// TODO: test changeProperty
|
||||
// TODO: test changeType (or cleanup the function)
|
||||
// TODO: test duplicate
|
||||
// TODO: test insertBefore
|
||||
// TODO: test replace
|
||||
// TODO: test append
|
||||
// TODO: test remove
|
||||
// TODO: test removeAll
|
||||
|
||||
it('sort root Array (json)', () => {
|
||||
const json = [1,3,2]
|
||||
const sorted1 = immutableJSONPatch(json, sort(json, [])).json
|
||||
const sorted2 = immutableJSONPatch(json, sort(json, [], 'asc')).json
|
||||
const sorted3 = immutableJSONPatch(json, sort(json, [], 'desc')).json
|
||||
|
||||
assertEqualEson(json, [1,3, 2]) // should be untouched
|
||||
assertEqualEson(sorted1, [1,2,3])
|
||||
assertEqualEson(sorted2, [1,2,3])
|
||||
assertEqualEson(sorted3, [3,2,1])
|
||||
})
|
||||
|
||||
it('sort root Array (eson)', () => {
|
||||
const eson = syncEson([1,3,2])
|
||||
const sorted1 = immutableJSONPatch(eson, sort(eson, [])).json
|
||||
const sorted2 = immutableJSONPatch(eson, sort(eson, [], 'asc')).json
|
||||
const sorted3 = immutableJSONPatch(eson, sort(eson, [], 'desc')).json
|
||||
|
||||
assertEqualEson(sorted1, syncEson([1, 2, 3]))
|
||||
assertEqualEson(sorted1[0], eson[0], false)
|
||||
assertEqualEson(sorted1[1], eson[2], false)
|
||||
assertEqualEson(sorted1[2], eson[1], false)
|
||||
expect(sorted1).not.toBe(eson)
|
||||
|
||||
assertEqualEson(sorted2[0], eson[0], false)
|
||||
assertEqualEson(sorted2[1], eson[2], false)
|
||||
assertEqualEson(sorted2[2], eson[1], false)
|
||||
expect(sorted2).not.toBe(eson)
|
||||
|
||||
assertEqualEson(sorted3[0], eson[1], false)
|
||||
assertEqualEson(sorted3[1], eson[2], false)
|
||||
assertEqualEson(sorted3[2], eson[0], false)
|
||||
expect(sorted3).not.toBe(eson)
|
||||
})
|
||||
|
||||
it('sort nested Array', () => {
|
||||
const eson = syncEson({arr: [4,1,8,5,3,9,2,7,6]})
|
||||
const actual = immutableJSONPatch(eson, sort(eson, ['arr'])).json
|
||||
const expected = syncEson({arr: [1,2,3,4,5,6,7,8,9]})
|
||||
assertEqualEson(actual, expected)
|
||||
})
|
||||
|
||||
it('sort nested Array reverse order', () => {
|
||||
// no order provided -> order ascending, but if nothing changes, order descending
|
||||
const eson = syncEson({arr: [1,2,3,4,5,6,7,8,9]})
|
||||
const actual = immutableJSONPatch(eson, sort(eson, ['arr'])).json
|
||||
const expected = syncEson({arr: [9,8,7,6,5,4,3,2,1]})
|
||||
assertEqualEson(actual, expected)
|
||||
|
||||
// id's and META should be the same
|
||||
expect(actual.arr[ID]).toEqual(eson.arr[ID])
|
||||
expect(actual.arr[7][ID]).toEqual(eson.arr[1][ID])
|
||||
})
|
|
@ -0,0 +1,8 @@
|
|||
// load brace
|
||||
import ace from 'brace'
|
||||
|
||||
// load required ace plugins
|
||||
import 'brace/mode/json'
|
||||
import 'brace/ext/searchbox'
|
||||
|
||||
export default ace
|
|
@ -0,0 +1,144 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
window.ace.define('ace/theme/jsoneditor', ['require', 'exports', 'module', 'ace/lib/dom'], function(acequire, exports, module) {
|
||||
|
||||
exports.isDark = false
|
||||
exports.cssClass = 'ace-jsoneditor'
|
||||
exports.cssText = `.ace-jsoneditor .ace_gutter {
|
||||
background: #ebebeb;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.ace-jsoneditor.ace_editor {
|
||||
font-family: "dejavu sans mono", "droid sans mono", consolas, monaco, "lucida console", "courier new", courier, monospace, sans-serif;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.ace-jsoneditor .ace_print-margin {
|
||||
width: 1px;
|
||||
background: #e8e8e8
|
||||
}
|
||||
.ace-jsoneditor .ace_scroller {
|
||||
background-color: #FFFFFF
|
||||
}
|
||||
.ace-jsoneditor .ace_text-layer {
|
||||
color: gray
|
||||
}
|
||||
.ace-jsoneditor .ace_variable {
|
||||
color: #1a1a1a
|
||||
}
|
||||
.ace-jsoneditor .ace_cursor {
|
||||
border-left: 2px solid #000000
|
||||
}
|
||||
.ace-jsoneditor .ace_overwrite-cursors .ace_cursor {
|
||||
border-left: 0px;
|
||||
border-bottom: 1px solid #000000
|
||||
}
|
||||
.ace-jsoneditor .ace_marker-layer .ace_selection {
|
||||
background: lightgray
|
||||
}
|
||||
.ace-jsoneditor.ace_multiselect .ace_selection.ace_start {
|
||||
box-shadow: 0 0 3px 0px #FFFFFF;
|
||||
border-radius: 2px
|
||||
}
|
||||
.ace-jsoneditor .ace_marker-layer .ace_step {
|
||||
background: rgb(255, 255, 0)
|
||||
}
|
||||
.ace-jsoneditor .ace_marker-layer .ace_bracket {
|
||||
margin: -1px 0 0 -1px;
|
||||
border: 1px solid #BFBFBF
|
||||
}
|
||||
.ace-jsoneditor .ace_marker-layer .ace_active-line {
|
||||
background: #FFFBD1
|
||||
}
|
||||
.ace-jsoneditor .ace_gutter-active-line {
|
||||
background-color : #dcdcdc
|
||||
}
|
||||
.ace-jsoneditor .ace_marker-layer .ace_selected-word {
|
||||
border: 1px solid lightgray
|
||||
}
|
||||
.ace-jsoneditor .ace_invisible {
|
||||
color: #BFBFBF
|
||||
}
|
||||
.ace-jsoneditor .ace_keyword,
|
||||
.ace-jsoneditor .ace_meta,
|
||||
.ace-jsoneditor .ace_support.ace_constant.ace_property-value {
|
||||
color: #AF956F
|
||||
}
|
||||
.ace-jsoneditor .ace_keyword.ace_operator {
|
||||
color: #484848
|
||||
}
|
||||
.ace-jsoneditor .ace_keyword.ace_other.ace_unit {
|
||||
color: #96DC5F
|
||||
}
|
||||
.ace-jsoneditor .ace_constant.ace_language {
|
||||
color: darkorange
|
||||
}
|
||||
.ace-jsoneditor .ace_constant.ace_numeric {
|
||||
color: red
|
||||
}
|
||||
.ace-jsoneditor .ace_constant.ace_character.ace_entity {
|
||||
color: #BF78CC
|
||||
}
|
||||
.ace-jsoneditor .ace_invalid {
|
||||
color: #FFFFFF;
|
||||
background-color: #FF002A;
|
||||
}
|
||||
.ace-jsoneditor .ace_fold {
|
||||
background-color: #AF956F;
|
||||
border-color: #000000
|
||||
}
|
||||
.ace-jsoneditor .ace_storage,
|
||||
.ace-jsoneditor .ace_support.ace_class,
|
||||
.ace-jsoneditor .ace_support.ace_function,
|
||||
.ace-jsoneditor .ace_support.ace_other,
|
||||
.ace-jsoneditor .ace_support.ace_type {
|
||||
color: #C52727
|
||||
}
|
||||
.ace-jsoneditor .ace_string {
|
||||
color: green
|
||||
}
|
||||
.ace-jsoneditor .ace_comment {
|
||||
color: #BCC8BA
|
||||
}
|
||||
.ace-jsoneditor .ace_entity.ace_name.ace_tag,
|
||||
.ace-jsoneditor .ace_entity.ace_other.ace_attribute-name {
|
||||
color: #606060
|
||||
}
|
||||
.ace-jsoneditor .ace_markup.ace_underline {
|
||||
text-decoration: underline
|
||||
}
|
||||
.ace-jsoneditor .ace_indent-guide {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y
|
||||
}`
|
||||
|
||||
var dom = acequire('../lib/dom')
|
||||
dom.importCssString(exports.cssText, exports.cssClass)
|
||||
})
|
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* Jison generated parser
|
||||
* Refactored into an ES6 module
|
||||
**/
|
||||
var parser = (function(){
|
||||
var parser = {trace: function trace() { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},
|
||||
productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],
|
||||
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
case 1: // replace escaped characters with actual character
|
||||
this.$ = yytext.replace(/\\(\\|")/g, "$1")
|
||||
.replace(/\\n/g,'\n')
|
||||
.replace(/\\r/g,'\r')
|
||||
.replace(/\\t/g,'\t')
|
||||
.replace(/\\v/g,'\v')
|
||||
.replace(/\\f/g,'\f')
|
||||
.replace(/\\b/g,'\b');
|
||||
|
||||
break;
|
||||
case 2:this.$ = Number(yytext);
|
||||
break;
|
||||
case 3:this.$ = null;
|
||||
break;
|
||||
case 4:this.$ = true;
|
||||
break;
|
||||
case 5:this.$ = false;
|
||||
break;
|
||||
case 6:
|
||||
return this.$ = $$[$0-1];
|
||||
case 13:this.$ = {};
|
||||
break;
|
||||
case 14:this.$ = $$[$0-1];
|
||||
break;
|
||||
case 15:this.$ = [$$[$0-2], $$[$0]];
|
||||
break;
|
||||
case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1];
|
||||
break;
|
||||
case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1];
|
||||
break;
|
||||
case 18:this.$ = [];
|
||||
break;
|
||||
case 19:this.$ = $$[$0-1];
|
||||
break;
|
||||
case 20:this.$ = [$$[$0]];
|
||||
break;
|
||||
case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}],
|
||||
defaultActions: {16:[2,6]},
|
||||
parseError: function parseError(str, hash) {
|
||||
throw new Error(str);
|
||||
},
|
||||
parse: function parse(input) {
|
||||
var self = this,
|
||||
stack = [0],
|
||||
vstack = [null], // semantic value stack
|
||||
lstack = [], // location stack
|
||||
table = this.table,
|
||||
yytext = '',
|
||||
yylineno = 0,
|
||||
yyleng = 0,
|
||||
recovering = 0,
|
||||
TERROR = 2,
|
||||
EOF = 1;
|
||||
|
||||
//this.reductionCount = this.shiftCount = 0;
|
||||
|
||||
this.lexer.setInput(input);
|
||||
this.lexer.yy = this.yy;
|
||||
this.yy.lexer = this.lexer;
|
||||
if (typeof this.lexer.yylloc === 'undefined')
|
||||
this.lexer.yylloc = {};
|
||||
var yyloc = this.lexer.yylloc;
|
||||
lstack.push(yyloc);
|
||||
|
||||
if (typeof this.yy.parseError === 'function')
|
||||
this.parseError = this.yy.parseError;
|
||||
|
||||
function popStack (n) {
|
||||
stack.length = stack.length - 2*n;
|
||||
vstack.length = vstack.length - n;
|
||||
lstack.length = lstack.length - n;
|
||||
}
|
||||
|
||||
function lex() {
|
||||
var token;
|
||||
token = self.lexer.lex() || 1; // $end = 1
|
||||
// if token isn't its numeric value, convert
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
var symbol, preErrorSymbol, state, action, r, yyval={},p,len,newState, expected;
|
||||
while (true) {
|
||||
// retreive state number from top of stack
|
||||
state = stack[stack.length-1];
|
||||
|
||||
// use default actions if available
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state];
|
||||
} else {
|
||||
if (symbol == null)
|
||||
symbol = lex();
|
||||
// read action for current state and first input
|
||||
action = table[state] && table[state][symbol];
|
||||
}
|
||||
|
||||
// handle parse error
|
||||
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
|
||||
if (!recovering) {
|
||||
// Report error
|
||||
expected = [];
|
||||
for (p in table[state]) if (this.terminals_[p] && p > 2) {
|
||||
expected.push("'"+this.terminals_[p]+"'");
|
||||
}
|
||||
var errStr = '';
|
||||
if (this.lexer.showPosition) {
|
||||
errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
|
||||
} else {
|
||||
errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
|
||||
(symbol === 1 /*EOF*/ ? "end of input" :
|
||||
("'"+(this.terminals_[symbol] || symbol)+"'"));
|
||||
}
|
||||
this.parseError(errStr,
|
||||
{text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
|
||||
}
|
||||
|
||||
// just recovered from another error
|
||||
if (recovering === 3) {
|
||||
if (symbol === EOF) {
|
||||
throw new Error(errStr || 'Parsing halted.');
|
||||
}
|
||||
|
||||
// discard current lookahead and grab another
|
||||
yyleng = this.lexer.yyleng;
|
||||
yytext = this.lexer.yytext;
|
||||
yylineno = this.lexer.yylineno;
|
||||
yyloc = this.lexer.yylloc;
|
||||
symbol = lex();
|
||||
}
|
||||
|
||||
// try to recover from error
|
||||
while (1) {
|
||||
// check for error recovery rule in this state
|
||||
if ((TERROR.toString()) in table[state]) {
|
||||
break;
|
||||
}
|
||||
if (state === 0) {
|
||||
throw new Error(errStr || 'Parsing halted.');
|
||||
}
|
||||
popStack(1);
|
||||
state = stack[stack.length-1];
|
||||
}
|
||||
|
||||
preErrorSymbol = symbol; // save the lookahead token
|
||||
symbol = TERROR; // insert generic error symbol as new lookahead
|
||||
state = stack[stack.length-1];
|
||||
action = table[state] && table[state][TERROR];
|
||||
recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
|
||||
}
|
||||
|
||||
// this shouldn't happen, unless resolve defaults are off
|
||||
if (action[0] instanceof Array && action.length > 1) {
|
||||
throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
|
||||
}
|
||||
|
||||
switch (action[0]) {
|
||||
|
||||
case 1: // shift
|
||||
//this.shiftCount++;
|
||||
|
||||
stack.push(symbol);
|
||||
vstack.push(this.lexer.yytext);
|
||||
lstack.push(this.lexer.yylloc);
|
||||
stack.push(action[1]); // push state
|
||||
symbol = null;
|
||||
if (!preErrorSymbol) { // normal execution/no error
|
||||
yyleng = this.lexer.yyleng;
|
||||
yytext = this.lexer.yytext;
|
||||
yylineno = this.lexer.yylineno;
|
||||
yyloc = this.lexer.yylloc;
|
||||
if (recovering > 0)
|
||||
recovering--;
|
||||
} else { // error just occurred, resume old lookahead f/ before error
|
||||
symbol = preErrorSymbol;
|
||||
preErrorSymbol = null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // reduce
|
||||
//this.reductionCount++;
|
||||
|
||||
len = this.productions_[action[1]][1];
|
||||
|
||||
// perform semantic action
|
||||
yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
|
||||
// default location, uses first token for firsts, last for lasts
|
||||
yyval._$ = {
|
||||
first_line: lstack[lstack.length-(len||1)].first_line,
|
||||
last_line: lstack[lstack.length-1].last_line,
|
||||
first_column: lstack[lstack.length-(len||1)].first_column,
|
||||
last_column: lstack[lstack.length-1].last_column
|
||||
};
|
||||
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
|
||||
|
||||
if (typeof r !== 'undefined') {
|
||||
return r;
|
||||
}
|
||||
|
||||
// pop off stack
|
||||
if (len) {
|
||||
stack = stack.slice(0,-1*len*2);
|
||||
vstack = vstack.slice(0, -1*len);
|
||||
lstack = lstack.slice(0, -1*len);
|
||||
}
|
||||
|
||||
stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
|
||||
vstack.push(yyval.$);
|
||||
lstack.push(yyval._$);
|
||||
// goto new state = table[STATE][NONTERMINAL]
|
||||
newState = table[stack[stack.length-2]][stack[stack.length-1]];
|
||||
stack.push(newState);
|
||||
break;
|
||||
|
||||
case 3: // accept
|
||||
return true;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}};
|
||||
/* Jison generated lexer */
|
||||
var lexer = (function(){
|
||||
var lexer = ({EOF:1,
|
||||
parseError:function parseError(str, hash) {
|
||||
if (this.yy.parseError) {
|
||||
this.yy.parseError(str, hash);
|
||||
} else {
|
||||
throw new Error(str);
|
||||
}
|
||||
},
|
||||
setInput:function (input) {
|
||||
this._input = input;
|
||||
this._more = this._less = this.done = false;
|
||||
this.yylineno = this.yyleng = 0;
|
||||
this.yytext = this.matched = this.match = '';
|
||||
this.conditionStack = ['INITIAL'];
|
||||
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
|
||||
return this;
|
||||
},
|
||||
input:function () {
|
||||
var ch = this._input[0];
|
||||
this.yytext+=ch;
|
||||
this.yyleng++;
|
||||
this.match+=ch;
|
||||
this.matched+=ch;
|
||||
var lines = ch.match(/\n/);
|
||||
if (lines) this.yylineno++;
|
||||
this._input = this._input.slice(1);
|
||||
return ch;
|
||||
},
|
||||
unput:function (ch) {
|
||||
this._input = ch + this._input;
|
||||
return this;
|
||||
},
|
||||
more:function () {
|
||||
this._more = true;
|
||||
return this;
|
||||
},
|
||||
less:function (n) {
|
||||
this._input = this.match.slice(n) + this._input;
|
||||
},
|
||||
pastInput:function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length);
|
||||
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
|
||||
},
|
||||
upcomingInput:function () {
|
||||
var next = this.match;
|
||||
if (next.length < 20) {
|
||||
next += this._input.substr(0, 20-next.length);
|
||||
}
|
||||
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
|
||||
},
|
||||
showPosition:function () {
|
||||
var pre = this.pastInput();
|
||||
var c = new Array(pre.length + 1).join("-");
|
||||
return pre + this.upcomingInput() + "\n" + c+"^";
|
||||
},
|
||||
next:function () {
|
||||
if (this.done) {
|
||||
return this.EOF;
|
||||
}
|
||||
if (!this._input) this.done = true;
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index,
|
||||
lines;
|
||||
if (!this._more) {
|
||||
this.yytext = '';
|
||||
this.match = '';
|
||||
}
|
||||
var rules = this._currentRules();
|
||||
for (var i=0;i < rules.length; i++) {
|
||||
tempMatch = this._input.match(this.rules[rules[i]]);
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch;
|
||||
index = i;
|
||||
if (!this.options.flex) break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
lines = match[0].match(/\n.*/g);
|
||||
if (lines) this.yylineno += lines.length;
|
||||
this.yylloc = {first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno+1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
|
||||
this.yytext += match[0];
|
||||
this.match += match[0];
|
||||
this.yyleng = this.yytext.length;
|
||||
this._more = false;
|
||||
this._input = this._input.slice(match[0].length);
|
||||
this.matched += match[0];
|
||||
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
|
||||
if (this.done && this._input) this.done = false;
|
||||
if (token) return token;
|
||||
else return;
|
||||
}
|
||||
if (this._input === "") {
|
||||
return this.EOF;
|
||||
} else {
|
||||
this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
|
||||
{text: "", token: null, line: this.yylineno});
|
||||
}
|
||||
},
|
||||
lex:function lex() {
|
||||
var r = this.next();
|
||||
if (typeof r !== 'undefined') {
|
||||
return r;
|
||||
} else {
|
||||
return this.lex();
|
||||
}
|
||||
},
|
||||
begin:function begin(condition) {
|
||||
this.conditionStack.push(condition);
|
||||
},
|
||||
popState:function popState() {
|
||||
return this.conditionStack.pop();
|
||||
},
|
||||
_currentRules:function _currentRules() {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
|
||||
},
|
||||
topState:function () {
|
||||
return this.conditionStack[this.conditionStack.length-2];
|
||||
},
|
||||
pushState:function begin(condition) {
|
||||
this.begin(condition);
|
||||
}});
|
||||
lexer.options = {};
|
||||
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
||||
|
||||
switch($avoiding_name_collisions) {
|
||||
case 0: break; /* skip whitespace */
|
||||
case 1:return 6
|
||||
case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4
|
||||
case 3:return 17
|
||||
case 4:return 18
|
||||
case 5:return 23
|
||||
case 6:return 24
|
||||
case 7:return 22
|
||||
case 8:return 21
|
||||
case 9:return 10
|
||||
case 10:return 11
|
||||
case 11:return 8
|
||||
case 12:return 14
|
||||
case 13:return 'INVALID'
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/];
|
||||
lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}};
|
||||
|
||||
|
||||
return lexer;
|
||||
})()
|
||||
|
||||
parser.lexer = lexer;
|
||||
return parser;
|
||||
})();
|
||||
|
||||
|
||||
export default {
|
||||
parse: parser.parse.bind(parser),
|
||||
parser
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
# Jump.js
|
||||
|
||||
[![Jump.js on NPM](https://img.shields.io/npm/v/jump.js.svg?style=flat-square)](https://www.npmjs.com/package/jump.js)
|
||||
|
||||
A small, modern, dependency-free smooth scrolling library.
|
||||
|
||||
* [Demo Page](http://callmecavs.github.io/jump.js/) (Click the arrows!)
|
||||
|
||||
## Usage
|
||||
|
||||
Jump was developed with a modern JavaScript workflow in mind. To use it, it's recommended you have a build system in place that can transpile ES6, and bundle modules. For a minimal boilerplate that fulfills those requirements, check out [outset](https://github.com/callmecavs/outset).
|
||||
|
||||
Follow these steps to get started:
|
||||
|
||||
1. [Install](#install)
|
||||
2. [Import](#import)
|
||||
3. [Call](#call)
|
||||
4. [Review Options](#options)
|
||||
|
||||
### Install
|
||||
|
||||
Using NPM, install Jump, and save it to your `package.json` dependencies.
|
||||
|
||||
```bash
|
||||
$ npm install jump.js --save
|
||||
```
|
||||
|
||||
### Import
|
||||
|
||||
Import Jump, naming it according to your preference.
|
||||
|
||||
```es6
|
||||
// import Jump
|
||||
|
||||
import jump from 'jump.js'
|
||||
```
|
||||
|
||||
### Call
|
||||
|
||||
Jump exports a _singleton_, so there's no need to create an instance. Just call it, passing a [target](#target).
|
||||
|
||||
```es6
|
||||
// call Jump, passing a target
|
||||
|
||||
jump('.target')
|
||||
```
|
||||
|
||||
Note that the singleton can make an infinite number of jumps.
|
||||
|
||||
## Options
|
||||
|
||||
All options, **except [target](#target)**, are optional, and have sensible defaults. The defaults are shown below:
|
||||
|
||||
```es6
|
||||
jump('.target', {
|
||||
duration: 1000,
|
||||
offset: 0,
|
||||
callback: undefined,
|
||||
easing: easeInOutQuad,
|
||||
a11y: false
|
||||
})
|
||||
```
|
||||
|
||||
Explanation of each option follows:
|
||||
|
||||
* [target](#target)
|
||||
* [duration](#duration)
|
||||
* [offset](#offset)
|
||||
* [callback](#callback)
|
||||
* [easing](#easing)
|
||||
* [a11y](#a11y)
|
||||
|
||||
### target
|
||||
|
||||
Scroll _from the current position_ by passing a number of pixels.
|
||||
|
||||
```es6
|
||||
// scroll down 100px
|
||||
|
||||
jump(100)
|
||||
|
||||
// scroll up 100px
|
||||
|
||||
jump(-100)
|
||||
```
|
||||
|
||||
Or, scroll _to an element_, by passing either:
|
||||
|
||||
* a node, or
|
||||
* a CSS selector
|
||||
|
||||
```es6
|
||||
// passing a node
|
||||
|
||||
const node = document.querySelector('.target')
|
||||
|
||||
jump(node)
|
||||
|
||||
// passing a CSS selector
|
||||
// the element referenced by the selector is determined using document.querySelector
|
||||
|
||||
jump('.target')
|
||||
```
|
||||
|
||||
### duration
|
||||
|
||||
Pass the time the `jump()` takes, in milliseconds.
|
||||
|
||||
```es6
|
||||
jump('.target', {
|
||||
duration: 1000
|
||||
})
|
||||
```
|
||||
|
||||
Or, pass a function that returns the duration of the `jump()` in milliseconds. This function is passed the `jump()` `distance`, in `px`, as a parameter.
|
||||
|
||||
```es6
|
||||
jump('.target', {
|
||||
duration: distance => Math.abs(distance)
|
||||
})
|
||||
```
|
||||
|
||||
### offset
|
||||
|
||||
Offset a `jump()`, _only if to an element_, by a number of pixels.
|
||||
|
||||
```es6
|
||||
// stop 10px before the top of the element
|
||||
|
||||
jump('.target', {
|
||||
offset: -10
|
||||
})
|
||||
|
||||
// stop 10px after the top of the element
|
||||
|
||||
jump('.target', {
|
||||
offset: 10
|
||||
})
|
||||
```
|
||||
|
||||
Note that this option is useful for accommodating `position: fixed` elements.
|
||||
|
||||
### callback
|
||||
|
||||
Pass a function that will be called after the `jump()` has been completed.
|
||||
|
||||
```es6
|
||||
// in both regular and arrow functions, this === window
|
||||
|
||||
jump('.target', {
|
||||
callback: () => console.log('Jump completed!')
|
||||
})
|
||||
```
|
||||
|
||||
### easing
|
||||
|
||||
Easing function used to transition the `jump()`.
|
||||
|
||||
```es6
|
||||
jump('.target', {
|
||||
easing: easeInOutQuad
|
||||
})
|
||||
```
|
||||
|
||||
See [easing.js](https://github.com/callmecavs/jump.js/blob/master/src/easing.js) for the definition of `easeInOutQuad`, the default easing function. Credit for this function goes to Robert Penner.
|
||||
|
||||
### a11y
|
||||
|
||||
If enabled, _and scrolling to an element_:
|
||||
|
||||
* add a [`tabindex`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) to, and
|
||||
* [`focus`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) the element
|
||||
|
||||
```es6
|
||||
jump('.target', {
|
||||
a11y: true
|
||||
})
|
||||
```
|
||||
|
||||
Note that this option is disabled by default because it has _visual implications_ in many browsers. Focusing an element triggers the `:focus` CSS state selector, and is often accompanied by an `outline`.
|
||||
|
||||
## Browser Support
|
||||
|
||||
Jump depends on the following browser APIs:
|
||||
|
||||
* [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
|
||||
|
||||
Consequently, it supports the following natively:
|
||||
|
||||
* Chrome 24+
|
||||
* Firefox 23+
|
||||
* Safari 6.1+
|
||||
* Opera 15+
|
||||
* IE 10+
|
||||
* iOS Safari 7.1+
|
||||
* Android Browser 4.4+
|
||||
|
||||
To add support for older browsers, consider including polyfills/shims for the APIs listed above. There are no plans to include any in the library, in the interest of file size.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://opensource.org/licenses/MIT). © 2016 Michael Cavalea
|
||||
|
||||
[![Built With Love](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com)
|
|
@ -0,0 +1,11 @@
|
|||
// Robert Penner's easeInOutQuad
|
||||
|
||||
// find the rest of his easing functions here: http://robertpenner.com/easing/
|
||||
// find them exported for ES6 consumption here: https://github.com/jaxgeller/ez.js
|
||||
|
||||
export default (t, b, c, d) => {
|
||||
t /= d / 2
|
||||
if(t < 1) return c / 2 * t * t + b
|
||||
t--
|
||||
return -c / 2 * (t * (t - 2) - 1) + b
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
import easeInOutQuad from './easing.js'
|
||||
|
||||
const jumper = () => {
|
||||
// private variable cache
|
||||
// no variables are created during a jump, preventing memory leaks
|
||||
|
||||
let container // container element to be scrolled (node)
|
||||
let element // element to scroll to (node)
|
||||
|
||||
let start // where scroll starts (px)
|
||||
let stop // where scroll stops (px)
|
||||
|
||||
let offset // adjustment from the stop position (px)
|
||||
let easing // easing function (function)
|
||||
let a11y // accessibility support flag (boolean)
|
||||
|
||||
let distance // distance of scroll (px)
|
||||
let duration // scroll duration (ms)
|
||||
|
||||
let timeStart // time scroll started (ms)
|
||||
let timeElapsed // time spent scrolling thus far (ms)
|
||||
|
||||
let next // next scroll position (px)
|
||||
|
||||
let callback // to call when done scrolling (function)
|
||||
|
||||
let scrolling // true whilst scrolling (boolean)
|
||||
|
||||
// scroll position helper
|
||||
|
||||
function location() {
|
||||
return container.scrollY || container.pageYOffset || container.scrollTop
|
||||
}
|
||||
|
||||
// element offset helper
|
||||
|
||||
function top(element) {
|
||||
const elementTop = element.getBoundingClientRect().top
|
||||
const containerTop = container.getBoundingClientRect
|
||||
? container.getBoundingClientRect().top
|
||||
: 0
|
||||
|
||||
return elementTop - containerTop + start
|
||||
}
|
||||
|
||||
// scrollTo helper
|
||||
|
||||
function scrollTo(top) {
|
||||
container.scrollTo
|
||||
? container.scrollTo(0, top) // window
|
||||
: container.scrollTop = top // custom container
|
||||
}
|
||||
|
||||
// rAF loop helper
|
||||
|
||||
function loop(timeCurrent) {
|
||||
// store time scroll started, if not started already
|
||||
if(!timeStart) {
|
||||
timeStart = timeCurrent
|
||||
}
|
||||
|
||||
// determine time spent scrolling so far
|
||||
timeElapsed = timeCurrent - timeStart
|
||||
|
||||
// calculate next scroll position
|
||||
next = easing(timeElapsed, start, distance, duration)
|
||||
|
||||
// scroll to it
|
||||
scrollTo(next)
|
||||
|
||||
scrolling = true
|
||||
|
||||
// check progress
|
||||
timeElapsed < duration
|
||||
? requestAnimationFrame(loop) // continue scroll loop
|
||||
: done() // scrolling is done
|
||||
}
|
||||
|
||||
// scroll finished helper
|
||||
|
||||
function done() {
|
||||
// account for rAF time rounding inaccuracies
|
||||
scrollTo(start + distance)
|
||||
|
||||
// if scrolling to an element, and accessibility is enabled
|
||||
if(element && a11y) {
|
||||
// add tabindex indicating programmatic focus
|
||||
element.setAttribute('tabindex', '-1')
|
||||
|
||||
// focus the element
|
||||
element.focus()
|
||||
}
|
||||
|
||||
// if it exists, fire the callback
|
||||
if(typeof callback === 'function') {
|
||||
callback()
|
||||
}
|
||||
|
||||
// reset time for next jump
|
||||
timeStart = false
|
||||
|
||||
// we're done scrolling
|
||||
scrolling = false
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
function jump(target, options = {}) {
|
||||
// resolve options, or use defaults
|
||||
duration = options.duration || 1000
|
||||
offset = options.offset || 0
|
||||
callback = options.callback // "undefined" is a suitable default, and won't be called
|
||||
easing = options.easing || easeInOutQuad
|
||||
a11y = options.a11y || false
|
||||
|
||||
// resolve container
|
||||
switch(typeof options.container) {
|
||||
case 'object':
|
||||
// we assume container is an HTML element (Node)
|
||||
container = options.container
|
||||
break
|
||||
|
||||
case 'string':
|
||||
container = document.querySelector(options.container)
|
||||
break
|
||||
|
||||
default:
|
||||
container = window
|
||||
}
|
||||
|
||||
// cache starting position
|
||||
start = location()
|
||||
|
||||
// resolve target
|
||||
switch(typeof target) {
|
||||
// scroll from current position
|
||||
case 'number':
|
||||
element = undefined // no element to scroll to
|
||||
a11y = false // make sure accessibility is off
|
||||
stop = start + target
|
||||
break
|
||||
|
||||
// scroll to element (node)
|
||||
// bounding rect is relative to the viewport
|
||||
case 'object':
|
||||
element = target
|
||||
stop = top(element)
|
||||
break
|
||||
|
||||
// scroll to element (selector)
|
||||
// bounding rect is relative to the viewport
|
||||
case 'string':
|
||||
element = document.querySelector(target)
|
||||
stop = top(element)
|
||||
break
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
// resolve scroll distance, accounting for offset
|
||||
distance = stop - start + offset
|
||||
|
||||
// resolve duration
|
||||
switch(typeof options.duration) {
|
||||
// number in ms
|
||||
case 'number':
|
||||
duration = options.duration
|
||||
break
|
||||
|
||||
// function passed the distance of the scroll
|
||||
case 'function':
|
||||
duration = options.duration(distance)
|
||||
break
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
// start the loop if we're not already scrolling
|
||||
if (!scrolling) {
|
||||
requestAnimationFrame(loop)
|
||||
}
|
||||
else {
|
||||
// reset time for next jump
|
||||
timeStart = false
|
||||
}
|
||||
}
|
||||
|
||||
// expose only the jump method
|
||||
return jump
|
||||
}
|
||||
|
||||
// export singleton
|
||||
|
||||
const singleton = jumper()
|
||||
|
||||
export default singleton
|
|
@ -0,0 +1,115 @@
|
|||
import { createElement as h, Component } from 'react'
|
||||
import ace from '../assets/ace'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
*
|
||||
* <Ace value={"{}"}
|
||||
* ace={Object}
|
||||
* indentation={2}
|
||||
* onChange={function(value: String)}
|
||||
* onLoadAce={function(aceEditor, container)} />
|
||||
*
|
||||
*/
|
||||
export default class Ace extends Component {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
indentation: PropTypes.number
|
||||
}
|
||||
|
||||
aceEditor = null
|
||||
settingValue = false // Used to prevent Ace from emitting onChange event whilst we're setting a value programmatically
|
||||
|
||||
render () {
|
||||
return h('div', {ref: 'container', className: 'jsoneditor-code'})
|
||||
}
|
||||
|
||||
shouldComponentUpdate () {
|
||||
// always prevent rerendering, that would destroy the DOM of the Ace editor
|
||||
return false
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const container = this.refs.container
|
||||
|
||||
// use ace from bundle, and if not available
|
||||
// try to use from options or else from global
|
||||
const _ace = ace || this.props.ace || window['ace']
|
||||
|
||||
let aceEditor = null
|
||||
if (_ace && _ace.edit) {
|
||||
// create ace editor
|
||||
aceEditor = _ace.edit(container)
|
||||
|
||||
// bundle and load jsoneditor theme for ace editor
|
||||
require('../assets/ace/theme-jsoneditor')
|
||||
|
||||
// configure ace editor
|
||||
aceEditor.$blockScrolling = Infinity
|
||||
aceEditor.setTheme('ace/theme/jsoneditor')
|
||||
aceEditor.setShowPrintMargin(false)
|
||||
aceEditor.setFontSize(13)
|
||||
aceEditor.getSession().setMode('ace/mode/json')
|
||||
aceEditor.getSession().setTabSize(this.props.indentation || 2)
|
||||
aceEditor.getSession().setUseSoftTabs(true)
|
||||
aceEditor.getSession().setUseWrapMode(true)
|
||||
aceEditor.commands.bindKey('Ctrl-L', null) // disable Ctrl+L (is used by the browser to select the address bar)
|
||||
aceEditor.commands.bindKey('Command-L', null) // disable Ctrl+L (is used by the browser to select the address bar)
|
||||
}
|
||||
else {
|
||||
// ace is excluded from the bundle.
|
||||
}
|
||||
|
||||
// allow changing the config or completely replacing aceEditor
|
||||
this.aceEditor = this.props.onLoadAce
|
||||
? this.props.onLoadAce(aceEditor, container) || aceEditor
|
||||
: aceEditor
|
||||
|
||||
// register onchange event
|
||||
if (this.aceEditor) {
|
||||
this.aceEditor.on('change', this.handleChange)
|
||||
}
|
||||
|
||||
// set value, the text contents for the editor
|
||||
if (this.aceEditor) {
|
||||
this.aceEditor.setValue(this.props.value || '', -1)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (this.aceEditor && nextProps.value !== this.aceEditor.getValue()) {
|
||||
this.settingValue = true
|
||||
this.aceEditor.setValue(nextProps.value, -1)
|
||||
this.settingValue = false
|
||||
}
|
||||
|
||||
if (this.aceEditor &&
|
||||
(typeof nextProps.indentation === 'number' || typeof nextProps.indentation === 'string')) {
|
||||
this.aceEditor.getSession().setTabSize(this.props.indentation)
|
||||
}
|
||||
|
||||
// TODO: only resize only when needed
|
||||
setTimeout(() => {
|
||||
if (this.aceEditor) {
|
||||
this.aceEditor.resize(false);
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
// neatly destroy ace editor instance
|
||||
if (this.aceEditor) {
|
||||
this.aceEditor.destroy()
|
||||
this.aceEditor = null
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = () => {
|
||||
if (this.props && this.props.onChange && this.aceEditor && !this.settingValue) {
|
||||
// TODO: pass a diff
|
||||
this.props.onChange(this.aceEditor.getValue())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import { createElement as h } from 'react'
|
||||
import TextMode from './TextMode'
|
||||
import Ace from './Ace'
|
||||
|
||||
/**
|
||||
* CodeMode (powered by Ace editor)
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <CodeMode
|
||||
* options={Object}
|
||||
* onChange={function(text: string)}
|
||||
* onChangeMode={function(mode: string)}
|
||||
* onError={function(error: Error)}
|
||||
* onLoadAce={function(aceEditor: Object, container: Element) : Object}
|
||||
* />
|
||||
*
|
||||
* Methods:
|
||||
*
|
||||
* setText(text)
|
||||
* getText() : text
|
||||
* set(json : JSON)
|
||||
* get() : JSON
|
||||
* patch(actions: JSONPatch)
|
||||
* format()
|
||||
* compact()
|
||||
* destroy()
|
||||
*
|
||||
*/
|
||||
export default class CodeMode extends TextMode {
|
||||
|
||||
// TODO: work out propTypes
|
||||
|
||||
state = {
|
||||
text: '{}',
|
||||
compiledSchema: null
|
||||
}
|
||||
|
||||
render () {
|
||||
return h('div', {
|
||||
className: 'jsoneditor jsoneditor-mode-code',
|
||||
onKeyDown: this.handleKeyDown
|
||||
}, [
|
||||
this.renderMenu(),
|
||||
|
||||
h('div', {key: 'contents', className: 'jsoneditor-contents'}, h(Ace, {
|
||||
value: this.state.text,
|
||||
onChange: this.handleChangeText,
|
||||
onLoadAce: this.props.onLoadAce,
|
||||
indentation: this.props.indentation,
|
||||
ace: this.props.ace
|
||||
})),
|
||||
|
||||
this.renderSchemaErrors ()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: define propTypes
|
|
@ -0,0 +1,57 @@
|
|||
import { createElement as h, PureComponent } from 'react'
|
||||
import CodeMode from './CodeMode'
|
||||
import TextMode from './TextMode'
|
||||
import TreeMode from './TreeMode'
|
||||
|
||||
import './jsoneditor.css'
|
||||
|
||||
const DEFAULT_MODE = 'tree'
|
||||
|
||||
export default class JSONEditor extends PureComponent {
|
||||
|
||||
// TODO: work out prop types
|
||||
// static propTypes = {
|
||||
// ...
|
||||
// }
|
||||
|
||||
static modeConstructors = {
|
||||
code: CodeMode,
|
||||
form: TreeMode,
|
||||
text: TextMode,
|
||||
tree: TreeMode,
|
||||
view: TreeMode
|
||||
}
|
||||
|
||||
render () {
|
||||
const mode = this.props.mode || DEFAULT_MODE // We use mode from state, not from props!
|
||||
const ModeConstructor = JSONEditor.modeConstructors[mode]
|
||||
|
||||
if (!ModeConstructor) {
|
||||
// TODO: show an on screen error instead of throwing an error?
|
||||
throw new Error('Unknown mode "' + mode + '". ' +
|
||||
'Choose from: ' + Object.keys(this.props.modes).join(', ')) // FIXME: this.props.modes may be undefined
|
||||
}
|
||||
|
||||
return h(ModeConstructor, {
|
||||
...this.props,
|
||||
mode,
|
||||
onError: this.handleError,
|
||||
onChangeMode: this.handleChangeMode
|
||||
})
|
||||
}
|
||||
|
||||
handleError = (err) => {
|
||||
if (this.props.onError) {
|
||||
this.props.onError(err)
|
||||
}
|
||||
else {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
handleChangeMode = (mode) => {
|
||||
if (this.props.onChangeMode) {
|
||||
this.props.onChangeMode(mode, this.props.mode)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,697 @@
|
|||
import { createElement as h, PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import initial from 'lodash/initial'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import naturalSort from 'javascript-natural-sort'
|
||||
|
||||
import { escapeHTML, unescapeHTML } from '../utils/stringUtils'
|
||||
import { getInnerText, insideRect } from '../utils/domUtils'
|
||||
import { isUrl, stringConvert, valueType } from '../utils/typeUtils'
|
||||
import {
|
||||
ERROR,
|
||||
EXPANDED,
|
||||
ID,
|
||||
SEARCH_PROPERTY,
|
||||
SEARCH_VALUE,
|
||||
SELECTED_AFTER, SELECTED_BEFORE_CHILDS,
|
||||
SELECTED_INSIDE,
|
||||
SELECTION,
|
||||
TYPE,
|
||||
VALUE
|
||||
} from '../eson'
|
||||
import { compileJSONPointer } from '../jsonPointer'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
import faExclamationTriangle from '@fortawesome/fontawesome-free-solid/faExclamationTriangle'
|
||||
import faCaretRight from '@fortawesome/fontawesome-free-solid/faCaretRight'
|
||||
import faCaretDown from '@fortawesome/fontawesome-free-solid/faCaretDown'
|
||||
|
||||
fontawesome.library.add(faExclamationTriangle, faCaretRight, faCaretDown)
|
||||
|
||||
export default class JSONNode extends PureComponent {
|
||||
static URL_TITLE = 'Ctrl+Click or Ctrl+Enter to open url'
|
||||
|
||||
static propTypes = {
|
||||
parentPath: PropTypes.array,
|
||||
prop: PropTypes.string, // in case of an object property
|
||||
index: PropTypes.number, // in case of an array item
|
||||
eson: PropTypes.any, // enriched JSON object: Object, Array, number, string, or null
|
||||
|
||||
emit: PropTypes.func.isRequired,
|
||||
findKeyBinding: PropTypes.func.isRequired,
|
||||
|
||||
// options
|
||||
options: PropTypes.shape({
|
||||
isPropertyEditable: PropTypes.func,
|
||||
isValueEditable: PropTypes.func,
|
||||
escapeUnicode: PropTypes.bool
|
||||
})
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
path: null // initialized via getDerivedStateFromProps
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
const path = props.parentPath
|
||||
? props.parentPath.concat('index' in props ? props.index : props.prop)
|
||||
: []
|
||||
|
||||
// only update the path in the state if there is actually something changed,
|
||||
// else we get unnecessary re-rendering of all nodes
|
||||
return isEqual(path, state.path)
|
||||
? null
|
||||
: { path }
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (hoveredNode === this) {
|
||||
hoveredNode = null
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
if (this.props.eson[TYPE] === 'array') {
|
||||
return this.renderJSONArray()
|
||||
}
|
||||
else if (this.props.eson[TYPE] === 'object') {
|
||||
return this.renderJSONObject()
|
||||
}
|
||||
else { // no Object or Array
|
||||
return this.renderJSONValue()
|
||||
}
|
||||
}
|
||||
|
||||
renderJSONObject () {
|
||||
// TODO: refactor renderJSONObject (too large/complex)
|
||||
const eson = this.props.eson
|
||||
const jsonProps = Object.keys(eson).sort(naturalSort)
|
||||
const jsonPropsCount = jsonProps.length
|
||||
|
||||
const nodeStart = h('div', {
|
||||
key: 'node',
|
||||
onKeyDown: this.handleKeyDown,
|
||||
'data-area': 'inside',
|
||||
className: 'jsoneditor-node jsoneditor-object'
|
||||
}, [
|
||||
this.renderExpandButton(),
|
||||
// this.renderDelimiter('\u2610'),
|
||||
this.renderProperty(),
|
||||
this.renderSeparator(),
|
||||
this.renderDelimiter('{', 'jsoneditor-delimiter-start'),
|
||||
!this.props.eson[EXPANDED]
|
||||
? [
|
||||
this.renderTag(`${jsonPropsCount} ${jsonPropsCount === 1 ? 'prop' : 'props'}`,
|
||||
`Object containing ${jsonPropsCount} ${jsonPropsCount === 1 ? 'property' : 'properties'}`),
|
||||
this.renderDelimiter('}', 'jsoneditor-delimiter-end jsoneditor-delimiter-collapsed')
|
||||
]
|
||||
: null,
|
||||
this.renderError(this.props.eson[ERROR]),
|
||||
this.renderBeforeChilds()
|
||||
])
|
||||
|
||||
let childs
|
||||
if (this.props.eson[EXPANDED]) {
|
||||
if (jsonPropsCount > 0) {
|
||||
const propsChilds = jsonProps.map((prop) => h(this.constructor, {
|
||||
key: eson[prop][ID],
|
||||
parentPath: this.state.path,
|
||||
prop,
|
||||
eson: eson[prop],
|
||||
emit: this.props.emit,
|
||||
findKeyBinding: this.props.findKeyBinding,
|
||||
options: this.props.options
|
||||
}))
|
||||
|
||||
childs = h('div', {
|
||||
key: 'childs',
|
||||
'data-area': 'before',
|
||||
className: 'jsoneditor-list'
|
||||
}, propsChilds)
|
||||
}
|
||||
else {
|
||||
childs = h('div', {
|
||||
key: 'childs',
|
||||
className: 'jsoneditor-list',
|
||||
'data-area': 'before'
|
||||
},
|
||||
this.renderEmpty('(empty object)')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const nodeEnd = this.props.eson[EXPANDED]
|
||||
? h('div', {
|
||||
key: 'node-end',
|
||||
className: 'jsoneditor-node-end',
|
||||
'data-area': 'after',
|
||||
}, [
|
||||
this.renderDelimiter('}', 'jsoneditor-delimiter-end', 'after-childs')
|
||||
])
|
||||
: null
|
||||
|
||||
return h('div', {
|
||||
'data-path': compileJSONPointer(this.state.path),
|
||||
'data-area': this.props.eson[EXPANDED] ? 'before-childs' : 'after',
|
||||
className: this.getContainerClassName(this.props.eson[SELECTION]),
|
||||
// onMouseOver: this.handleMouseOver,
|
||||
// onMouseLeave: this.handleMouseLeave
|
||||
}, [nodeStart, childs, nodeEnd])
|
||||
}
|
||||
|
||||
renderJSONArray () {
|
||||
// TODO: refactor renderJSONArray (too large/complex)
|
||||
const count = this.props.eson.length
|
||||
const nodeStart = h('div', {
|
||||
key: 'node',
|
||||
onKeyDown: this.handleKeyDown,
|
||||
'data-area': 'inside',
|
||||
className: 'jsoneditor-node jsoneditor-array'
|
||||
}, [
|
||||
this.renderExpandButton(),
|
||||
this.renderProperty(),
|
||||
this.renderSeparator(),
|
||||
this.renderDelimiter('[', 'jsoneditor-delimiter-start'),
|
||||
!this.props.eson[EXPANDED]
|
||||
? [
|
||||
this.renderTag(`${count} ${count === 1 ? 'item' : 'items'}`,
|
||||
`Array containing ${count} ${count === 1 ? 'item' : 'items'}`),
|
||||
this.renderDelimiter(']', 'jsoneditor-delimiter-end jsoneditor-delimiter-collapsed'),
|
||||
]
|
||||
: null,
|
||||
this.renderError(this.props.eson[ERROR]),
|
||||
this.renderBeforeChilds()
|
||||
])
|
||||
|
||||
let childs
|
||||
if (this.props.eson[EXPANDED]) {
|
||||
if (count > 0) {
|
||||
const items = this.props.eson.map((item, index) => h(this.constructor, {
|
||||
key: item[ID],
|
||||
parentPath: this.state.path,
|
||||
index,
|
||||
eson: item,
|
||||
options: this.props.options,
|
||||
emit: this.props.emit,
|
||||
findKeyBinding: this.props.findKeyBinding
|
||||
}))
|
||||
|
||||
childs = h('div', {
|
||||
key: 'childs',
|
||||
'data-area': 'before',
|
||||
className: 'jsoneditor-list'
|
||||
}, items)
|
||||
}
|
||||
else {
|
||||
childs = h('div', {
|
||||
key: 'childs',
|
||||
className: 'jsoneditor-list',
|
||||
'data-area': 'before',
|
||||
},
|
||||
this.renderEmpty('(empty array)')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const nodeEnd = this.props.eson[EXPANDED]
|
||||
? h('div', {
|
||||
key: 'node-end',
|
||||
className: 'jsoneditor-node-end',
|
||||
'data-area': 'after'
|
||||
}, [
|
||||
this.renderDelimiter(']', 'jsoneditor-delimiter-end', 'after-childs')
|
||||
])
|
||||
: null
|
||||
|
||||
return h('div', {
|
||||
'data-path': compileJSONPointer(this.state.path),
|
||||
'data-area': this.props.eson[EXPANDED] ? 'before-childs' : 'after',
|
||||
className: this.getContainerClassName(this.props.eson[SELECTION]),
|
||||
// onMouseOver: this.handleMouseOver,
|
||||
// onMouseLeave: this.handleMouseLeave
|
||||
}, [nodeStart, childs, nodeEnd])
|
||||
}
|
||||
|
||||
renderJSONValue () {
|
||||
const node = h('div', {
|
||||
key: 'node',
|
||||
onKeyDown: this.handleKeyDown,
|
||||
'data-area': 'inside',
|
||||
className: 'jsoneditor-node'
|
||||
}, [
|
||||
this.renderPlaceholder(),
|
||||
this.renderProperty(),
|
||||
this.renderSeparator(),
|
||||
this.renderValue(this.props.eson[VALUE], this.props.eson[SEARCH_VALUE], this.props.options), // FIXME
|
||||
this.renderError(this.props.eson[ERROR])
|
||||
])
|
||||
|
||||
// const insertArea = this.renderInsertBeforeArea()
|
||||
|
||||
return h('div', {
|
||||
'data-path': compileJSONPointer(this.state.path),
|
||||
'data-area': 'after',
|
||||
className: this.getContainerClassName(this.props.eson[SELECTION]),
|
||||
// onMouseOver: this.handleMouseOver,
|
||||
// onMouseLeave: this.handleMouseLeave
|
||||
}, [node])
|
||||
}
|
||||
|
||||
/**
|
||||
* Render contents for an empty object or array
|
||||
* @param {string} text
|
||||
* @return {*}
|
||||
*/
|
||||
renderEmpty (text) {
|
||||
return h('div', {
|
||||
'data-path': compileJSONPointer(this.state.path) + '/-',
|
||||
'data-area': 'after',
|
||||
className: 'jsoneditor-node-container'
|
||||
}, h('div', {
|
||||
className: 'jsoneditor-node',
|
||||
'data-area': 'inside',
|
||||
onKeyDown: this.handleKeyDownAppend
|
||||
}, [
|
||||
this.renderPlaceholder(),
|
||||
this.renderReadonly(text)
|
||||
]))
|
||||
}
|
||||
|
||||
renderPlaceholder () {
|
||||
return h('div', {
|
||||
key: 'placeholder',
|
||||
'data-area': 'before',
|
||||
className: 'jsoneditor-button-placeholder'
|
||||
})
|
||||
}
|
||||
|
||||
renderReadonly (text, title = null) {
|
||||
return h('div', {
|
||||
key: 'readonly',
|
||||
className: 'jsoneditor-readonly',
|
||||
title
|
||||
}, text)
|
||||
}
|
||||
|
||||
renderTag (text, title = null) {
|
||||
return h('div', {
|
||||
key: 'readonly',
|
||||
className: 'jsoneditor-tag',
|
||||
onClick: this.handleExpand,
|
||||
title
|
||||
}, text)
|
||||
}
|
||||
|
||||
// TODO: simplify the method renderProperty
|
||||
|
||||
/**
|
||||
* Render a property field of a JSONNode
|
||||
*/
|
||||
renderProperty () {
|
||||
const isProp = typeof this.props.prop === 'string'
|
||||
|
||||
if (!isProp) {
|
||||
return null
|
||||
}
|
||||
|
||||
const editable = !this.props.options.isPropertyEditable ||
|
||||
this.props.options.isPropertyEditable(this.state.path)
|
||||
|
||||
const emptyClassName = (this.props.prop != null && this.props.prop.length === 0) ? ' jsoneditor-empty' : ''
|
||||
const searchClassName = this.props.prop != null ? JSONNode.getSearchResultClass(this.props.eson[SEARCH_PROPERTY]) : ''
|
||||
const escapedPropName = this.props.prop != null ? escapeHTML(this.props.prop, this.props.options.escapeUnicode) : null
|
||||
|
||||
if (editable) {
|
||||
return [
|
||||
h('div', {
|
||||
key: 'property',
|
||||
className: 'jsoneditor-property' + emptyClassName + searchClassName,
|
||||
'data-input': 'property',
|
||||
contentEditable: 'true',
|
||||
suppressContentEditableWarning: true,
|
||||
spellCheck: 'false',
|
||||
onBlur: this.handleChangeProperty
|
||||
}, escapedPropName),
|
||||
]
|
||||
}
|
||||
else {
|
||||
return h('div', {
|
||||
key: 'property',
|
||||
className: 'jsoneditor-property jsoneditor-readonly' + searchClassName,
|
||||
spellCheck: 'false'
|
||||
}, escapedPropName)
|
||||
}
|
||||
}
|
||||
|
||||
renderBeforeChilds () {
|
||||
return h('div', {
|
||||
key: 'before-childs',
|
||||
className: 'jsoneditor-before-childs',
|
||||
'data-area': 'before-childs'
|
||||
})
|
||||
}
|
||||
|
||||
renderSeparator() {
|
||||
const isProp = typeof this.props.prop === 'string'
|
||||
if (!isProp) {
|
||||
return null
|
||||
}
|
||||
|
||||
return h('div', {
|
||||
key: 'separator',
|
||||
className: 'jsoneditor-delimiter',
|
||||
}, ':')
|
||||
}
|
||||
|
||||
renderDelimiter (text, className = '', dataArea) {
|
||||
return h('div', {
|
||||
key: text,
|
||||
'data-area': dataArea,
|
||||
className: 'jsoneditor-delimiter ' + className
|
||||
}, text)
|
||||
}
|
||||
|
||||
renderValue (value, searchResult, options) {
|
||||
const escapedValue = escapeHTML(value, options.escapeUnicode)
|
||||
const type = valueType (value)
|
||||
const itsAnUrl = isUrl(value)
|
||||
const isEmpty = escapedValue.length === 0
|
||||
|
||||
const editable = !options.isValueEditable || options.isValueEditable(this.state.path)
|
||||
if (editable) {
|
||||
return h('div', {
|
||||
key: 'value',
|
||||
className: JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
|
||||
JSONNode.getSearchResultClass(searchResult),
|
||||
'data-input': 'value',
|
||||
contentEditable: 'true',
|
||||
suppressContentEditableWarning: true,
|
||||
spellCheck: 'false',
|
||||
onBlur: this.handleChangeValue,
|
||||
onInput: this.updateValueStyling,
|
||||
onClick: this.handleClickValue,
|
||||
onKeyDown: this.handleKeyDownValue,
|
||||
title: itsAnUrl ? JSONNode.URL_TITLE : null
|
||||
}, escapedValue)
|
||||
}
|
||||
else {
|
||||
return h('div', {
|
||||
key: 'value',
|
||||
className: 'jsoneditor-readonly',
|
||||
title: itsAnUrl ? JSONNode.URL_TITLE : null
|
||||
}, escapedValue)
|
||||
}
|
||||
}
|
||||
|
||||
renderError (error) {
|
||||
if (error) {
|
||||
return h('button', {
|
||||
key: 'error',
|
||||
type: 'button',
|
||||
className: 'jsoneditor-schema-error',
|
||||
onFocus: this.updatePopoverDirection,
|
||||
onMouseOver: this.updatePopoverDirection
|
||||
},
|
||||
[
|
||||
h('i', {className: 'fa fa-exclamation-triangle', key: 'icon'}),
|
||||
h('div', {className: 'jsoneditor-popover jsoneditor-right', 'key': 'message'}, error.message)
|
||||
]
|
||||
)
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
getContainerClassName (selected) {
|
||||
let classNames = [
|
||||
'jsoneditor-node-container',
|
||||
// `jsoneditor-node-${this.props.eson[TYPE]}`
|
||||
this.props.eson[EXPANDED] ? 'jsoneditor-node-expanded' : 'jsoneditor-node-collapsed'
|
||||
]
|
||||
|
||||
if (selected === SELECTED_INSIDE) {
|
||||
classNames.push('jsoneditor-selected')
|
||||
}
|
||||
|
||||
if (selected === SELECTED_AFTER) {
|
||||
classNames.push('jsoneditor-selected-after')
|
||||
}
|
||||
|
||||
if (selected === SELECTED_BEFORE_CHILDS) {
|
||||
classNames.push('jsoneditor-selected-before-childs')
|
||||
}
|
||||
|
||||
return classNames.join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the best position for the popover: right, above, below, or left
|
||||
* from the warning icon.
|
||||
* @param event
|
||||
*/
|
||||
updatePopoverDirection = (event) => {
|
||||
if (event.target.nodeName === 'BUTTON') {
|
||||
const popover = event.target.lastChild
|
||||
|
||||
const directions = ['right', 'above', 'below', 'left']
|
||||
for (let i = 0; i < directions.length; i++) {
|
||||
const direction = directions[i]
|
||||
popover.className = 'jsoneditor-popover jsoneditor-' + direction
|
||||
|
||||
const contents = event.target.parentNode.parentNode.parentNode
|
||||
const contentRect = contents.getBoundingClientRect()
|
||||
const popoverRect = popover.getBoundingClientRect()
|
||||
const margin = 20 // account for a scroll bar
|
||||
|
||||
if (insideRect(contentRect, popoverRect, margin)) {
|
||||
// we found a location that fits, stop here
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this function manipulates the className and title of the editable div
|
||||
* outside of Preact, so the user gets immediate feedback
|
||||
* @param event
|
||||
*/
|
||||
updateValueStyling = (event) => {
|
||||
const value = this.getValueFromEvent(event)
|
||||
const type = valueType (value)
|
||||
const itsAnUrl = isUrl(value)
|
||||
const isEmpty = false // not needed, our div has a border and is clearly visible
|
||||
|
||||
// find the editable div, the root
|
||||
let target = event.target
|
||||
while (target.contentEditable !== 'true') {
|
||||
target = target.parentNode
|
||||
}
|
||||
|
||||
target.className = JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
|
||||
JSONNode.getSearchResultClass(this.props.eson[SEARCH_VALUE])
|
||||
target.title = itsAnUrl ? JSONNode.URL_TITLE : ''
|
||||
|
||||
// remove all classNames from childs (needed for IE and Edge)
|
||||
JSONNode.removeChildClasses(target)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the className for the property value
|
||||
* @param {string} type
|
||||
* @param {boolean} isUrl
|
||||
* @param {boolean} isEmpty
|
||||
* @return {string}
|
||||
* @public
|
||||
*/
|
||||
static getValueClass (type, isUrl, isEmpty) {
|
||||
return 'jsoneditor-value ' +
|
||||
'jsoneditor-' + type +
|
||||
(isUrl ? ' jsoneditor-url' : '') +
|
||||
(isEmpty ? ' jsoneditor-empty' : '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the css style given a search result type
|
||||
* @param {SearchResultStatus} [searchResultStatus]
|
||||
*/
|
||||
static getSearchResultClass (searchResultStatus) {
|
||||
if (searchResultStatus === 'active') {
|
||||
return ' jsoneditor-highlight-active'
|
||||
}
|
||||
|
||||
if (searchResultStatus === 'normal') {
|
||||
return ' jsoneditor-highlight'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively remove all classes from the childs of this element
|
||||
* @param elem
|
||||
* @public
|
||||
*/
|
||||
static removeChildClasses (elem) {
|
||||
for (let i = 0; i < elem.childNodes.length; i++) {
|
||||
const child = elem.childNodes[i]
|
||||
if (child.class) {
|
||||
child.class = ''
|
||||
}
|
||||
JSONNode.removeChildClasses(child)
|
||||
}
|
||||
}
|
||||
|
||||
renderExpandButton () {
|
||||
const expanded = this.props.eson[EXPANDED]
|
||||
const className = `jsoneditor-button jsoneditor-${expanded ? 'expanded' : 'collapsed'}`
|
||||
|
||||
|
||||
return h('div', {key: 'expand', className: 'jsoneditor-button-container'},
|
||||
h('button', {
|
||||
'data-area': 'before',
|
||||
className: className,
|
||||
onClick: this.handleExpand,
|
||||
title:
|
||||
'Click to expand/collapse this field. \n' +
|
||||
'Ctrl+Click to expand/collapse including all childs.'
|
||||
}, h('span', {
|
||||
key: expanded, // to force the fontawesome icon to update
|
||||
}, h('i', {
|
||||
className: expanded ? 'fa fa-caret-down' : 'fa fa-caret-right'
|
||||
})))
|
||||
)
|
||||
}
|
||||
|
||||
/** @private */
|
||||
handleChangeProperty = (event) => {
|
||||
const parentPath = initial(this.state.path)
|
||||
const oldProp = this.props.prop
|
||||
const newProp = unescapeHTML(getInnerText(event.target))
|
||||
|
||||
if (newProp !== oldProp) {
|
||||
this.props.emit('changeProperty', {parentPath, oldProp, newProp})
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
handleChangeValue = (event) => {
|
||||
const value = this.getValueFromEvent(event)
|
||||
const path = this.state.path
|
||||
|
||||
if (value !== this.props.eson[VALUE]) {
|
||||
this.props.emit('changeValue', {path, value})
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
handleClickValue = (event) => {
|
||||
if (event.ctrlKey && event.button === 0) { // Ctrl+Left click
|
||||
this.openLinkIfUrl(event)
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
handleKeyDown = (event) => {
|
||||
const keyBinding = this.props.findKeyBinding(event)
|
||||
const path = this.state.path
|
||||
|
||||
if (keyBinding === 'duplicate') {
|
||||
event.preventDefault()
|
||||
this.props.emit('duplicate', {path})
|
||||
}
|
||||
|
||||
if (keyBinding === 'insert') {
|
||||
event.preventDefault()
|
||||
this.props.emit('insert', {path, type: 'value'})
|
||||
}
|
||||
|
||||
if (keyBinding === 'remove') {
|
||||
event.preventDefault()
|
||||
this.props.emit('remove', {path})
|
||||
}
|
||||
|
||||
if (keyBinding === 'expand') {
|
||||
event.preventDefault()
|
||||
const recurse = false
|
||||
const expanded = !this.props.eson[EXPANDED]
|
||||
this.props.emit('expand', {path, expanded, recurse})
|
||||
}
|
||||
|
||||
if (keyBinding === 'actionMenu') {
|
||||
event.preventDefault()
|
||||
// FIXME: open floating menu
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
handleKeyDownAppend = (event) => {
|
||||
const keyBinding = this.props.findKeyBinding(event)
|
||||
const path = this.state.path
|
||||
|
||||
if (keyBinding === 'insert') {
|
||||
event.preventDefault()
|
||||
this.props.emit('append', {path, type: 'value'})
|
||||
}
|
||||
|
||||
if (keyBinding === 'actionMenu') {
|
||||
event.preventDefault()
|
||||
// FIXME: open floating menu
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
handleKeyDownValue = (event) => {
|
||||
const keyBinding = this.props.findKeyBinding(event)
|
||||
|
||||
if (keyBinding === 'openUrl') {
|
||||
this.openLinkIfUrl(event)
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
handleExpand = (event) => {
|
||||
const recurse = event.ctrlKey
|
||||
const path = this.state.path
|
||||
const expanded = !this.props.eson[EXPANDED]
|
||||
|
||||
this.props.emit('expand', {path, expanded, recurse})
|
||||
}
|
||||
|
||||
/**
|
||||
* When this JSONNode holds an URL as value, open this URL in a new browser tab
|
||||
* @param event
|
||||
* @protected
|
||||
*/
|
||||
openLinkIfUrl (event) {
|
||||
const value = this.getValueFromEvent(event)
|
||||
|
||||
if (isUrl(value)) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
window.open(value, '_blank')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the target of an event, and convert it to it's type
|
||||
* @param event
|
||||
* @return {string | number | boolean | null}
|
||||
* @private
|
||||
*/
|
||||
getValueFromEvent (event) {
|
||||
const stringValue = unescapeHTML(getInnerText(event.target))
|
||||
return this.state.type === 'string' // FIXME
|
||||
? stringValue
|
||||
: stringConvert(stringValue)
|
||||
}
|
||||
}
|
||||
|
||||
// singleton holding the node that's currently being hovered
|
||||
let hoveredNode = null
|
|
@ -0,0 +1,36 @@
|
|||
import JSONNode from './JSONNode'
|
||||
|
||||
/**
|
||||
* JSONNodeForm
|
||||
*
|
||||
* Creates JSONNodes without action menus and with readonly properties
|
||||
*/
|
||||
export default class JSONNodeForm extends JSONNode {
|
||||
|
||||
// render no action menu...
|
||||
renderActionMenuButton () {
|
||||
return null
|
||||
}
|
||||
|
||||
// render no append menu...
|
||||
renderAppendMenuButton () {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a property field of a JSONNode
|
||||
* @param {string} [prop]
|
||||
* @param {string} [index]
|
||||
* @param {ESON} eson
|
||||
* @param {{escapeUnicode: boolean, isPropertyEditable: function(Path) : boolean}} options
|
||||
*/
|
||||
renderProperty (prop, index, eson, options) {
|
||||
const formOptions = Object.assign({}, options, { isPropertyEditable })
|
||||
|
||||
return JSONNode.prototype.renderProperty.call(this, prop, index, eson, formOptions)
|
||||
}
|
||||
}
|
||||
|
||||
function isPropertyEditable () {
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import { createElement as h } from 'react'
|
||||
|
||||
import { escapeHTML } from '../utils/stringUtils'
|
||||
import { valueType, isUrl } from '../utils/typeUtils'
|
||||
import JSONNode from './JSONNode'
|
||||
import JSONNodeForm from './JSONNodeForm'
|
||||
|
||||
/**
|
||||
* JSONNodeView
|
||||
*
|
||||
* Creates JSONNodes without action menus and with readonly properties and values
|
||||
*/
|
||||
export default class JSONNodeView extends JSONNodeForm {
|
||||
|
||||
// render a readonly value but with colors
|
||||
renderValue (value, searchResult, options) {
|
||||
const escapedValue = escapeHTML(value, options.escapeUnicode)
|
||||
const type = valueType (value)
|
||||
const itsAnUrl = isUrl(value)
|
||||
const isEmpty = escapedValue.length === 0
|
||||
|
||||
const editable = !options.isValueEditable || options.isValueEditable(this.props.path)
|
||||
if (editable) {
|
||||
return h('div', {
|
||||
key: 'value',
|
||||
ref: 'value',
|
||||
className: JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
|
||||
JSONNode.getSearchResultClass(searchResult),
|
||||
contentEditable: 'false',
|
||||
spellCheck: 'false',
|
||||
onClick: this.handleClickValue,
|
||||
title: itsAnUrl ? JSONNode.URL_TITLE : null
|
||||
}, escapedValue)
|
||||
}
|
||||
else {
|
||||
return h('div', {
|
||||
key: 'value',
|
||||
className: 'jsoneditor-readonly',
|
||||
title: itsAnUrl ? JSONNode.URL_TITLE : null
|
||||
}, escapedValue)
|
||||
}
|
||||
}
|
||||
|
||||
handleClickValue = (event) => {
|
||||
if (event.button === 0) { // Left click
|
||||
this.openLinkIfUrl(event)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,376 @@
|
|||
import { Component, createElement as h } from 'react'
|
||||
import Ajv from 'ajv'
|
||||
import { parseJSON } from '../utils/jsonUtils'
|
||||
import { escapeUnicodeChars } from '../utils/stringUtils'
|
||||
import { enrichSchemaError, limitErrors } from '../utils/schemaUtils'
|
||||
import { createFindKeyBinding } from '../utils/keyBindings'
|
||||
import { KEY_BINDINGS } from '../constants'
|
||||
import { immutableJSONPatch } from '../immutableJSONPatch'
|
||||
import TextModeMenu from './menu/TextModeMenu'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
import faExclamationTriangle from '@fortawesome/fontawesome-free-solid/faExclamationTriangle'
|
||||
|
||||
fontawesome.library.add(faExclamationTriangle)
|
||||
|
||||
const AJV_OPTIONS = {
|
||||
allErrors: true,
|
||||
verbose: true,
|
||||
jsonPointers: true
|
||||
}
|
||||
|
||||
/**
|
||||
* TextMode
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <TextMode
|
||||
* text={string}
|
||||
* json={JSON}
|
||||
* ...options
|
||||
* onChange={function(text: string)}
|
||||
* onChangeMode={function(mode: string)}
|
||||
* onError={function(error: Error)}
|
||||
* />
|
||||
*
|
||||
* Methods:
|
||||
*
|
||||
* setText(text)
|
||||
* getText() : text
|
||||
* set(json : JSON)
|
||||
* get() : JSON
|
||||
* patch(actions: JSONPatch)
|
||||
* format()
|
||||
* compact()
|
||||
* destroy()
|
||||
*
|
||||
*/
|
||||
export default class TextMode extends Component {
|
||||
|
||||
state = {
|
||||
text: '{}',
|
||||
compiledSchema: null
|
||||
}
|
||||
|
||||
keyDownActions = {
|
||||
'format': (event) => this.handleCompact(),
|
||||
'compact': (event) => this.handleFormat()
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.applyProps(this.props, {})
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
this.applyProps(nextProps, this.props)
|
||||
}
|
||||
|
||||
// TODO: create some sort of watcher structure for these props? Is there a React pattern for that?
|
||||
applyProps (nextProps, currentProps) {
|
||||
// Apply text
|
||||
if (nextProps.text !== currentProps.text) {
|
||||
this.setText(nextProps.text)
|
||||
}
|
||||
|
||||
// Apply json
|
||||
if (nextProps.json !== currentProps.json) {
|
||||
this.set(nextProps.json)
|
||||
}
|
||||
|
||||
// Apply JSON Schema
|
||||
if (nextProps.schema !== currentProps.schema) {
|
||||
this.setSchema(nextProps.schema)
|
||||
}
|
||||
|
||||
// Apply key bindings
|
||||
if (!this.findKeyBinding ||
|
||||
JSON.stringify(nextProps.keyBindings) !== JSON.stringify(currentProps.keyBindings)) {
|
||||
// merge default and custom key bindings
|
||||
const keyBindings = Object.assign({}, KEY_BINDINGS, nextProps.keyBindings)
|
||||
this.findKeyBinding = createFindKeyBinding(keyBindings)
|
||||
}
|
||||
|
||||
// TODO: apply patch
|
||||
// TODO: apply patchText
|
||||
}
|
||||
|
||||
render () {
|
||||
return h('div', {
|
||||
className: 'jsoneditor jsoneditor-mode-text',
|
||||
onKeyDown: this.handleKeyDown
|
||||
}, [
|
||||
this.renderMenu(),
|
||||
|
||||
h('div', {key: 'contents', className: 'jsoneditor-contents'},
|
||||
h('textarea', {
|
||||
className: 'jsoneditor-text',
|
||||
value: this.state.text,
|
||||
onChange: this.handleChange,
|
||||
onInput: this.handleInput
|
||||
})
|
||||
),
|
||||
|
||||
this.renderSchemaErrors ()
|
||||
])
|
||||
}
|
||||
|
||||
/** @protected */
|
||||
renderMenu () {
|
||||
return h(TextModeMenu, {
|
||||
key: 'menu',
|
||||
|
||||
mode: this.props.mode,
|
||||
modes: this.props.modes,
|
||||
onChangeMode: this.props.onChangeMode,
|
||||
|
||||
onFormat: this.handleFormat,
|
||||
onCompact: this.handleCompact,
|
||||
onRepair: this.handleRepair
|
||||
})
|
||||
}
|
||||
|
||||
/** @protected */
|
||||
renderSchemaErrors () {
|
||||
// TODO: move the JSON Schema stuff into a separate Component
|
||||
|
||||
try {
|
||||
// TODO: only validate again when json is changed since last validation
|
||||
const json = this.get(); // this can fail when there is no valid json
|
||||
const valid = this.state.compiledSchema
|
||||
? this.state.compiledSchema(json)
|
||||
: true
|
||||
|
||||
if (!valid) {
|
||||
const allErrors = this.state.compiledSchema.errors.map(enrichSchemaError)
|
||||
const limitedErrors = limitErrors(allErrors)
|
||||
|
||||
return h('div', { key: 'errors', className: 'jsoneditor-errors'},
|
||||
h('table', {},
|
||||
h('tbody', {}, limitedErrors.map(TextMode.renderSchemaError))
|
||||
)
|
||||
)
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
// no valid JSON
|
||||
// TODO: display errors in text mode somehow? shouldn't be too much in your face
|
||||
// maybe a warning icon top right?
|
||||
// return h('table', {className: 'jsoneditor-text-errors'},
|
||||
// h('tbody', {}, TextMode.renderSchemaError(err))
|
||||
// )
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a table row of a single JSON schema error
|
||||
* @param {Error | Object | string} error
|
||||
* @param {number} index
|
||||
* @return {JSX.Element}
|
||||
*/
|
||||
static renderSchemaError (error, index) {
|
||||
const icon = h('button', {className: 'jsoneditor-schema-error'},
|
||||
h('i', {className: 'fa fa-exclamation-triangle'}))
|
||||
|
||||
if (error && error.schema && error.schemaPath) {
|
||||
// this is an ajv error message
|
||||
return h('tr', { key: index }, [
|
||||
h('td', {key: 'icon'}, icon),
|
||||
h('td', {key: 'path'}, error.esonPath),
|
||||
h('td', {key: 'message'}, error.message)
|
||||
])
|
||||
}
|
||||
else {
|
||||
// any other error message
|
||||
console.log('error???', error)
|
||||
return h('tr', { key: index },
|
||||
h('td', {key: 'icon'}, icon),
|
||||
h('td', {key: 'message', colSpan: 2}, h('code', {}, String(error)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a JSON schema for validation of the JSON object.
|
||||
* To remove the schema, call JSONEditor.setSchema(null)
|
||||
* @param {Object | null} schema
|
||||
*/
|
||||
setSchema (schema) {
|
||||
if (schema) {
|
||||
const ajv = this.props.ajv || (Ajv && Ajv(AJV_OPTIONS))
|
||||
|
||||
if (!ajv) {
|
||||
throw new Error('Cannot validate JSON: ajv not available. ' +
|
||||
'Provide ajv via options or use a JSONEditor bundle including ajv.')
|
||||
}
|
||||
|
||||
this.setState({
|
||||
compiledSchema: ajv.compile(schema)
|
||||
})
|
||||
}
|
||||
else {
|
||||
this.setState({
|
||||
compiledSchema: null
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured indentation. When not configured, returns the default value 2
|
||||
* @param {{indentation?: number}} props
|
||||
* @return {number}
|
||||
*/
|
||||
static getIndentation (props) {
|
||||
return (props && props.indentation) || 2
|
||||
}
|
||||
|
||||
static format (text, indentation) {
|
||||
const json = parseJSON(text)
|
||||
return JSON.stringify(json, null, indentation)
|
||||
}
|
||||
|
||||
static compact (text) {
|
||||
const json = parseJSON(text)
|
||||
return JSON.stringify(json)
|
||||
}
|
||||
|
||||
// TODO: move the static functions above into a separate util file
|
||||
|
||||
handleChange = (event) => {
|
||||
// do nothing...
|
||||
}
|
||||
|
||||
findKeyBinding = createFindKeyBinding(KEY_BINDINGS)
|
||||
|
||||
handleKeyDown = (event) => {
|
||||
const keyBinding = this.findKeyBinding(event)
|
||||
const action = this.keyDownActions[keyBinding]
|
||||
|
||||
if (action) {
|
||||
event.preventDefault()
|
||||
action(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle changed text input in the textarea
|
||||
* @param {Event} event
|
||||
* @protected
|
||||
*/
|
||||
handleInput = (event) => {
|
||||
this.handleChangeText(event.target.value)
|
||||
}
|
||||
|
||||
/** @protected */
|
||||
handleFormat = () => {
|
||||
try {
|
||||
const formatted = TextMode.format(this.getText(), TextMode.getIndentation(this.props))
|
||||
this.handleChangeText(formatted)
|
||||
}
|
||||
catch (err) {
|
||||
this.props.onError(err)
|
||||
}
|
||||
}
|
||||
|
||||
/** @protected */
|
||||
handleCompact = () => {
|
||||
try {
|
||||
const compacted = TextMode.compact(this.getText())
|
||||
this.handleChangeText(compacted)
|
||||
}
|
||||
catch (err) {
|
||||
this.props.onError(err)
|
||||
}
|
||||
}
|
||||
|
||||
/** @protected */
|
||||
handleRepair = () => {
|
||||
// FIXME: implement repair button
|
||||
console.log('handleRepair not yet implemented')
|
||||
alert('sorry, not yet implemented...')
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply new text to the state, and emit an onChangeText event if there is a change
|
||||
* @param {string} text
|
||||
*/
|
||||
handleChangeText = (text) => {
|
||||
if (this.props.onChangeText && text !== this.state.text) {
|
||||
const appliedText = this.setText(text)
|
||||
this.props.onChangeText(appliedText)
|
||||
}
|
||||
else {
|
||||
this.setText(text)
|
||||
}
|
||||
|
||||
// TODO: also invoke a patch action
|
||||
}
|
||||
|
||||
// TODO: implement method patchText
|
||||
// TODO: implement callback onPatchText
|
||||
|
||||
/**
|
||||
* Apply a JSONPatch to the current JSON document
|
||||
* @param {JSONPatchDocument} operations JSONPatch operations
|
||||
* @return {JSONPatchResult} Returns a patch result containing the
|
||||
* patch, a patch to revert the action, and
|
||||
* an error object which is null when successful
|
||||
*/
|
||||
patch (operations) {
|
||||
const json = this.get()
|
||||
|
||||
const result = immutableJSONPatch(json, operations)
|
||||
|
||||
this.set(result.data)
|
||||
|
||||
return {
|
||||
patch: operations,
|
||||
revert: result.revert,
|
||||
error: result.error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set JSON object in editor
|
||||
* @param {Object | Array | string | number | boolean | null} json JSON data
|
||||
*/
|
||||
set (json) {
|
||||
this.setText(JSON.stringify(json, null, TextMode.getIndentation(this.props)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JSON from the editor
|
||||
* @returns {Object | Array | string | number | boolean | null} json
|
||||
*/
|
||||
get () {
|
||||
return parseJSON(this.getText())
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a string containing a JSON document
|
||||
* @param {string} text
|
||||
* @return {string}
|
||||
*/
|
||||
setText (text) {
|
||||
const normalizedText = this.props.escapeUnicode
|
||||
? escapeUnicodeChars(text)
|
||||
: text
|
||||
|
||||
this.setState({ text: normalizedText })
|
||||
|
||||
return normalizedText
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON document as text
|
||||
* @return {string} text
|
||||
*/
|
||||
getText () {
|
||||
return this.state.text
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: define propTypes
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
|||
// custom fontawesome icons
|
||||
|
||||
export const faCompact = {
|
||||
prefix: 'fa',
|
||||
iconName: 'compact',
|
||||
icon: [
|
||||
16, 16,
|
||||
[],
|
||||
null,
|
||||
'm 0,2 0,2 16,0 0,-2 m -16,4 0,2 16,0 0,-2 m -16,4 0,2 9,0 0,-2'
|
||||
]
|
||||
}
|
||||
|
||||
export const faFormat = {
|
||||
prefix: 'fa',
|
||||
iconName: 'format',
|
||||
icon: [
|
||||
16, 16,
|
||||
[],
|
||||
null,
|
||||
'm 0,2 0,2 12,0 0,-2 m -6,4 0,2 10,0 0,-2 m -10,4 0,2 9,0 0,-2 m -15,4 0,2 10,0 0,-2'
|
||||
]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue