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 |
17
.babelrc
17
.babelrc
|
@ -1,5 +1,20 @@
|
|||
{
|
||||
"plugins": [
|
||||
"transform-class-properties",
|
||||
"transform-object-rest-spread",
|
||||
"transform-react-jsx",
|
||||
["transform-runtime", {
|
||||
"polyfill": false,
|
||||
"regenerator": true
|
||||
}]
|
||||
],
|
||||
"presets": [
|
||||
["@babel/preset-env"]
|
||||
["env", {
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "ie >= 9", "safari >= 7"]
|
||||
},
|
||||
"modules": "commonjs",
|
||||
"loose": true
|
||||
}]
|
||||
]
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
github: josdejong
|
|
@ -1,10 +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
|
||||
*.iml
|
||||
|
||||
# visual studio code
|
||||
.vscode
|
||||
build
|
||||
dist
|
||||
downloads
|
||||
node_modules
|
||||
*.zip
|
||||
npm-debug.log
|
||||
/.vs
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
bower.json
|
||||
CONTRIBUTING.md
|
||||
downloads
|
||||
misc
|
||||
node_modules
|
||||
|
@ -10,4 +9,4 @@ component.json
|
|||
.npmignore
|
||||
.gitignore
|
||||
*.zip
|
||||
npm-debug.log
|
||||
npm-debug.log
|
|
@ -1,5 +0,0 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "lts/*"
|
||||
|
||||
script: npm test && npm run lint
|
|
@ -6,18 +6,10 @@ up with ideas and suggestions, and contribute to the code.
|
|||
|
||||
There are a few preferences regarding code contributions:
|
||||
|
||||
- Send pull requests to the `develop` branch, not the `master` branch.
|
||||
- You can use modern JavaScript features, the code is transpiled using Babel.
|
||||
- `jsoneditor` follows the https://standardjs.com/ code style. To test:
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
- If possible, create a unit test for any new functionality. To run tests:
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
- `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`, do not
|
||||
commit builds located in the `./lib` and `./dist` folders.
|
||||
|
||||
Thanks!
|
||||
|
|
917
HISTORY.md
917
HISTORY.md
|
@ -3,916 +3,7 @@
|
|||
https://github.com/josdejong/jsoneditor
|
||||
|
||||
|
||||
## not yet published, version 9.1.2
|
||||
|
||||
- Log a clear error in the console when the returned value of `onEditable` is
|
||||
invalid. See #1112.
|
||||
|
||||
|
||||
## 2020-09-23, version 9.1.1
|
||||
|
||||
- Fix #1111: Enum dropdown not showing when using patternProperties for schema.
|
||||
Thanks @ziga-miklic.
|
||||
- Fixed JSONEditor not working when opened in a new window, see #1098.
|
||||
Thanks @joshkel.
|
||||
- Fix quick-key `Ctrl+D` (duplicate) not working.
|
||||
- Define "charset: utf-8" in all HTML examples.
|
||||
|
||||
|
||||
## 2020-09-15, version 9.1.0
|
||||
|
||||
- Implemented German translation (`de`). Thanks @s-a.
|
||||
- Fix quick-keys `Ctrl-\` (format) and `Ctrl-Shift-\` (compact) not working
|
||||
in `code` mode.
|
||||
- Updated dependencies to `ajv@6.12.5`.
|
||||
|
||||
|
||||
## 2020-09-09, version 9.0.5
|
||||
|
||||
- Fix #1090: autocomplete firing on dragging or clicking a node.
|
||||
- Fix #1096: editor crashing when passing an empty string as `name`.
|
||||
- Updated dependencies to `ajv@6.12.4`.
|
||||
|
||||
|
||||
## 2020-08-15, version 9.0.4
|
||||
|
||||
- Updated dependencies to `ace-builds@1.4.12`, `ajv@6.12.3`.
|
||||
- Fix #1077: change the `main` field in `package.json` to point to the actual
|
||||
bundled and minified file instead of a node.js index file.
|
||||
|
||||
|
||||
## 2020-07-02, version 9.0.3
|
||||
|
||||
- Fix regression introduced in `v9.0.2` in the select boxes in the
|
||||
Transform model not lighlighting the matches correctly.
|
||||
|
||||
|
||||
## 2020-07-01, version 9.0.2
|
||||
|
||||
- Fix #1029: XSS vulnerabilities. Thanks @onemoreflag for reporting.
|
||||
- Fix #1017: unable to style the color of a value containing a color.
|
||||
Thanks @p3x-robot.
|
||||
|
||||
|
||||
## 2020-06-24, version 9.0.1
|
||||
|
||||
- Fixed broken link to the Ace editor website (https://ace.c9.io/).
|
||||
Thanks @p3x-robot.
|
||||
- Fix #1027: create IE11 Array polyfills `find` and `findIndex` in such a way
|
||||
that they are not iterable.
|
||||
|
||||
|
||||
## 2020-05-24, version 9.0.0
|
||||
|
||||
- Implemented option `limitDragging`, see #962. This is a breaking change when
|
||||
using a JSON schema: dragging is more restrictive by default in that case.
|
||||
Set `limitDragging: false` to keep the old, non-restricted behavior.
|
||||
|
||||
|
||||
## 2020-05-13, version 8.6.8
|
||||
|
||||
- Fix #936: too many return characters inserted when pasting formatted text
|
||||
from OpenOffice.
|
||||
|
||||
|
||||
## 2020-05-10, version 8.6.7
|
||||
|
||||
- Fix #858: the `dist/jsoneditor.js` bundle containing a link to a
|
||||
non-existing source map.
|
||||
- Fix #978: in some special cases the caret was jumping to the beginning of the
|
||||
line whilst typing.
|
||||
- Update dependencies to `ajv@6.12.2`.
|
||||
|
||||
|
||||
## 2020-04-21, version 8.6.6
|
||||
|
||||
- Fix #969: adding a new property to an empty object or array is broken.
|
||||
Regression introduced in `v8.6.5`.
|
||||
|
||||
|
||||
## 2020-04-19, version 8.6.5
|
||||
|
||||
- Fix #964: translation of titles of some context menu items not working.
|
||||
- Update dependencies to `ace-builds@1.4.11`, `ajv@6.12.1`.
|
||||
|
||||
|
||||
## 2020-03-29, version 8.6.4
|
||||
|
||||
- Fix #921: `sortObjectKeys` emits `onChange` events.
|
||||
- Fix #946: `language` not working in modes `text`, `code`, and `preview`.
|
||||
- Revert reckoning with the order of object properties when updating an
|
||||
object (introduced in `v8.6.2`). See #917.
|
||||
- Implement support for repairing line separate JSON.
|
||||
|
||||
|
||||
## 2020-03-18, version 8.6.3
|
||||
|
||||
- Fix #932: `JSONEditor.update` broken, did not always recognize when the
|
||||
input changed. Regression introduced in `v8.6.2`.
|
||||
|
||||
|
||||
## 2020-03-18, version 8.6.2
|
||||
|
||||
- Fixed #917, #926: Keep order of properties when updating an object.
|
||||
- Fixed #928: Custom root name not reflected in path of navigation bar.
|
||||
- Upgraded to `ajv@6.12.0`
|
||||
|
||||
|
||||
## 2020-02-17, version 8.6.1
|
||||
|
||||
- Fixed #908: editor throwing an exception when switching from `'preview'`
|
||||
to `'code'` mode.
|
||||
|
||||
|
||||
## 2020-02-16, version 8.6.0
|
||||
|
||||
- Fixed #906: Implemented turning Python objects containing `True`, `False`
|
||||
and `None` into valid JSON using repair.
|
||||
|
||||
|
||||
## 2020-02-06, version 8.5.3
|
||||
|
||||
- Fix #892: the undo/redo buttons in mode `code` being broken when custom
|
||||
loading an old version of Ace Editor.
|
||||
|
||||
|
||||
## 2020-02-05, version 8.5.2
|
||||
|
||||
- Fix undo/redo buttons in mode `code` not always updating.
|
||||
|
||||
|
||||
## 2020-02-05, version 8.5.1
|
||||
|
||||
- Fix broken build.
|
||||
|
||||
|
||||
## 2020-02-05, version 8.5.0
|
||||
|
||||
- Implemented support for customizing the query language used in the
|
||||
Transform modal. New options `createQuery`, `executeQuery`, and
|
||||
`queryDescription` are available for this now. An example is available
|
||||
in `examples/23_custom_query_language.html`. See #857, #871.
|
||||
- Implement undo/redo buttons in `code` mode.
|
||||
- Fix history (undo/redo) being cleared in mode `code` and `text` after
|
||||
transforming or sorting.
|
||||
|
||||
|
||||
## 2020-01-25, version 8.4.1
|
||||
|
||||
- Fix `console.log` in production code. Oopsie.
|
||||
|
||||
|
||||
## 2020-01-25, version 8.4.0
|
||||
|
||||
- Added CSS classes `jsoneditor-expanded` and `jsoneditor-collapsed` on array
|
||||
and object nodes reflecting there state.
|
||||
|
||||
|
||||
## 2020-01-18, version 8.3.0
|
||||
|
||||
- Update dependency `ajv` to `v6.11.0`.
|
||||
- Fix #790: editor breaking when missing a translation containing a
|
||||
placeholder.
|
||||
|
||||
|
||||
## 2020-01-16, version 8.2.0
|
||||
|
||||
- Make it easy to create custom styling by overriding default SASS variable
|
||||
values, see #881. Thanks @petermanders89.
|
||||
- Update `ace` to `v1.4.8`.
|
||||
|
||||
|
||||
## 2020-01-06, version 8.1.2
|
||||
|
||||
- Fix #873: buttons Format, Compact, and Repair not supporting
|
||||
internationalization.
|
||||
- Fix #877: Some CSS styling issues when used in combination with Materialize.
|
||||
- Updated dependency `vanilla-picker` to `v2.10.1`.
|
||||
|
||||
|
||||
## 2019-12-28, version 8.1.1
|
||||
|
||||
- Fixed the file size reported in `preview` mode show `KB` and `MB` instead
|
||||
of `KiB` and `MiB` in order to match the size reported by filesystems.
|
||||
|
||||
|
||||
## 2019-12-18, version 8.1.0
|
||||
|
||||
- Implemented `popupAnchor` allowing to select a custom anchor element.
|
||||
See #869 and #870.
|
||||
- Fixed #502: CSS rule `* { font-family: ... }` resulting in Ace editor (`code`
|
||||
mode) not having a mono-space font anymore.
|
||||
|
||||
|
||||
## 2019-12-11, version 8.0.0
|
||||
|
||||
- Implemented option `timestampFormat` which allows customizing the formatting
|
||||
of timestamp tags. See also option `timestampTag`. Thanks @smallp.
|
||||
- Changed the behavior of `timestampTag` to fallback on the built-in rules when
|
||||
the function does not return a boolean. See #856.
|
||||
- Reverted the heuristics introduced in `v7.3.0` to check whether some field
|
||||
contains a timestamp based on the field name, because they can give wrong
|
||||
timestamps in case of values in seconds instead of the assumed milliseconds
|
||||
(see #847, #856).
|
||||
|
||||
|
||||
## 2019-12-08, version 7.5.0
|
||||
|
||||
- Extended the callback `onValidationError` to also report parse errors,
|
||||
and distinguish between JSON schema validation errors and custom errors.
|
||||
See #861 and #612. Thanks @meirotstein.
|
||||
|
||||
|
||||
## 2019-12-01, version 7.4.0
|
||||
|
||||
- Implemented callback function `onValidationError`, see #612, #854.
|
||||
Thanks @meirotstein.
|
||||
- Fixed #850: make autocomplete options robust against non-string inputs
|
||||
like `null`, `123`, `true`, `false`.
|
||||
|
||||
|
||||
## 2019-12-01, version 7.3.1
|
||||
|
||||
- Fixed #855: `onFocus` and `onBlur` not working in modes `text` and `code`
|
||||
when editor was created without main menu bar, and `editor.destroy()`
|
||||
throwing an exception.
|
||||
|
||||
|
||||
## 2019-11-27, version 7.3.0
|
||||
|
||||
- Implemented callbacks `onFocus` and `onBlur` (PR #809, issue #727).
|
||||
Thanks @123survesh.
|
||||
- Fixed #847: allow customizing the in rules determining whether a value
|
||||
is a timestamp or not by passing a callback function to `timestampTag`.
|
||||
|
||||
|
||||
## 2019-10-27, version 7.2.1
|
||||
|
||||
- Fixed #826: editor not allowing indentation `0`.
|
||||
- Fixed #828: do not expand/collapse when clicking the text of a node
|
||||
in modes `view` or `form`.
|
||||
- Fixed #829: z-index issue of context-menu button and conflicting css names.
|
||||
|
||||
|
||||
## 2019-10-23, version 7.2.0
|
||||
|
||||
- Implemented Japanese translation (`ja`). Thanks @yutakiyama.
|
||||
- Implemented French translation (`fr-FR`), and some improvements in the
|
||||
translation. Thanks @yannickyvin.
|
||||
- Upgraded to the latest version of Ace editor, 1.4.7.
|
||||
- Fixed #824: Parse errors not displayed with bottom right error icon in modes
|
||||
`code` and `text`.
|
||||
|
||||
|
||||
## 2019-10-13, version 7.1.0
|
||||
|
||||
- Upgraded to the latest version of Ace editor 1.4.6. Changed implementation
|
||||
to use `ace-builds` directly instead of `brace` (still using Ace 1.2.9).
|
||||
- Improved Portuguese translation. Thanks @victorananias.
|
||||
|
||||
|
||||
## 2019-10-06, version 7.0.5
|
||||
|
||||
- Upgraded dependencies: `vanilla-picker@2.10.0`.
|
||||
- Minor documentation improvements. Thanks @slash-arun.
|
||||
- Minor styling fixes.
|
||||
|
||||
|
||||
## 2019-09-11, version 7.0.4
|
||||
|
||||
- Fixed #723: schema error popup and color picker not always fully visible.
|
||||
- Fixed wrong text color in search box when using JSONEditor in combination
|
||||
with bootstrap. See #791. Thanks @dmitry-kulikov.
|
||||
- Fixed react examples not working out of the box when cloning or downloading
|
||||
the git repository of JSONEditor. See #787, #788. Thanks @vishwasnavadak.
|
||||
|
||||
|
||||
## 2019-09-04, version 7.0.3
|
||||
|
||||
- Fixed `index.js` pointing to non-transpiled code. See #783.
|
||||
- Fixed absolute url of images in SASS. Thanks @moonbreezee.
|
||||
|
||||
|
||||
## 2019-09-02, version 7.0.2
|
||||
|
||||
- Fix #781: race condition when destroying the editor right after setting data.
|
||||
|
||||
|
||||
## 2019-09-01, version 7.0.1
|
||||
|
||||
- Fix npm package missing `dist` folder.
|
||||
|
||||
|
||||
## 2019-09-01, version 7.0.0
|
||||
|
||||
- Converted the code largely to ES6, put Babel transpiler in place.
|
||||
- Dropped support for bower, removed the `dist` folder from the git repository.
|
||||
- Fixed #586: caret position lost when switching browser tabs.
|
||||
|
||||
|
||||
## 2019-08-28, version 6.4.1
|
||||
|
||||
- Fix styling of autocompletion dropdown broken. Regression since `v6.4.0`.
|
||||
|
||||
|
||||
## 2019-08-28, version 6.4.0
|
||||
|
||||
- Replaces CSS with SASS internally, improvements in styling. Thanks @ppetkow.
|
||||
- Fixed #761: JSON schema errors not rendered in the gutter for mode `code`
|
||||
when the path contained a property with a forward slash, and errors not
|
||||
clickable in the error table.
|
||||
- Fixed #777: option `sortObjectKeys` broken.
|
||||
|
||||
|
||||
## 2019-08-15, version 6.3.0
|
||||
|
||||
- Fixed #755: JSONEditor throwing an exception in mode `code`, `text`, and
|
||||
`preview` when `statusBar: false`.
|
||||
- When duplicating an object property, move focus to the field and do not
|
||||
immediately add the ` (copy)` suffix. See #766.
|
||||
- Fixed #769: option `name` not working anymore. Regression since `v6.1.0`.
|
||||
- Fixed #763: `autocomplete.trigger: 'focus'` throws an error when opening the
|
||||
context menu. Thanks @Thaina.
|
||||
- Updated dependencies `json-source-map@0.6.1`
|
||||
|
||||
|
||||
## 2019-08-01, version 6.2.1
|
||||
|
||||
- Updated Chinese translation. Thanks @SargerasWang.
|
||||
|
||||
|
||||
## 2019-07-28, version 6.2.0
|
||||
|
||||
- Implemented new mode `preview`, capable of working with large JSON documents
|
||||
up to 500 MiB.
|
||||
- Repair button is now capable of turning MongoDB documents into valid JSON.
|
||||
- Fixed #730: in `code` mode, there was an initial undo action which clears
|
||||
the content.
|
||||
- Upgraded dependencies `vanilla-picker@2.9.2`, `mobius1-selectr@2.4.13`,
|
||||
`ajv@6.10.2`.
|
||||
|
||||
|
||||
## 2019-06-22, version 6.1.0
|
||||
|
||||
- Implemented menu options `sort` and `transform` for modes `code` and `text`.
|
||||
- Implemented new context menu item `extract`.
|
||||
- Minor tweaks in the way paths are displayed in the sort and transform modals.
|
||||
|
||||
|
||||
## 2019-06-12, version 6.0.0
|
||||
|
||||
- Breaking change: upgraded dependency `ajv@6.10.0`, supporting JSON schema
|
||||
draft-07 alongside draft-06 and draft-04.
|
||||
- Upgraded dependency `vanilla-picker@2.8.1`.
|
||||
- Use JSON schema title as name for the root object if defined (see #635).
|
||||
|
||||
|
||||
## 2019-06-08, version 5.34.0
|
||||
|
||||
- Extended the autocomplete feature with new options `filter` and `trigger`.
|
||||
Thanks @Gcaufy.
|
||||
- Removed :hover style on disabled buttons. Thanks @Gcaufy.
|
||||
- Upgraded dependency `mobius1-selectr@2.4.12`.
|
||||
|
||||
|
||||
## 2019-05-29, version 5.33.0
|
||||
|
||||
- Fixed #697: JSON Schema enum dropdown not working inside an array.
|
||||
- Fixed #698: When using `onCreateMenu`, `node.path` is null when clicking
|
||||
on an append node or when multiple nodes are selected.
|
||||
- Upgraded dependencies to `mobius1-selectr@2.4.10`, `vanilla-picker@2.8.0`.
|
||||
- Remove :hover style on disabled buttons. Thanks @Gcaufy.
|
||||
|
||||
|
||||
## 2019-04-27, version 5.32.5
|
||||
|
||||
- Fixed a bug in the JMESPath query wizard which didn't correctly handle
|
||||
selecting multiple fields.
|
||||
- Fixed context menu not working when multiple nodes are selected.
|
||||
|
||||
|
||||
## 2019-04-10, version 5.32.4
|
||||
|
||||
- Fixed #682 and #687: JSONEditor not being able to handle JSON schema
|
||||
validation errors when the root of the document is an Array. Thanks @DusuWen.
|
||||
|
||||
|
||||
## 2019-04-04, version 5.32.3
|
||||
|
||||
- Fixed #684: `const` used in bundled library.
|
||||
|
||||
|
||||
## 2019-04-03, version 5.32.2
|
||||
|
||||
- Fixed #416: Clipped action menu for append nodes.
|
||||
- Improve detection of value type in transform modal.
|
||||
- Styling improvements in the transform modal.
|
||||
- Fix CSS class for default/non-default schema values not applied to enums,
|
||||
see (#666).
|
||||
- Fixed #671: Improved handling of duplicate property names, which could cause
|
||||
values to be cleared when used as a controlled component in for example React.
|
||||
|
||||
|
||||
## 2019-03-28, version 5.32.1
|
||||
|
||||
- Fixed a regression in parsing JSON paths: numbers where parsed as strings
|
||||
instead of a numeric value. See #679. Thanks @AdamVig.
|
||||
- Fixed using hyphens in the path of custom validation errors (see #665).
|
||||
Thanks @tobiasfriden.
|
||||
|
||||
|
||||
## 2019-03-20, version 5.32.0
|
||||
|
||||
- Implemented support for reckoning with JSON schema default values: custom
|
||||
styling can be applied for default and non-default values. Thanks @AdamVig.
|
||||
- Fixed #667: resolving JSON Schema examples and descriptions did not always
|
||||
work for referenced schemas. Thanks @AdamVig.
|
||||
- Fixed #676: JSON Paths containing array properties with a `]` not parsed
|
||||
correctly.
|
||||
|
||||
|
||||
## 2019-03-14, version 5.31.1
|
||||
|
||||
- Fix IE11 issue.
|
||||
- Some fixes in the Simplified Chinese translation.
|
||||
Thanks @@adf0001 and @yuxizhe.
|
||||
|
||||
|
||||
## 2019-03-10, version 5.31.0
|
||||
|
||||
- Display JSON schema examples in tooltip (#664). Thanks @AdamVig.
|
||||
|
||||
|
||||
## 2019-03-02, version 5.30.0
|
||||
|
||||
- Implemented a new option `onCreateMenu` to customize the action menu.
|
||||
Thanks @RobAley.
|
||||
|
||||
|
||||
## 2019-02-20, version 5.29.1
|
||||
|
||||
- Fixed #661: JSONEditor broken on IE11 caused by duplicate JSON entries
|
||||
in a translation.
|
||||
|
||||
|
||||
## 2019-02-16, version 5.29.0
|
||||
|
||||
- Added Simplified Chinese localization. Thanks @long2ice.
|
||||
- Added Turkish localization. Thanks @beratpostalci.
|
||||
- Improved JSON schema titles on fields. Fixes #321. Thanks @AdamVig.
|
||||
- Fixes in resolving JSON schemas, see #651. Thanks @AdamVig.
|
||||
- Fix #657: `onClassName` throwing an error when a node is removed.
|
||||
|
||||
|
||||
## 2019-01-23, version 5.28.2
|
||||
|
||||
- Fix #639: Occurrence of non-ES5 `const` declaration in published code.
|
||||
Regression introduced in `v5.28.0`.
|
||||
|
||||
|
||||
## 2019-01-22, version 5.28.1
|
||||
|
||||
- Fix #637: Vertical white border left/right from the main menu in some
|
||||
specific circumstances.
|
||||
- Fix #638: Cannot expand after collapse. Regression introduced in v5.28.0.
|
||||
|
||||
|
||||
## 2019-01-21, version 5.28.0
|
||||
|
||||
- Implemented new option `maxVisibleChilds` to customize the maximum number
|
||||
childs that is rendered by default. Thanks @20goto10.
|
||||
- Implemented new option `onClassName`, allowing customized and dynamic
|
||||
styling of nodes. See 20_custom_css_style_for_nodes.html for a demo.
|
||||
Thanks @maestr0.
|
||||
- Make the method `refresh()` public.
|
||||
|
||||
|
||||
## 2019-01-16, version 5.27.1
|
||||
|
||||
- Improved navigating deeply nested paths via the navigation bar, see #619.
|
||||
Thanks @meirotstein.
|
||||
- Sdd title from schema description to show the tips for user input.
|
||||
Thanks @tylerchen.
|
||||
- Fix JSON Schema not resolving refs `$ref`, and not creating enum dropdowns.
|
||||
Thanks @tylerchen.
|
||||
|
||||
|
||||
## 2019-01-05, version 5.27.0
|
||||
|
||||
- Implemented customizing object and array names via a new option
|
||||
`onNodeName`. Thanks @bnanchen.
|
||||
- Visibility of schema validation errors at the bottom of mode code and text
|
||||
are now toggleable. Thanks @meirotstein.
|
||||
- Fixed text of the mode switcher not being translated. Thanks @antfu.
|
||||
|
||||
|
||||
## 2018-12-06, version 5.26.3
|
||||
|
||||
- Fixed #610: JSON Repair now removes trailing commas.
|
||||
- Upgraded devDependency `gulp` to v4. Thanks @maestr0.
|
||||
|
||||
|
||||
## 2018-11-13, version 5.26.2
|
||||
|
||||
- Fixed dragging and selecting multiple nodes not working
|
||||
(regression introduced in `v5.26.1`).
|
||||
|
||||
|
||||
## 2018-11-13, version 5.26.1
|
||||
|
||||
- Fixed `.update()` throwing an exception when replacing a JSON object
|
||||
with `null`. Thanks @DullReferenceException.
|
||||
- Fixed #598: Search field can't be focused in object view.
|
||||
|
||||
|
||||
## 2018-11-12, version 5.26.0
|
||||
|
||||
- Implemented option `mainMenuBar` to enable/disable the main menu bar.
|
||||
Thanks @tanmayrajani.
|
||||
|
||||
|
||||
## 2018-10-29, version 5.25.0
|
||||
|
||||
- Implemented options `enableSort` and `enableTransform` so you can turn off
|
||||
these features. Thanks @tanmayrajani.
|
||||
- Fixed #590: validation failing in code and text mode when status
|
||||
bar is disabled.
|
||||
- Fixed #589: the path in the navigation bar is not updated
|
||||
when duplicating or removing a node, and neither after an undo/redo action.
|
||||
- Fixed duplicate and remove of the action menu of multiple selected
|
||||
nodes not working.
|
||||
- Fixed not preventing default selection of text when selecting nodes.
|
||||
- Fixed #595: navigation bar path link not working.
|
||||
|
||||
|
||||
## 2018-10-08, version 5.24.7
|
||||
|
||||
- Fix #582: parse error annotations not always up to date in
|
||||
code editor. Thanks @meirotstein.
|
||||
|
||||
|
||||
## 2018-09-12, version 5.24.6
|
||||
|
||||
- Fix #548: `import JSONEditor from 'jsoneditor'` not working in
|
||||
TypeScript projects (gave a constructor is undefined error).
|
||||
|
||||
|
||||
## 2018-09-06, version 5.24.5
|
||||
|
||||
- Fixed a bug in textmode on IE 11, not loading the editor when
|
||||
`Promise` is undefined.
|
||||
|
||||
|
||||
## 2018-09-06, version 5.24.4
|
||||
|
||||
- Fixed #576: Visualization in mode `view` when an array
|
||||
with more than 100 items is rendered.
|
||||
- Fixed JSONEditor not working on IE11: continue and throw console
|
||||
errors when `Promise` is undefined. Regression since `v5.23.0`.
|
||||
- Fixed `onClose` of color picker not being fired when clicking outside
|
||||
the picker to close it.
|
||||
- Upgraded dependencies `brace`, `mobius1-selectr`, `vanilla-picker`.
|
||||
- Upgraded devDependency `mocha`.
|
||||
|
||||
|
||||
## 2018-08-29, version 5.24.3
|
||||
|
||||
- Fixed color picker not working in ES6 projects.
|
||||
- Fixed color picker closing immediately after the first `onChange`
|
||||
event, and `onChange` events are now debounced like all text inputs.
|
||||
|
||||
|
||||
## 2018-08-27, version 5.24.2
|
||||
|
||||
- Improved error and validation messaging in `text` mode.
|
||||
Thanks @meirotstein.
|
||||
- Clicking a message now selects the line where the error occurs.
|
||||
- Icon bottom right showing when there are warnings or errors.
|
||||
- Fixed field still editable after moving a node from an object
|
||||
to an array, changing the field from a property into an index.
|
||||
|
||||
|
||||
## 2018-08-26, version 5.24.1
|
||||
|
||||
- Context menu and color picker are now absolutely positioned, and
|
||||
can overflow the borders of the editor.
|
||||
- Fixed #568: mode switcher disappearing when selecting the current
|
||||
mode again.
|
||||
- Fixed `transform` not creating/removing expand button when the type
|
||||
of a node changed.
|
||||
|
||||
|
||||
## 2018-08-22, version 5.24.0
|
||||
|
||||
- Implemented a color picker, and allow hooking in a custom color
|
||||
picker. new options are `colorPicker` and `onColorPicker`.
|
||||
- Implemented a timestamp tag displayed right from timestamps,
|
||||
with corresponding option `timestampTag`.
|
||||
|
||||
|
||||
## 2018-08-17, version 5.23.1
|
||||
|
||||
- Fixed #566: transform function broken, regression since `v5.20.0`.
|
||||
|
||||
|
||||
## 2018-08-15, version 5.23.0
|
||||
|
||||
- Implemented support for custom validation using a new `onValidate` callback.
|
||||
- In tree mode, nodes containing a validation error now have a className
|
||||
`jsoneditor-validation-error` which can be used for custom styling.
|
||||
|
||||
|
||||
## 2018-08-13, version 5.22.0
|
||||
|
||||
- Implemented `onEvent` callback triggered when an event occurs in a JSON
|
||||
field or value. Thanks @cristinabarrantes.
|
||||
|
||||
|
||||
## 2018-08-12, version 5.21.0
|
||||
|
||||
- Show validation errors inline instead of at the bottom when in code
|
||||
mode. Thanks @meirotstein.
|
||||
- Fix #562: allow `$` character in property names of of a JSON schema.
|
||||
|
||||
|
||||
## 2018-08-10, version 5.20.0
|
||||
|
||||
_Good news: JSONEditor is finally framework friendly and can now be easily
|
||||
integrated in React, Vue, and Angular!_
|
||||
|
||||
- Implemented new methods `update` and `updateText`, which maintain the state
|
||||
of the editor (expanded nodes, search, selection). This makes it easy to
|
||||
integrate in frameworks like React.
|
||||
- Implemented options `onChangeJSON(json)` and `onChangeText(jsonString)`.
|
||||
- Added two React examples to the `examples` folder.
|
||||
- Fixed menu buttons "Sort" and "Transform" being available in modes `view`
|
||||
and `form`.
|
||||
|
||||
|
||||
## 2018-08-02, version 5.19.2
|
||||
|
||||
- Fixed #558: scrolling to search results and automatically scrolling up/down
|
||||
when dragging an item broken (regression since v5.19.1).
|
||||
|
||||
|
||||
## 2018-07-28, version 5.19.1
|
||||
|
||||
- Fixed #557: inner contents of the scrollable area being displayed outside of
|
||||
the editor (on Chrome only).
|
||||
|
||||
|
||||
## 2018-07-11, version 5.19.0
|
||||
|
||||
- No more grayed out icons of the context menu, see #532.
|
||||
- Added Sort and Transform buttons to the main menu.
|
||||
- Fixes and improvements in the Transform dialog.
|
||||
|
||||
|
||||
## 2018-06-27, version 5.18.0
|
||||
|
||||
- Implemented JMESPath support for advanced filtering, sorting, and
|
||||
transforming of JSON documents.
|
||||
- Implemented a new option `modalAnchor` to control at which part of the
|
||||
screen the modals are displayed.
|
||||
- Fixed #544: JSON Schema errors sometimes not being displayed in the
|
||||
editor.
|
||||
|
||||
|
||||
## 2018-06-03, version 5.17.1
|
||||
|
||||
- Fixed a bug in a translation text.
|
||||
|
||||
|
||||
## 2018-06-03, version 5.17.0
|
||||
|
||||
- Implemented advanced sorting for arrays.
|
||||
|
||||
|
||||
## 2018-05-23, version 5.16.0
|
||||
|
||||
- Better handling of JSON documents containing large arrays:
|
||||
- Only displays the first 100 items of large arrays,
|
||||
with buttons "show more" and "show all" to render more items.
|
||||
- Search results are now limited to max 1000 matches,
|
||||
and search does no longer expand the paths to all matches
|
||||
but only expands the path of the current search result.
|
||||
- Fixed index numbers of Array items not being updated after sorting.
|
||||
|
||||
|
||||
## 2018-05-02, version 5.15.0
|
||||
|
||||
- Implemented selection API: `onSelectionChanged`, `onTextSelectionChanged`,
|
||||
`getSelection`, `getTextSelection`, `setSelection`, `setTextSelection`,
|
||||
and `getNodesByRange`. Thanks @meirotstein.
|
||||
|
||||
|
||||
## 2018-03-21, version 5.14.1
|
||||
|
||||
- Fixed absolute path of css image `jsoneditor-icons.svg`, which could.
|
||||
give issues with webpack plugin "file-loader". Thanks @landru29.
|
||||
|
||||
|
||||
## 2018-02-25, version 5.14.0
|
||||
|
||||
- Implemented support for translations. Thanks @mariohmol.
|
||||
- Fixed a bug sometimes occurring when dragging items from array to
|
||||
object, see #509. Thanks @43081j.
|
||||
- Fixed autocomplete not accepting returned `null` values, see #512.
|
||||
Thanks @43081j.
|
||||
- Fixed memory inefficiency when working with large JSON Schema's
|
||||
generating many errors. Thanks @43081j.
|
||||
|
||||
|
||||
## 2018-02-07, version 5.13.3
|
||||
|
||||
- Fixed a positioning issue with JSON Schema errors in text/code mode.
|
||||
|
||||
|
||||
## 2018-01-18, version 5.13.2
|
||||
|
||||
- Fixed view mode opening links in a new tab instead of current tab
|
||||
when Ctrl key is not down. Thanks @LEW21.
|
||||
- Fixed #502: code editor not showing a monospaced font some cases.
|
||||
|
||||
|
||||
## 2017-12-28, version 5.13.1
|
||||
|
||||
- Fixed another occurrence of #494: properties not escaped in the
|
||||
navigation bar.
|
||||
|
||||
|
||||
## 2017-12-28, version 5.13.0
|
||||
|
||||
- Implemented cursor position in text mode. Thanks @meirotstein.
|
||||
- Fixed #494: properties not escaped in the navigation bar.
|
||||
Thanks @meirotstein.
|
||||
|
||||
|
||||
## 2017-12-18, version 5.12.0
|
||||
|
||||
- Implemented #482: Include `caseSensitive` option for autocomplete.
|
||||
Thanks @israelito3000.
|
||||
- Upgraded dependencies
|
||||
- `ajv@5.5.2`
|
||||
|
||||
|
||||
## 2017-11-22, version 5.11.0
|
||||
|
||||
- Upgraded dependencies
|
||||
- `ajv@5.4.0`
|
||||
- `brace@0.11.0`
|
||||
- Fixed dropdown for JSON Schema enums when defined inside pattern
|
||||
properties. Thanks @alquist.
|
||||
- Fixed code containing a non UTF-8 character. Thanks @alshakero.
|
||||
|
||||
|
||||
## 2017-11-15, version 5.10.1
|
||||
|
||||
- Some styling tweaks in the navigation bar and status bar.
|
||||
- Don't display status bar in `text` mode (which doesn't yet support
|
||||
row and col counts).
|
||||
|
||||
|
||||
## 2017-11-15, version 5.10.0
|
||||
|
||||
- Implemented a navigation bar showing the path. Thanks @meirotstein.
|
||||
- Implemented a status bar showing cursor location.
|
||||
Thanks @meirotstein.
|
||||
- Implemented repairing JSON objects containing left and right single
|
||||
and double quotes (which you get when typing a JSON object in Word)
|
||||
in `text` and `code` mode.
|
||||
- Implemented repairing JSON objects containing special white space
|
||||
characters like non-breaking space.
|
||||
- Upgraded dependency `ajv` to version `5.3.0`.
|
||||
- Fixed #481: A polyfill required `DocumentType` which is not defined
|
||||
in all environments.
|
||||
|
||||
|
||||
## 2017-09-16, version 5.9.6
|
||||
|
||||
- Fixed displaying a dropdown for enums inside composite schemas.
|
||||
Thanks @hachichaud.
|
||||
- Fixed #461: Urls opening twice on Firefox and Safari.
|
||||
|
||||
|
||||
## 2017-08-26, version 5.9.5
|
||||
|
||||
- Fixed a regression introduced in `v5.9.4`: after using the context
|
||||
menu once, it was not possible to set focus to an other input field
|
||||
anymore.
|
||||
|
||||
|
||||
## 2017-08-20, version 5.9.4
|
||||
|
||||
- Fixed #447: context menus not working in Shadow DOM. Thanks @tomalec.
|
||||
|
||||
|
||||
## 2017-07-24, version 5.9.3
|
||||
|
||||
- Fixed broken multi-selection (regression).
|
||||
|
||||
|
||||
## 2017-07-13, version 5.9.2
|
||||
|
||||
- Fixed a bug in the JSON sanitizer.
|
||||
|
||||
|
||||
## 2017-07-13, version 5.9.1
|
||||
|
||||
- `setText` method of tree mode now automatically sanitizes JSON input
|
||||
when needed.
|
||||
- Fixed #430: automatically fix unescaped control characters in
|
||||
JSON input.
|
||||
|
||||
|
||||
## 2017-07-10, version 5.9.0
|
||||
|
||||
- Implemented support for JSON schema references `$ref`, see #302.
|
||||
Thanks @meirotstein.
|
||||
- Fixed #429: JSONEditor no longer accepting an empty array for option
|
||||
`modes`. Thanks @trystan2k.
|
||||
- Fixed JSONEditor picking the first entry of `modes` as initial mode
|
||||
instead of option `mode`.
|
||||
|
||||
|
||||
## 2017-07-08, version 5.8.2
|
||||
|
||||
- Select first option from `modes` instead of `tree` when `mode` is not
|
||||
configured. Thanks @bag-man.
|
||||
- Some fixes and improvements in the API of autocompletion.
|
||||
Thanks @israelito3000.
|
||||
|
||||
|
||||
## 2017-07-03, version 5.8.1
|
||||
|
||||
- Fixed broken minified bundles in folder `dist` (again...).
|
||||
|
||||
|
||||
## 2017-07-02, version 5.8.0
|
||||
|
||||
- Implemented support for autocompletion. Thanks @israelito3000.
|
||||
|
||||
|
||||
## 2017-06-27, version 5.7.2
|
||||
|
||||
- Fixed broken minified bundles in folder `dist`
|
||||
(reverted to `uglify-js@2.8.22` for now).
|
||||
|
||||
|
||||
## 2017-06-25, version 5.7.1
|
||||
|
||||
- Upgraded dependency `ajv` to version `5.2.0`. Resolves warnings in
|
||||
Webpack build processes.
|
||||
|
||||
|
||||
## 2017-05-26, version 5.7.0
|
||||
|
||||
- Implemented support for template items. Thanks @israelito3000.
|
||||
- Upgraded dependencies to the latest versions. Thanks @andreykaipov.
|
||||
|
||||
|
||||
## 2017-04-15, version 5.6.0
|
||||
|
||||
- Implemented readonly option for modes `text` and `code.`
|
||||
Thanks @walkerrandolphsmith.
|
||||
- Upgraded dependencies (`brance` and `ajv`) to the latest versions.
|
||||
- Fixed not being able to move focus to enum select box when clicking
|
||||
a JSON Schema warning.
|
||||
- Fixed #309: already loaded version of Ace being overwritten by the
|
||||
embedded version of JSONEditor.
|
||||
- Fixed #368: Mode selection drop down not fully visible on small screen.
|
||||
- Fixed #253: Optimize the input experience of Chinese IME.
|
||||
Thanks @chinesedfan.
|
||||
|
||||
|
||||
## 2017-01-06, version 5.5.11
|
||||
|
||||
- Fixed embedded version of jsoneditor ace theme not being loaded in
|
||||
minimalist version (see #55).
|
||||
- Fixed a styling issue in the SearchBox of Ace editor (mode `code`).
|
||||
- Fixed #347: CSS more robust against global settings of div position.
|
||||
- Added docs and example on how to use a custom version of Ace editor.
|
||||
|
||||
|
||||
## 2016-11-02, version 5.5.10
|
||||
|
||||
- Fixed #85: pressing enter in an input in a form containing a JSONEditor too
|
||||
breaks submitting the form.
|
||||
|
||||
|
||||
## 2016-10-17, version 5.5.9
|
||||
|
||||
- Fixed #329: Editor showing duplicate key warnings for keys defined on the
|
||||
Object prototype, like `toString` and `watch`.
|
||||
|
||||
|
||||
## 2016-09-27, version 5.5.8
|
||||
|
||||
- Fixed #314: JSON schema validation throwing an error "Unexpected token ' in
|
||||
JSON at position 0" in specific cases. Thanks @apostrophest
|
||||
|
||||
|
||||
## 2016-08-17, version 5.5.7
|
||||
## not yet released, version 5.5.7
|
||||
|
||||
- Fixed #308: wrong positioning of label "empty array" when `onEditable`
|
||||
returns false.
|
||||
|
@ -1364,7 +455,7 @@ integrated in React, Vue, and Angular!_
|
|||
|
||||
## 2012-08-12, version 1.2.0
|
||||
|
||||
- New: Added search functionality. Search results are expanded and highlighted.
|
||||
- New: Added search functionality. Search results are expanded and highlighed.
|
||||
Quickkeys in the search box: Enter (next), Shift+Enter (previous), Ctrl+Enter
|
||||
(search again).
|
||||
- New: The position of the vertical separator between left and right panel is
|
||||
|
@ -1411,7 +502,7 @@ integrated in React, Vue, and Angular!_
|
|||
## 2012-03-01, version 0.9.10
|
||||
|
||||
- Nicer looking select box for the field types, with icons.
|
||||
- Improved drag and drop: better visualized, and now working in all browsers.
|
||||
- Improved drag and drop: better visualized, and now working in all browers.
|
||||
- Previous values will be restored after changing the type of a field. When
|
||||
changing the type back, the previous value or childs will be restored.
|
||||
- When hovering buttons (fieldtype, duplicate, delete, add) or when dragging
|
||||
|
@ -1444,7 +535,7 @@ integrated in React, Vue, and Angular!_
|
|||
|
||||
## 2012-01-09, version 0.9.7
|
||||
|
||||
- Added functionality to expand/collapse a node and all its childs. Click
|
||||
- Added functionallity to expand/collapse a node and all its childs. Click
|
||||
the expand button of a node while holding Ctrl down.
|
||||
- Small interface improvements
|
||||
|
||||
|
|
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-2020 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.
|
142
README.md
142
README.md
|
@ -1,11 +1,5 @@
|
|||
# JSON Editor
|
||||
|
||||
[![Version](https://img.shields.io/npm/v/jsoneditor.svg)](https://www.npmjs.com/package/jsoneditor)
|
||||
[![Downloads](https://img.shields.io/npm/dm/jsoneditor.svg)](https://www.npmjs.com/package/jsoneditor)
|
||||
![Maintenance](https://img.shields.io/maintenance/yes/2020.svg)
|
||||
[![License](https://img.shields.io/github/license/josdejong/jsoneditor.svg)](https://github.com/josdejong/jsoneditor/blob/master/LICENSE)
|
||||
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjosdejong%2Fjsoneditor.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjosdejong%2Fjsoneditor?ref=badge_shield)
|
||||
|
||||
JSON Editor 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.
|
||||
|
@ -13,50 +7,32 @@ editor.
|
|||
The editor can be used as a component in your own web application. The library
|
||||
can be loaded as CommonJS module, AMD module, or as a regular javascript file.
|
||||
|
||||
Supported browsers: Chrome, Firefox, Safari, Opera, Edge, Internet Explorer 11.
|
||||
Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 9+.
|
||||
|
||||
<img alt="json editor" src="https://raw.github.com/josdejong/jsoneditor/master/misc/jsoneditor.png"> <img alt="code editor" src="https://raw.github.com/josdejong/jsoneditor/master/misc/codeeditor.png">
|
||||
|
||||
Cross browser testing for JSONEditor is generously provided by <a href="https://www.browserstack.com" target="_blank">BrowserStack</a>
|
||||
|
||||
<a href="https://www.browserstack.com" target="_blank"><img alt="BrowserStack" src="https://raw.github.com/josdejong/jsoneditor/master/misc/browserstack.png"></a>
|
||||
|
||||
## Features
|
||||
|
||||
JSONEditor has various modes, with the following features.
|
||||
|
||||
### Tree mode
|
||||
|
||||
- Change, add, move, remove, and duplicate fields and values.
|
||||
### Tree editor
|
||||
- Edit, add, move, remove, and duplicate fields and values.
|
||||
- Change type of values.
|
||||
- Sort arrays and objects.
|
||||
- Transform JSON using [JMESPath](http://jmespath.org/) queries.
|
||||
- Colorized code.
|
||||
- Color picker.
|
||||
- Search & highlight text in the tree view.
|
||||
- Undo and redo all actions.
|
||||
- JSON schema validation (powered by [ajv](https://github.com/epoberezkin/ajv)).
|
||||
|
||||
### Code mode
|
||||
|
||||
### Code editor
|
||||
- Colorized code (powered by [Ace](https://ace.c9.io)).
|
||||
- Inspect JSON (powered by [Ace](https://ace.c9.io)).
|
||||
- Format and compact JSON.
|
||||
- Repair JSON.
|
||||
- JSON schema validation (powered by [ajv](https://github.com/epoberezkin/ajv)).
|
||||
|
||||
### Text mode
|
||||
|
||||
### Text editor
|
||||
- Format and compact JSON.
|
||||
- Repair JSON.
|
||||
- JSON schema validation (powered by [ajv](https://github.com/epoberezkin/ajv)).
|
||||
|
||||
### Preview mode
|
||||
|
||||
- Handle large JSON documents up to 500 MiB.
|
||||
- Transform JSON using [JMESPath](http://jmespath.org/) queries.
|
||||
- Format and compact JSON.
|
||||
- Repair JSON.
|
||||
- JSON schema validation (powered by [ajv](https://github.com/epoberezkin/ajv)).
|
||||
|
||||
## Documentation
|
||||
|
||||
|
@ -71,48 +47,76 @@ JSONEditor has various modes, with the following features.
|
|||
|
||||
## Install
|
||||
|
||||
with npm (recommended):
|
||||
Install via npm:
|
||||
|
||||
npm install jsoneditor
|
||||
|
||||
> Note that to use JSONEditor in Internet Explorer 11, it is necessary
|
||||
> to load a polyfill for `Promise` in your application.
|
||||
### Versions
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
|
||||
## Use
|
||||
|
||||
```html
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<!-- when using the mode "code", it's important to specify charset utf-8 -->
|
||||
<meta 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
|
||||
const container = document.getElementById("jsoneditor")
|
||||
const options = {}
|
||||
const editor = new JSONEditor(container, options)
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var options = {};
|
||||
var editor = jsoneditor(container, options);
|
||||
|
||||
// set json
|
||||
const initialJson = {
|
||||
var json = {
|
||||
"Array": [1, 2, 3],
|
||||
"Boolean": true,
|
||||
"Null": null,
|
||||
"Number": 123,
|
||||
"Object": {"a": "b", "c": "d"},
|
||||
"String": "Hello World"
|
||||
}
|
||||
editor.set(initialJson)
|
||||
};
|
||||
editor.set(json);
|
||||
|
||||
// get json
|
||||
const updatedJson = editor.get()
|
||||
var json = editor.get();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -136,51 +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 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
|
||||
```
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
Run unit tests:
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
Run code linting ([JavaScript Standard Style](https://standardjs.com/)):
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
|
||||
## 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 one third 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
|
|
@ -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
|
888
docs/api.md
888
docs/api.md
File diff suppressed because it is too large
Load Diff
|
@ -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).
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
# Styling Reference
|
||||
|
||||
Documentation for writing custom JSON Editor styles.
|
||||
|
||||
## Node
|
||||
Node is the fundamental unit that makes up the hierarchical JSON display in the Form, Tree, and View modes. It can be
|
||||
customized with several classes that reflect its type and state.
|
||||
|
||||
- `jsoneditor-field`: the property name
|
||||
- `jsoneditor-value`: the value of the property
|
||||
- The value element will have one of the following classes depending on its type:
|
||||
- `jsoneditor-null`
|
||||
- `jsoneditor-undefined`
|
||||
- `jsoneditor-number`
|
||||
- `jsoneditor-string`
|
||||
- `jsoneditor-string jsoneditor-color-value`
|
||||
- `jsoneditor-boolean`
|
||||
- `jsoneditor-regexp`
|
||||
- `jsoneditor-array`
|
||||
- `jsoneditor-object`
|
||||
- `jsoneditor-url`
|
||||
- `jsoneditor-is-default`: applied to the value element when the value matches the default from the schema
|
||||
- `jsoneditor-is-not-default`: applied to the value element when the value does not match the default from the schema
|
||||
- `jsoneditor-schema-error`: the warning icon that appears when the Node has a schema validation error
|
||||
- `jsoneditor-popover`: the popover that appears when hovering over the schema validation error warning icon
|
|
@ -2,23 +2,22 @@
|
|||
|
||||
### Install
|
||||
|
||||
Install via npm:
|
||||
using npm:
|
||||
|
||||
npm install jsoneditor
|
||||
|
||||
## 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
|
||||
|
||||
In the body, create a div element with an id and a size:
|
||||
In the body, create an div element with an id and a size:
|
||||
|
||||
```html
|
||||
<div id="jsoneditor" style="width: 400px; height: 400px;"></div>
|
||||
|
@ -27,11 +26,11 @@ In the body, create a 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:
|
||||
|
@ -44,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()
|
||||
```
|
||||
|
||||
|
||||
|
@ -59,25 +58,24 @@ var json = editor.get();
|
|||
|
||||
```html
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<!-- when using the mode "code", it's important to specify charset utf-8 -->
|
||||
<meta 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 () {
|
||||
|
@ -88,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,11 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="UTF-8">
|
||||
<title>Basic usage | JSONEditor</title>
|
||||
|
||||
<title>JSONEditor | Basic usage</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
|
@ -24,29 +22,26 @@
|
|||
|
||||
<script>
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const options = {}
|
||||
const editor = new JSONEditor(container, options)
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var options = {}
|
||||
var editor = jsoneditor(container, options)
|
||||
|
||||
// set json
|
||||
document.getElementById('setJSON').onclick = function () {
|
||||
const json = {
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'color': '#82b92c',
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'time': 1575599819000,
|
||||
'string': 'Hello World',
|
||||
'onlineDemo': 'https://jsoneditoronline.org/'
|
||||
'string': 'Hello World'
|
||||
}
|
||||
editor.set(json)
|
||||
}
|
||||
|
||||
// get json
|
||||
document.getElementById('getJSON').onclick = function () {
|
||||
const json = editor.get()
|
||||
var json = editor.get()
|
||||
alert(JSON.stringify(json, null, 2))
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="UTF-8">
|
||||
<title>Viewer | JSONEditor</title>
|
||||
|
||||
<title>JSONEditor | Viewer</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
|
||||
|
@ -20,18 +18,20 @@
|
|||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This editor is read-only (mode='viewer').
|
||||
This editor is read-only (mode='view').
|
||||
</p>
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('jsoneditor')
|
||||
var container = document.getElementById('jsoneditor')
|
||||
|
||||
const options = {
|
||||
var options = {
|
||||
mode: 'view'
|
||||
}
|
||||
|
||||
const json = {
|
||||
var editor = jsoneditor(container, options)
|
||||
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
|
@ -39,8 +39,8 @@
|
|||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
editor.set(json)
|
||||
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<title>Switch mode | JSONEditor</title>
|
||||
|
||||
<!-- when using the mode "code", it's important to specify charset utf-8 -->
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<title>JSONEditor | Switch mode</title>
|
||||
|
||||
<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,20 +38,22 @@
|
|||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('jsoneditor')
|
||||
var container = document.getElementById('jsoneditor')
|
||||
|
||||
const options = {
|
||||
var options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
||||
modes: ['text', 'code', 'tree', 'form', 'view'], // allowed modes
|
||||
onError: function (err) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
const json = {
|
||||
var editor = jsoneditor(container, options)
|
||||
|
||||
var json = {
|
||||
"array": [1, 2, 3],
|
||||
"boolean": true,
|
||||
"null": null,
|
||||
|
@ -60,7 +62,7 @@
|
|||
"string": "Hello World"
|
||||
}
|
||||
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="UTF-8">
|
||||
<title>Load and save | JSONEditor</title>
|
||||
|
||||
<title>JSONEditor | Load and save</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>
|
||||
|
@ -40,7 +38,7 @@
|
|||
|
||||
<script>
|
||||
// create the editor
|
||||
const editor = new JSONEditor(document.getElementById('jsoneditor'))
|
||||
var editor = jsoneditor(document.getElementById('jsoneditor'))
|
||||
|
||||
// Load a JSON document
|
||||
FileReaderJS.setupInput(document.getElementById('loadDocument'), {
|
||||
|
@ -55,19 +53,19 @@
|
|||
// Save a JSON document
|
||||
document.getElementById('saveDocument').onclick = function () {
|
||||
// Save Dialog
|
||||
let fname = window.prompt("Save as...")
|
||||
|
||||
fname = window.prompt('Save as...')
|
||||
|
||||
// Check json extension in file name
|
||||
if (fname.indexOf(".") === -1) {
|
||||
fname = fname + ".json"
|
||||
if (fname.indexOf('.') == -1) {
|
||||
fname = fname + '.json'
|
||||
} else {
|
||||
if (fname.split('.').pop().toLowerCase() === "json") {
|
||||
if (fname.split('.').pop().toLowerCase() == 'json'){
|
||||
// Nothing to do
|
||||
} else {
|
||||
fname = fname.split('.')[0] + ".json"
|
||||
fname = fname.split('.')[0] + '.json'
|
||||
}
|
||||
}
|
||||
const blob = new Blob([editor.getText()], {type: 'application/json;charset=utf-8'})
|
||||
}
|
||||
var blob = new Blob([editor.getText()], {type: 'application/json;charset=utf-8'})
|
||||
saveAs(blob, fname)
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Custom editable fields</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
In this example:
|
||||
</p>
|
||||
<ul>
|
||||
<li>the field <code>_id</code> and its value are read-only</li>
|
||||
<li>the field <code>name</code> is read-only but has an editable value</li>
|
||||
<li>the field <code>age</code> and its value are editable</li>
|
||||
</ul>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('jsoneditor')
|
||||
|
||||
const 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const json = {
|
||||
_id: 123456,
|
||||
name: 'John',
|
||||
age: 32
|
||||
}
|
||||
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Read-only properties and values | JSONEditor</title>
|
||||
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Read-only properties and values</h1>
|
||||
<p>
|
||||
In this example:
|
||||
</p>
|
||||
<ul>
|
||||
<li>the field <code>_id</code> and its value are read-only</li>
|
||||
<li>the field <code>name</code> is read-only but has an editable value</li>
|
||||
<li>the field <code>age</code> and its value are editable</li>
|
||||
</ul>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
var container = document.getElementById('jsoneditor');
|
||||
|
||||
var options = {
|
||||
// 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 = jsoneditor(container, options)
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,45 +1,46 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="UTF-8">
|
||||
<title>Custom styling | JSONEditor</title>
|
||||
|
||||
<title>JSONEditor | Custom styling</title>
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
p {
|
||||
width: 500px;
|
||||
font-family: "DejaVu Sans", sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
p {
|
||||
width: 500px;
|
||||
font-family: "DejaVu Sans", sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
<link href="./css/darktheme.css" rel="stylesheet" type="text/css">
|
||||
<link href="./css/darktheme.css" rel="stylesheet" type="text/css">
|
||||
|
||||
</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
|
||||
out for the mode <code>code</code>. To do that, you can load and configure
|
||||
a custom theme for the Ace editor.
|
||||
This example demonstrates how to customize the look of JSONEditor,
|
||||
the editor below has a dark theme. Note that the example isn't worked
|
||||
out for the mode <code>code</code>. To do that, you can load and configure
|
||||
a custom theme for the Ace editor.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const options = {
|
||||
modes: ['tree', 'text']
|
||||
}
|
||||
const json = {
|
||||
var editor = jsoneditor(document.getElementById('jsoneditor'), {
|
||||
modes: ['text', 'tree']
|
||||
})
|
||||
|
||||
// load json
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
|
@ -47,7 +48,7 @@
|
|||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="UTF-8">
|
||||
<title>JSON schema validation | JSONEditor</title>
|
||||
|
||||
<title>JSONEditor | JSON schema validation</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
|
@ -18,16 +16,12 @@
|
|||
height: 500px;
|
||||
}
|
||||
|
||||
/* custom bold styling for non-default JSON schema values */
|
||||
.jsoneditor-is-not-default {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>JSON schema validation</h1>
|
||||
<p>
|
||||
This example demonstrates JSON schema validation. The JSON object in this example must contain properties like <code>firstName</code> and <code>lastName</code>, can can optionally have a property <code>age</code> which must be a positive integer.
|
||||
This example demonstrates JSON schema validation. The JSON object in this example must contain properties <code>firstName</code> and <code>lastName</code>, can can optionally have a property <code>age</code> which must be a positive integer.
|
||||
</p>
|
||||
<p>
|
||||
See <a href="http://json-schema.org/" target="_blank">http://json-schema.org/</a> for more information.
|
||||
|
@ -36,103 +30,44 @@
|
|||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const schema = {
|
||||
"title": "Employee",
|
||||
"description": "Object containing employee details",
|
||||
var schema = {
|
||||
"title": "Example Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"firstName": {
|
||||
"title": "First Name",
|
||||
"description": "The given name.",
|
||||
"examples": [
|
||||
"John"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"title": "Last Name",
|
||||
"description": "The family name.",
|
||||
"examples": [
|
||||
"Smith"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"gender": {
|
||||
"title": "Gender",
|
||||
"enum": ["male", "female"]
|
||||
},
|
||||
"availableToHire": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"age": {
|
||||
"description": "Age in years",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"examples": [28, 32]
|
||||
},
|
||||
"job": {
|
||||
"$ref": "job"
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": ["firstName", "lastName"]
|
||||
}
|
||||
|
||||
const job = {
|
||||
"title": "Job description",
|
||||
"type": "object",
|
||||
"required": ["address"],
|
||||
"properties": {
|
||||
"company": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"ACME",
|
||||
"Dexter Industries"
|
||||
]
|
||||
},
|
||||
"role": {
|
||||
"description": "Job title.",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"Human Resources Coordinator",
|
||||
"Software Developer"
|
||||
],
|
||||
"default": "Software Developer"
|
||||
},
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"salary": {
|
||||
"type": "number",
|
||||
"minimum": 120,
|
||||
"examples": [100, 110, 120]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const json = {
|
||||
var json = {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
gender: null,
|
||||
age: "28",
|
||||
availableToHire: true,
|
||||
job: {
|
||||
company: 'freelance',
|
||||
role: 'developer',
|
||||
salary: 100
|
||||
}
|
||||
age: 28
|
||||
}
|
||||
|
||||
const options = {
|
||||
schema: schema,
|
||||
schemaRefs: {"job": job},
|
||||
mode: 'tree',
|
||||
modes: ['code', 'text', 'tree', 'preview']
|
||||
var options = {
|
||||
modes: ['code', 'tree']
|
||||
}
|
||||
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const 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>
|
||||
|
|
|
@ -1,66 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="UTF-8">
|
||||
<title>Custom Ace Editor | JSONEditor</title>
|
||||
|
||||
<title>JSONEditor | Custom Ace</title>
|
||||
|
||||
<!-- load a custom version of Ace editor -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js"></script>
|
||||
|
||||
<!-- load the minimalist version of JSONEditor, which doesn't have Ace embedded -->
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<!-- 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;
|
||||
}
|
||||
|
||||
body, html {
|
||||
font-family: "DejaVu Sans", sans-serif;
|
||||
}
|
||||
|
||||
p, li {
|
||||
width: 500px;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1>Custom Ace editor</h1>
|
||||
|
||||
<h1>Custom Ace Editor</h1>
|
||||
<p>
|
||||
This example demonstrates how to load a custom version of Ace editor into JSONEditor.
|
||||
</p>
|
||||
<p>
|
||||
By default, JSONEditor <code>code</code> mode loads the following Ace plugins:
|
||||
</p>
|
||||
<ul>
|
||||
<li>ace/mode/json</li>
|
||||
<li>ace/ext/searchbox</li>
|
||||
<li>ace/theme/jsoneditor</li>
|
||||
</ul>
|
||||
<p>
|
||||
The jsoneditor theme comes embedded with JSONEditor. The other two plugins (json and searchbox) must be available in the folder of the custom Ace editor, or already be loaded via a script tag.
|
||||
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
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const options = {
|
||||
modes: ['text', 'code', 'tree', 'form', 'view'],
|
||||
// create the editor, set mode to 'code' (powered by ace editor)
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var options = {
|
||||
mode: 'code',
|
||||
ace: ace
|
||||
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
|
||||
}
|
||||
}
|
||||
const json = {
|
||||
var editor = jsoneditor(container, options)
|
||||
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
|
@ -68,7 +57,7 @@
|
|||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
editor.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- when using the mode "code", it's important to specify charset utf-8 -->
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Switch mode</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font: 10.5pt arial;
|
||||
color: #4d4d4d;
|
||||
line-height: 150%;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Switch editor mode using the mode box.
|
||||
Note that the mode can be changed programmatically as well using the method
|
||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('jsoneditor')
|
||||
|
||||
const options = {
|
||||
mode: 'text',
|
||||
modes: ['text', 'code'],
|
||||
onEditable: function (node) {
|
||||
if (!node.path) {
|
||||
// In modes code and text, node is empty: no path, field, or value
|
||||
// returning false makes the text area read-only
|
||||
return false;
|
||||
}
|
||||
},
|
||||
onError: function (err) {
|
||||
alert(err.toString())
|
||||
},
|
||||
onModeChange: function (newMode, oldMode) {
|
||||
console.log('Mode switched from', oldMode, 'to', newMode)
|
||||
}
|
||||
}
|
||||
|
||||
const json = {
|
||||
"array": [1, 2, 3],
|
||||
"boolean": true,
|
||||
"null": null,
|
||||
"number": 123,
|
||||
"object": {"a": "b", "c": "d"},
|
||||
"string": "Hello World"
|
||||
}
|
||||
|
||||
const editor = new JSONEditor(container, options, 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>
|
|
@ -1,73 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Item templates</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
width: 600px;
|
||||
font: 11pt sans-serif;
|
||||
}
|
||||
#jsoneditor {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Item templates</h1>
|
||||
<p>
|
||||
Using item templates, the options in the context menu under "insert" and "append" can be extended with extra options, containing a domain specific template like a "Person", "Contact", "Order", "Address", etc.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const json = [
|
||||
{
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
age: 28
|
||||
}
|
||||
]
|
||||
|
||||
const options = {
|
||||
templates: [
|
||||
{
|
||||
text: 'Person',
|
||||
title: 'Insert a Person Node',
|
||||
className: 'jsoneditor-type-object',
|
||||
field: 'PersonTemplate',
|
||||
value: {
|
||||
'firstName': 'John',
|
||||
'lastName': 'Do',
|
||||
'age': 28
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Address',
|
||||
title: 'Insert a Address Node',
|
||||
field: 'AddressTemplate',
|
||||
value: {
|
||||
'street': '',
|
||||
'city': '',
|
||||
'state': '',
|
||||
'ZIP code': ''
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
editor.expandAll()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,52 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Auto Complete</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
p {
|
||||
width: 500px;
|
||||
font-family: "DejaVu Sans", sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This example demonstrates how to autocomplete works, options available are: 'apple','cranberry','raspberry','pie', 'mango', 'mandarine', 'melon', 'appleton'.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const options = {
|
||||
autocomplete: {
|
||||
getOptions: function () {
|
||||
return ['apple', 'cranberry', 'raspberry', 'pie', 'mango', 'mandarine', 'melon', 'appleton'];
|
||||
}
|
||||
}
|
||||
}
|
||||
const json = {
|
||||
'array': [{'field1':'v1', 'field2':'v2'}, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,73 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Dynamic Auto Complete</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
p {
|
||||
width: 500px;
|
||||
font-family: "DejaVu Sans", sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This example demonstrates how to autocomplete works, options available are dynamics and consist in all the strings found in the json
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const options = {
|
||||
autocomplete: {
|
||||
applyTo:['value'],
|
||||
filter: 'contain',
|
||||
trigger: 'focus',
|
||||
getOptions: function (text, path, input, editor) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const options = extractUniqueWords(editor.get())
|
||||
if (options.length > 0) {
|
||||
resolve(options)
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// helper function to extract all unique words in the keys and values of a JSON object
|
||||
function extractUniqueWords (json) {
|
||||
return _.uniq(_.flatMapDeep(json, function (value, key) {
|
||||
return _.isObject(value)
|
||||
? [key]
|
||||
: [key, String(value)]
|
||||
}))
|
||||
}
|
||||
|
||||
const json = {
|
||||
'array': [{'field1':'v1', 'field2':'v2'}, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,109 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Advanced Auto Complete</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
<script src="https://unpkg.com/jsonpath@0.2.11/jsonpath.min.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
p {
|
||||
width: 500px;
|
||||
font-family: "DejaVu Sans", sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This example demonstrates how to autocomplete works with an ActivationChar option, press "*" in any value and continue with autocompletion.
|
||||
The autocomplete returns the posible jsonpaths of the existing json document, for example <code>*object.a</code>.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const activationChar = '*'
|
||||
const options = {
|
||||
autocomplete: {
|
||||
confirmKeys: [39, 35, 9, 190], // Confirm Autocomplete Keys: [right, end, tab, '.'] // By default are only [right, end, tab]
|
||||
caseSensitive: false,
|
||||
|
||||
getOptions: function (text, path, input, editor) {
|
||||
if (!text.startsWith(activationChar) || input !== 'value') return []
|
||||
let data = {}
|
||||
let startFrom = 0
|
||||
const lastPoint = text.lastIndexOf('.')
|
||||
const jsonObj = editor.get()
|
||||
if ((lastPoint > 0) && (text.length > 1)) {
|
||||
data = jsonpath.query(jsonObj, '$.' + text.substring(activationChar.length, lastPoint))
|
||||
if (data.length > 0) {
|
||||
data = data[0]
|
||||
} else {
|
||||
data = {}
|
||||
}
|
||||
// Indicate that autocompletion should start after the . (ignoring the first part)
|
||||
startFrom = text.lastIndexOf('.') + 1
|
||||
} else {
|
||||
data = jsonObj
|
||||
}
|
||||
|
||||
const optionsStr = YaskON.stringify(data, null, activationChar)
|
||||
const options = optionsStr.split('\n')
|
||||
return { startFrom: startFrom, options: options }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// helper function to auto complete paths of a JSON object
|
||||
const YaskON = {
|
||||
// Return first level json paths by the node 'o'
|
||||
stringify: function (o, prefix, activationChar) {
|
||||
prefix = prefix || ''
|
||||
switch (typeof o) {
|
||||
case 'object':
|
||||
let output = ''
|
||||
if (Array.isArray(o)) {
|
||||
o.forEach(function (e, index) {
|
||||
output += activationChar + prefix + '[' + index + ']' + '\n'
|
||||
}.bind(this))
|
||||
return output
|
||||
}
|
||||
output = ''
|
||||
for (let k in o) {
|
||||
if (o.hasOwnProperty(k)) {
|
||||
if (prefix === '') output += this.stringify(o[k], k, activationChar)
|
||||
}
|
||||
}
|
||||
if (prefix !== '') output += activationChar + prefix + '\n'
|
||||
return output
|
||||
case 'function':
|
||||
return ''
|
||||
default:
|
||||
return prefix + '\n'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const json = {
|
||||
'array': [{ 'field1': 'v1', 'field2': 'v2' }, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': { 'a': 'b', 'c': 'd' },
|
||||
'string': 'Hello World'
|
||||
}
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,57 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSONEditor | Translate</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
JSONEditor has support for multiple languages (i18n), in this case uses <code>pt-BR</code>.
|
||||
</p>
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const options = {
|
||||
// switch between pt-BR or en for testing forcing a language
|
||||
// leave blank to get language
|
||||
'language': 'pt-BR',
|
||||
'languages': {
|
||||
'pt-BR': {
|
||||
'auto': 'Automático testing'
|
||||
},
|
||||
'en': {
|
||||
'auto': 'Auto testing'
|
||||
},
|
||||
'newlang': {
|
||||
'auto': 'Auto new lang'
|
||||
}
|
||||
}
|
||||
}
|
||||
const editor = new JSONEditor(container, options)
|
||||
|
||||
const 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>
|
|
@ -1,124 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font: 10.5pt arial;
|
||||
color: #4d4d4d;
|
||||
line-height: 150%;
|
||||
width: 500px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
code.multiline {
|
||||
display: block;
|
||||
white-space: pre-wrap
|
||||
}
|
||||
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Selection indication was done using the <code>on[Text]SelectionChange</code> listeners.<br/>
|
||||
you can try the following calls in the console of your browser:<br/>
|
||||
<code class="multiline">
|
||||
// text and code modes:
|
||||
editor.getTextSelection()
|
||||
editor.setTextSelection(startPos, endPos)
|
||||
// tree mode:
|
||||
editor.getSelection()
|
||||
editor.setSelection(startNode, endNode)
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<form>
|
||||
<div id="jsoneditor"></div>
|
||||
<div id="textModeSelection" style="display:none;">
|
||||
<b>Selection:</b><div id="textRange"></div>
|
||||
<b>Text:</b><div id="selectedText"></div>
|
||||
</div>
|
||||
<div id="treeModeSelection">
|
||||
<b>Selection:</b>
|
||||
<div id="selectedNodes"></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('jsoneditor')
|
||||
|
||||
const options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
||||
onError: function (err) {
|
||||
alert(err.toString())
|
||||
},
|
||||
onChange: function () {
|
||||
console.log('change')
|
||||
},
|
||||
onModeChange: function (mode) {
|
||||
const treeMode = document.getElementById('treeModeSelection')
|
||||
const textMode = document.getElementById('textModeSelection')
|
||||
|
||||
treeMode.style.display = textMode.style.display = 'none'
|
||||
|
||||
if (mode === 'code' || mode === 'text') {
|
||||
textMode.style.display = 'inline'
|
||||
} else {
|
||||
treeMode.style.display = 'inline'
|
||||
}
|
||||
},
|
||||
indentation: 4,
|
||||
escapeUnicode: true,
|
||||
onTextSelectionChange: function(start, end, text) {
|
||||
const rangeEl = document.getElementById('textRange')
|
||||
rangeEl.innerHTML = 'start: ' + JSON.stringify(start) + ', end: ' + JSON.stringify(end)
|
||||
const textEl = document.getElementById('selectedText')
|
||||
textEl.innerHTML = text
|
||||
},
|
||||
onSelectionChange: function(start, end) {
|
||||
const nodesEl = document.getElementById('selectedNodes')
|
||||
nodesEl.innerHTML = ''
|
||||
if (start) {
|
||||
nodesEl.innerHTML = ('start: ' + JSON.stringify(start))
|
||||
if (end) {
|
||||
nodesEl.innerHTML += ('<br/>end: ' + JSON.stringify(end))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const json = {
|
||||
"array": [1, 2, [3,4,5]],
|
||||
"boolean": true,
|
||||
"htmlcode": '"',
|
||||
"escaped_unicode": '\\u20b9',
|
||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
||||
"return": '\n',
|
||||
"null": null,
|
||||
"number": 123,
|
||||
"object": {"a": "b", "c": "d"},
|
||||
"string": "Hello World",
|
||||
"url": "http://jsoneditoronline.org"
|
||||
}
|
||||
|
||||
window.editor = new JSONEditor(container, options, json)
|
||||
|
||||
console.log('json', json)
|
||||
console.log('string', JSON.stringify(json))
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,66 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Synchronize two editors</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#jsoneditor1,
|
||||
#jsoneditor2 {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Keep two editors synchronized using <code>onChangeText</code> and <code>updateText</code>.
|
||||
</p>
|
||||
<p>
|
||||
This can be done too with <code>onChangeJSON</code> and <code>update</code>, which can only be used in
|
||||
modes <code>tree</code>, <code>form</code> (and <code>view</code>).
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor1"></div>
|
||||
<div id="jsoneditor2"></div>
|
||||
|
||||
<script>
|
||||
// create editor 1
|
||||
const editor1 = new JSONEditor(document.getElementById('jsoneditor1'), {
|
||||
onChangeText: function (jsonString) {
|
||||
editor2.updateText(jsonString)
|
||||
}
|
||||
})
|
||||
|
||||
// create editor 2
|
||||
const editor2 = new JSONEditor(document.getElementById('jsoneditor2'), {
|
||||
onChangeText: function (jsonString) {
|
||||
editor1.updateText(jsonString)
|
||||
}
|
||||
})
|
||||
|
||||
// set initial data in both editors
|
||||
const json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
editor1.set(json)
|
||||
editor2.set(json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,96 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font: 10.5pt arial;
|
||||
color: #4d4d4d;
|
||||
line-height: 150%;
|
||||
width: 500px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
When clicking on a JSON field or value, a log message will be shown in
|
||||
console.
|
||||
</p>
|
||||
|
||||
<form>
|
||||
<div id="jsoneditor"></div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('jsoneditor')
|
||||
|
||||
const options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
||||
name: "jsonContent",
|
||||
onError: function (err) {
|
||||
alert(err.toString())
|
||||
},
|
||||
onEvent: function(node, event) {
|
||||
if (event.type === 'click') {
|
||||
let message = 'click on <' + node.field +
|
||||
'> under path <' + node.path +
|
||||
'> with pretty path: <' + prettyPrintPath(node.path) + '>'
|
||||
if (node.value) {
|
||||
message += ' with value <' + node.value + '>'
|
||||
}
|
||||
console.log(message)
|
||||
}
|
||||
function prettyPrintPath(path) {
|
||||
let str = ''
|
||||
for (let i=0; i<path.length; i++) {
|
||||
const element = path[i]
|
||||
if (typeof element === 'number') {
|
||||
str += '[' + element + ']'
|
||||
} else {
|
||||
if (str.length > 0) str += ','
|
||||
str += element
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const json = {
|
||||
"array": [1, 2, [3,4,5]],
|
||||
"boolean": true,
|
||||
"htmlcode": '"',
|
||||
"escaped_unicode": '\\u20b9',
|
||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
||||
"return": '\n',
|
||||
"null": null,
|
||||
"number": 123,
|
||||
"object": {"a": "b", "c": "d", "e": [1, 2, 3]},
|
||||
"string": "Hello World",
|
||||
"url": "http://jsoneditoronline.org",
|
||||
"[0]": "zero"
|
||||
}
|
||||
|
||||
window.editor = new JSONEditor(container, options, json)
|
||||
|
||||
console.log('json', json)
|
||||
console.log('string', JSON.stringify(json))
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,109 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Custom validation</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
width: 600px;
|
||||
font: 11pt sans-serif;
|
||||
}
|
||||
#jsoneditor {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Custom validation</h1>
|
||||
<p>
|
||||
This example demonstrates how to run custom validation on a JSON object.
|
||||
The validation is available in all modes.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const json = {
|
||||
team: [
|
||||
{
|
||||
name: 'Joe',
|
||||
age: 17
|
||||
},
|
||||
{
|
||||
name: 'Sarah',
|
||||
age: 13
|
||||
},
|
||||
{
|
||||
name: 'Jack'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'text', 'tree', 'preview'],
|
||||
onValidate: function (json) {
|
||||
// rules:
|
||||
// - team, names, and ages must be filled in and be of correct type
|
||||
// - a team must have 4 members
|
||||
// - at lease one member of the team must be adult
|
||||
const errors = []
|
||||
|
||||
if (json && Array.isArray(json.team)) {
|
||||
// check whether each team member has name and age filled in correctly
|
||||
json.team.forEach(function (member, index) {
|
||||
if (typeof member !== 'object') {
|
||||
errors.push({path: ['team', index], message: 'Member must be an object with properties "name" and "age"'})
|
||||
}
|
||||
|
||||
if ('name' in member) {
|
||||
if (typeof member.name !== 'string') {
|
||||
errors.push({path: ['team', index, 'name'], message: 'Name must be a string'})
|
||||
}
|
||||
} else {
|
||||
errors.push({path: ['team', index], message: 'Required property "name"" missing'})
|
||||
}
|
||||
|
||||
if ('age' in member) {
|
||||
if (typeof member.age !== 'number') {
|
||||
errors.push({path: ['team', index, 'age'], message: 'Age must be a number'})
|
||||
}
|
||||
} else {
|
||||
errors.push({path: ['team', index], message: 'Required property "age" missing'})
|
||||
}
|
||||
})
|
||||
|
||||
// check whether the team consists of exactly four members
|
||||
if (json.team.length !== 4) {
|
||||
errors.push({path: ['team'], message: 'A team must have 4 members'})
|
||||
}
|
||||
|
||||
// check whether there is at least one adult member in the team
|
||||
const adults = json.team.filter(function (member) {
|
||||
return member ? member.age >= 18 : false
|
||||
})
|
||||
if (adults.length === 0) {
|
||||
errors.push({path: ['team'], message: 'A team must have at least one adult person (age >= 18)'})
|
||||
}
|
||||
} else {
|
||||
errors.push({path: [], message: 'Required property "team" missing or not an Array'})
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
}
|
||||
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
editor.expandAll()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,92 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Custom validation (asynchronous)</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
width: 600px;
|
||||
font: 11pt sans-serif;
|
||||
}
|
||||
#jsoneditor {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Asynchronous custom validation</h1>
|
||||
<p>
|
||||
This example demonstrates how to run asynchronous custom validation on a JSON object.
|
||||
The names are checked asynchronously and the results "come in" half a second later.
|
||||
Known names in this example are 'Joe', 'Harry', 'Megan'. For other names, a validation error will be displayed.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const json = {
|
||||
customers: [
|
||||
{name: 'Joe'},
|
||||
{name: 'Sarah'},
|
||||
{name: 'Harry'},
|
||||
]
|
||||
}
|
||||
|
||||
const options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'text', 'tree', 'preview'],
|
||||
onValidate: function (json) {
|
||||
// in this validation function we fake sending a request to a server
|
||||
// to validate the existence of customers
|
||||
if (json && Array.isArray(json.customers)) {
|
||||
return Promise
|
||||
.all(json.customers.map(function (customer, index) {
|
||||
return isExistingCustomer(customer && customer.name).then(function (exists) {
|
||||
if (!exists) {
|
||||
return {
|
||||
path: ['customers', index],
|
||||
message: 'Customer ' + customer.name + ' doesn\'t exist in our database'
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
}))
|
||||
.then(function (errors) {
|
||||
return errors.filter(function (error) {
|
||||
return error != null
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
editor.expandAll()
|
||||
|
||||
// this function fakes a request (asynchronous) to a server to validate the existence of a customer
|
||||
function isExistingCustomer (customerName) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
const customers = ['Joe', 'Harry', 'Megan']
|
||||
const exists = customers.indexOf(customerName) !== -1
|
||||
resolve(exists)
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,160 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font: 10.5pt arial;
|
||||
color: #4d4d4d;
|
||||
line-height: 150%;
|
||||
width: 100%;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
#containerLeft {
|
||||
display: inline-block;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#containerRight {
|
||||
display: inline-block;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
#containerRight .different_element {
|
||||
background-color: #acee61;
|
||||
}
|
||||
#containerRight .different_element div.jsoneditor-field,
|
||||
#containerRight .different_element div.jsoneditor-value {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#containerLeft .different_element {
|
||||
background-color: pink;
|
||||
}
|
||||
#containerLeft .different_element div.jsoneditor-field,
|
||||
#containerLeft .different_element div.jsoneditor-value {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3>JSON Diff</h3>
|
||||
<p>
|
||||
This example highlights the differences between two JSON objects using the option <code>onClassName</code>.
|
||||
Make a change in the left or right editor to see the changes update accordingly.
|
||||
</p>
|
||||
<div id="wrapper">
|
||||
<div id="containerLeft"></div>
|
||||
<div id="containerRight"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const containerLeft = document.getElementById('containerLeft')
|
||||
const containerRight = document.getElementById('containerRight')
|
||||
|
||||
function findNodeInJson(json, path){
|
||||
if(!json || path.length ===0) {
|
||||
return {field: undefined, value: undefined}
|
||||
}
|
||||
const first = path[0]
|
||||
const remainingPath = path.slice(1)
|
||||
|
||||
if(remainingPath.length === 0) {
|
||||
return {field: (typeof json[first] !== 'undefined' ? first : undefined), value: json[first]}
|
||||
} else {
|
||||
return findNodeInJson(json[first], remainingPath)
|
||||
}
|
||||
}
|
||||
|
||||
function onClassName({ path, field, value }) {
|
||||
const thisNode = findNodeInJson(jsonRight, path)
|
||||
const oppositeNode = findNodeInJson(jsonLeft, path)
|
||||
let isValueEqual = JSON.stringify(thisNode.value) === JSON.stringify(oppositeNode.value)
|
||||
|
||||
if(Array.isArray(thisNode.value) && Array.isArray(oppositeNode.value)) {
|
||||
isValueEqual = thisNode.value.every(function (e) {
|
||||
return oppositeNode.value.includes(e)
|
||||
})
|
||||
}
|
||||
|
||||
if (thisNode.field === oppositeNode.field && isValueEqual) {
|
||||
return 'the_same_element'
|
||||
} else {
|
||||
return 'different_element'
|
||||
}
|
||||
}
|
||||
|
||||
const optionsLeft = {
|
||||
mode: 'tree',
|
||||
onError: function (err) {
|
||||
alert(err.toString())
|
||||
},
|
||||
onClassName: onClassName,
|
||||
onChangeJSON: function (j) {
|
||||
jsonLeft = j
|
||||
window.editorRight.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
const optionsRight = {
|
||||
mode: 'tree',
|
||||
onError: function (err) {
|
||||
alert(err.toString())
|
||||
},
|
||||
onClassName: onClassName,
|
||||
onChangeJSON: function (j) {
|
||||
jsonRight = j
|
||||
window.editorLeft.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
let jsonLeft = {
|
||||
"arrayOfArrays": [1, 2, 999, [3,4,5]],
|
||||
"someField": true,
|
||||
"boolean": true,
|
||||
"htmlcode": '"',
|
||||
"escaped_unicode": '\\u20b9',
|
||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
||||
"return": '\n',
|
||||
"null": null,
|
||||
"thisObjectDoesntExistOnTheRight" : {key: "value"},
|
||||
"number": 123,
|
||||
"object": {"a": "b","new":4, "c": "d", "e": [1, 2, 3]},
|
||||
"string": "Hello World",
|
||||
"url": "http://jsoneditoronline.org",
|
||||
"[0]": "zero"
|
||||
}
|
||||
|
||||
let jsonRight = {
|
||||
"arrayOfArrays": [1, 2, [3,4,5]],
|
||||
"boolean": true,
|
||||
"htmlcode": '"',
|
||||
"escaped_unicode": '\\u20b9',
|
||||
"thisFieldDoesntExistOnTheLeft": 'foobar',
|
||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
||||
"return": '\n',
|
||||
"null": null,
|
||||
"number": 123,
|
||||
"object": {"a": "b", "c": "d", "e": [1, 2, 3]},
|
||||
"string": "Hello World",
|
||||
"url": "http://jsoneditoronline.org",
|
||||
"[0]": "zero"
|
||||
}
|
||||
|
||||
window.editorLeft = new JSONEditor(containerLeft, optionsLeft, jsonLeft)
|
||||
window.editorRight = new JSONEditor(containerRight, optionsRight, jsonRight)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,148 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | Basic usage</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.submenu-highlight {
|
||||
background-color: yellow !important;
|
||||
}
|
||||
|
||||
.rainbow {
|
||||
background: linear-gradient(to right, cyan, yellow, violet, green, orange, blue) !important;
|
||||
}
|
||||
|
||||
.example-class > .jsoneditor-icon {
|
||||
background-position: -168px -48px; /* warning triangle */
|
||||
}
|
||||
|
||||
.dotty {
|
||||
border-top : 1px dotted #e5e5e5 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Context Menu Customization</h1>
|
||||
<p>
|
||||
This example demonstrates the use of the onCreateMenu callback option, which
|
||||
allows you to customise context menus after they are created but before they
|
||||
are shown to the user. You can alter/delete existing items as well as
|
||||
adding new menu items. See the source code for this example for more
|
||||
information.
|
||||
</p>
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
|
||||
const options = {
|
||||
// onCreateMenu allows us to register a call back function to customise
|
||||
// the context menu. The callback accpets two parameters, items and path.
|
||||
// Items is an array containing the current menu items, and path
|
||||
// (if present) contains the path of the current node (as an array).
|
||||
// The callback should return the modified (or unmodified) list of menu
|
||||
// items.
|
||||
|
||||
// Every time the user clicks on a context menu button, the menu
|
||||
// is created from scratch and this callback is called.
|
||||
|
||||
onCreateMenu: function (items, node) {
|
||||
const path = node.path
|
||||
|
||||
// log the current items and node for inspection
|
||||
console.log('items:', items, 'node:', node)
|
||||
|
||||
// We are going to add a menu item which returns the current node path
|
||||
// as a jq path selector ( https://stedolan.github.io/jq/ ). First we
|
||||
// will create a function, and then We will connect this function to
|
||||
// the menu item click property in a moment.
|
||||
|
||||
function pathTojq() {
|
||||
let pathString = ''
|
||||
|
||||
path.forEach(function (segment, index) { // path is an array, loop through it
|
||||
if (typeof segment == 'number') { // format the selector for array indexs ...
|
||||
pathString += '[' + segment + ']'
|
||||
} else { // ... or object keys
|
||||
pathString += '."' + segment + '"'
|
||||
}
|
||||
})
|
||||
|
||||
alert(pathString) // show it to the user.
|
||||
}
|
||||
|
||||
// Create a new menu item. For our example, we only want to do this
|
||||
// if there is a path (in the case of appendnodes (for new objects)
|
||||
// path is null until a node is created)
|
||||
if (path) {
|
||||
// Each item in the items array represents a menu item,
|
||||
// and requires the following details :
|
||||
|
||||
items.push({
|
||||
text: 'jq Path', // the text for the menu item
|
||||
title: 'Show the jq path for this node', // the HTML title attribute
|
||||
className: 'example-class', // the css class name(s) for the menu item
|
||||
click: pathTojq // the function to call when the menu item is clicked
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// Now we will iterate through the menu items, which includes the items
|
||||
// created by jsoneditor, and the new item we added above. In this
|
||||
// example we will just alter the className property for the items, but
|
||||
// you can alter any property (e.g. the click callback, text property etc.)
|
||||
// for any item, or even delete the whole menu item.
|
||||
items.forEach(function (item, index, items) {
|
||||
if ("submenu" in item) {
|
||||
// if the item has a submenu property, it is a submenu heading
|
||||
// and contains another array of menu items. Let's colour
|
||||
// that yellow...
|
||||
items[index].className += ' submenu-highlight'
|
||||
} else {
|
||||
// if it's not a submenu heading, let's make it colorful
|
||||
items[index].className += ' rainbow'
|
||||
}
|
||||
})
|
||||
|
||||
// note that the above loop isn't recursive, so it only alters the classes
|
||||
// on the top-level menu items. To also process menu items in submenus
|
||||
// you should iterate through any "submenu" arrays of items if the item has one.
|
||||
|
||||
// next, just for fun, let's remove any menu separators (again just at the
|
||||
// top level menu). A menu separator is an item with a type : 'separator'
|
||||
// property
|
||||
items = items.filter(function (item) {
|
||||
return item.type !== 'separator'
|
||||
})
|
||||
|
||||
// finally we need to return the items array. If we don't, the menu
|
||||
// will be empty.
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
const json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'color': '#82b92c',
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,153 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | onValidationError</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
width: 600px;
|
||||
font: 11pt sans-serif;
|
||||
}
|
||||
#jsoneditor {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#onValidationOutput {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
/* custom bold styling for non-default JSON schema values */
|
||||
.jsoneditor-is-not-default {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>JSON schema validation</h1>
|
||||
<p>
|
||||
This example demonstrates onValidationError callback.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
<div id="onValidationOutput"></div>
|
||||
|
||||
<script>
|
||||
var schema = {
|
||||
"title": "Employee",
|
||||
"description": "Object containing employee details",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"firstName": {
|
||||
"title": "First Name",
|
||||
"description": "The given name.",
|
||||
"examples": [
|
||||
"John"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"title": "Last Name",
|
||||
"description": "The family name.",
|
||||
"examples": [
|
||||
"Smith"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"gender": {
|
||||
"title": "Gender",
|
||||
"enum": ["male", "female"]
|
||||
},
|
||||
"availableToHire": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"age": {
|
||||
"description": "Age in years",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"examples": [28, 32]
|
||||
},
|
||||
"job": {
|
||||
"$ref": "job"
|
||||
}
|
||||
},
|
||||
"required": ["firstName", "lastName"]
|
||||
};
|
||||
|
||||
var job = {
|
||||
"title": "Job description",
|
||||
"type": "object",
|
||||
"required": ["address"],
|
||||
"properties": {
|
||||
"company": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"ACME",
|
||||
"Dexter Industries"
|
||||
]
|
||||
},
|
||||
"role": {
|
||||
"description": "Job title.",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"Human Resources Coordinator",
|
||||
"Software Developer"
|
||||
],
|
||||
"default": "Software Developer"
|
||||
},
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"salary": {
|
||||
"type": "number",
|
||||
"minimum": 120,
|
||||
"examples": [100, 110, 120]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var json = {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
gender: null,
|
||||
age: "28",
|
||||
availableToHire: true,
|
||||
job: {
|
||||
company: 'freelance',
|
||||
role: 'developer',
|
||||
salary: 100
|
||||
}
|
||||
};
|
||||
|
||||
var options = {
|
||||
schema: schema,
|
||||
schemaRefs: {"job": job},
|
||||
mode: 'code',
|
||||
modes: ['code', 'text', 'tree', 'preview'],
|
||||
onValidationError: function(errors) {
|
||||
console.error('onValidationError', errors);
|
||||
const outputEL = document.getElementById('onValidationOutput')
|
||||
outputEL.innerHTML = '<code>onValidationError</code> was called with ' + errors.length + ' error' + (errors.length > 1 ? 's' : '') + ' <br> ' +
|
||||
'open the browser console to see the error objects';
|
||||
},
|
||||
onValidate: function (json) {
|
||||
var errors = [];
|
||||
if(!isNaN(json.age) && json.age < 30) {
|
||||
errors.push({ path: ['age'], message: 'Member age must be 30 or higher' });
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
};
|
||||
|
||||
// create the editor
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var editor = new JSONEditor(container, options, json);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,123 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>JSONEditor | Custom query language</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
<script src="https://unpkg.com/lodash@4.17.15/lodash.min.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
p {
|
||||
max-width: 500px;
|
||||
font-family: sans-serif;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 11pt;
|
||||
background: #e5e5e5;
|
||||
}
|
||||
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This demo shows how to configure a custom query language.
|
||||
Click on the "Transform" button and try it out.
|
||||
</p>
|
||||
<p>
|
||||
This basic example uses lodash functions <code>filter</code>, <code>sort</code>, and <code>pick</code>,
|
||||
but you can run any JavaScript code.
|
||||
</p>
|
||||
<p class="warning">
|
||||
WARNING: this example uses <code>new Function()</code> which can be dangerous when executed with arbitrary code.
|
||||
Don't use it in production.
|
||||
</p>
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const options = {
|
||||
createQuery: function (json, queryOptions) {
|
||||
console.log('createQuery', queryOptions)
|
||||
|
||||
const { filter, sort, projection } = queryOptions
|
||||
let query = 'data'
|
||||
|
||||
if (filter) {
|
||||
// Note that the comparisons embrace type coercion,
|
||||
// so a filter value like '5' (text) will match numbers like 5 too.
|
||||
const getActualValue = filter.field !== '@'
|
||||
? `item => _.get(item, '${filter.field}')`
|
||||
: `item => item`
|
||||
query = `_.filter(${query}, ${getActualValue} ${filter.relation} '${filter.value}')`
|
||||
}
|
||||
|
||||
if (sort) {
|
||||
// The '@' field name is a special case,
|
||||
// which means that the field itself is selected.
|
||||
// For example when we have an array containing numbers.
|
||||
query = sort.field !== '@'
|
||||
? `_.orderBy(${query}, '${sort.field}', '${sort.direction}')`
|
||||
: `_.sortBy(${query}, '${sort.direction}')`
|
||||
}
|
||||
|
||||
if (projection) {
|
||||
// It is possible to make a util function "pickFlat"
|
||||
// and use that when building the query to make it more readable.
|
||||
if (projection.fields.length > 1) {
|
||||
const fields = projection.fields.map(field => {
|
||||
const name = _.last(field.split('.'))
|
||||
return ` '${name}': _.get(item, '${field}')`
|
||||
})
|
||||
query = `_.map(${query}, item => ({\n${fields.join(',\n')}})\n)`
|
||||
} else {
|
||||
const field = projection.fields[0]
|
||||
query = `_.map(${query}, item => _.get(item, '${field}'))`
|
||||
}
|
||||
}
|
||||
|
||||
return query
|
||||
},
|
||||
executeQuery: function (json, query) {
|
||||
console.log('executeQuery', query)
|
||||
|
||||
// WARNING: Using new Function() with arbitrary input can be dangerous! Be careful.
|
||||
const execute = new Function('data', 'return ' + query)
|
||||
|
||||
return execute(json)
|
||||
},
|
||||
queryDescription: 'Enter a JavaScript query to filter, sort, or transform the JSON data.<br/>' +
|
||||
'The <a href="https://lodash.com/" target="_blank">Lodash</a> library is available via <code>_</code> to facilitate this.'
|
||||
}
|
||||
const json = []
|
||||
for (let i = 0; i < 100; i++) {
|
||||
var longitude = 4 + i / 100
|
||||
var latitude = 51 + i / 100
|
||||
|
||||
json.push({
|
||||
name: 'Item ' + i,
|
||||
id: String(i),
|
||||
index: i,
|
||||
time: new Date().toISOString(),
|
||||
location: {
|
||||
latitude: longitude,
|
||||
longitude: latitude,
|
||||
coordinates: [longitude, latitude]
|
||||
},
|
||||
random: Math.random()
|
||||
})
|
||||
}
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,89 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>JSONEditor | New window</title>
|
||||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<button id="openNewEditor">Open Editor in New Window</button>
|
||||
<button id="setJSON">Set JSON</button>
|
||||
<button id="getJSON">Get JSON</button>
|
||||
</p>
|
||||
|
||||
<script>
|
||||
let editor
|
||||
|
||||
function openNewEditor() {
|
||||
const child = window.open("", "_blank", "width=400,height=400")
|
||||
child.document.title = 'JSONEditor | New window'
|
||||
child.onunload = function () {
|
||||
editor = undefined
|
||||
}
|
||||
|
||||
// make the necessary styles available within the child window
|
||||
// for JSONEditor
|
||||
const baseUrl = window.location.href.slice(0, window.location.href.lastIndexOf('/'))
|
||||
const jsonEditorStyles = child.document.createElement("link")
|
||||
jsonEditorStyles.setAttribute("href", baseUrl + "/../dist/jsoneditor.css")
|
||||
jsonEditorStyles.setAttribute("rel", "stylesheet")
|
||||
child.document.head.append(jsonEditorStyles)
|
||||
// for vanilla-picker
|
||||
const colorPickerStyles = JSONEditor.VanillaPicker.StyleElement.cloneNode(true)
|
||||
child.document.head.append(colorPickerStyles)
|
||||
|
||||
const container = child.document.createElement("div")
|
||||
child.document.body.append(container)
|
||||
|
||||
// create the editor
|
||||
const options = {
|
||||
// Show sort and transform modals in the child window, not the parent.
|
||||
modalAnchor: child.document.body
|
||||
}
|
||||
editor = new JSONEditor(container, options)
|
||||
}
|
||||
|
||||
// create a new window
|
||||
document.getElementById('openNewEditor').onclick = openNewEditor
|
||||
|
||||
// set json
|
||||
document.getElementById('setJSON').onclick = function () {
|
||||
if (!editor) {
|
||||
openNewEditor()
|
||||
}
|
||||
const json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'color': '#82b92c',
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'time': 1575599819000,
|
||||
'string': 'Hello World'
|
||||
}
|
||||
editor.set(json)
|
||||
}
|
||||
|
||||
// get json
|
||||
document.getElementById('getJSON').onclick = function () {
|
||||
if (!editor) {
|
||||
alert('No editor is open')
|
||||
} else {
|
||||
const json = editor.get()
|
||||
alert(JSON.stringify(json, null, 2))
|
||||
}
|
||||
}
|
||||
</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;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
SKIP_PREFLIGHT_CHECK=true
|
|
@ -1,21 +0,0 @@
|
|||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
|
@ -1,21 +0,0 @@
|
|||
# JSONEditor React advanced demo
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
|
||||
|
||||
## Install
|
||||
|
||||
Install dependencies once:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
To run the demo:
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
This will open a development server at http://localhost:3000
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"name": "react_advanced_demo",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"jsoneditor": "latest",
|
||||
"lodash": "4.17.15",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-scripts": "3.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
.app .contents {
|
||||
width: 500px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.app .contents .mode {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.app .contents .code {
|
||||
background: #f5f5f5;
|
||||
overflow: auto;
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import JSONEditorReact from './JSONEditorReact';
|
||||
import './App.css';
|
||||
|
||||
const schema = {
|
||||
title: 'Example Schema',
|
||||
type: 'object',
|
||||
properties: {
|
||||
array: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'number'
|
||||
}
|
||||
},
|
||||
boolean: {
|
||||
type: 'boolean'
|
||||
},
|
||||
number: {
|
||||
type: 'number'
|
||||
}
|
||||
},
|
||||
required: ['array', 'string', 'boolean']
|
||||
};
|
||||
|
||||
const json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 'four',
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
};
|
||||
|
||||
const modes = ['tree', 'form', 'view', 'code', 'text'];
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
schema,
|
||||
text: JSON.stringify(json, null, 2),
|
||||
mode: 'tree'
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="app">
|
||||
<h1>JSONEditor React advanced demo</h1>
|
||||
<div className="contents">
|
||||
<div className="mode">
|
||||
mode: <select value={this.state.mode} onChange={this.onModeChangeSelect}>
|
||||
{
|
||||
modes.map(mode => <option key={mode} value={mode}>{mode}</option>)
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<JSONEditorReact
|
||||
schema={this.state.schema}
|
||||
text={this.state.text}
|
||||
mode={this.state.mode}
|
||||
modes={modes}
|
||||
indentation={4}
|
||||
onChangeText={this.onChangeText}
|
||||
onModeChange={this.onModeChange}
|
||||
/>
|
||||
<div className="code">
|
||||
<pre>
|
||||
<code>
|
||||
{this.state.text}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeText = (text) => {
|
||||
this.setState({ text });
|
||||
};
|
||||
|
||||
onModeChangeSelect = (event) => {
|
||||
this.setState({ mode: event.target.value });
|
||||
};
|
||||
|
||||
onModeChange = (mode) => {
|
||||
this.setState({ mode });
|
||||
};
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
|
@ -1,4 +0,0 @@
|
|||
.jsoneditor-react-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
import React, {Component} from 'react';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import JSONEditor from 'jsoneditor';
|
||||
import 'jsoneditor/dist/jsoneditor.css';
|
||||
|
||||
import './JSONEditorReact.css';
|
||||
|
||||
export default class JSONEditorReact extends Component {
|
||||
componentDidMount () {
|
||||
// copy all properties into options for the editor
|
||||
// (except the properties for the JSONEditorReact component itself)
|
||||
const options = Object.assign({}, this.props);
|
||||
delete options.json;
|
||||
delete options.text;
|
||||
|
||||
this.jsoneditor = new JSONEditor(this.container, options);
|
||||
|
||||
if ('json' in this.props) {
|
||||
this.jsoneditor.set(this.props.json);
|
||||
}
|
||||
if ('text' in this.props) {
|
||||
this.jsoneditor.setText(this.props.text);
|
||||
}
|
||||
this.schema = cloneDeep(this.props.schema);
|
||||
this.schemaRefs = cloneDeep(this.props.schemaRefs);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if ('json' in this.props) {
|
||||
this.jsoneditor.update(this.props.json);
|
||||
}
|
||||
|
||||
if ('text' in this.props) {
|
||||
this.jsoneditor.updateText(this.props.text);
|
||||
}
|
||||
|
||||
if ('mode' in this.props) {
|
||||
this.jsoneditor.setMode(this.props.mode);
|
||||
}
|
||||
|
||||
// store a clone of the schema to keep track on when it actually changes.
|
||||
// (When using a PureComponent all of this would be redundant)
|
||||
const schemaChanged = !isEqual(this.props.schema, this.schema);
|
||||
const schemaRefsChanged = !isEqual(this.props.schemaRefs, this.schemaRefs);
|
||||
if (schemaChanged || schemaRefsChanged) {
|
||||
this.schema = cloneDeep(this.props.schema);
|
||||
this.schemaRefs = cloneDeep(this.props.schemaRefs);
|
||||
this.jsoneditor.setSchema(this.props.schema, this.props.schemaRefs);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.jsoneditor) {
|
||||
this.jsoneditor.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="jsoneditor-react-container" ref={elem => this.container = elem} />
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
|
@ -1 +0,0 @@
|
|||
SKIP_PREFLIGHT_CHECK=true
|
|
@ -1,21 +1,17 @@
|
|||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.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
|
@ -2,28 +2,18 @@
|
|||
"name": "react_demo",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"react-scripts": "1.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsoneditor": "latest",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-scripts": "3.4.1"
|
||||
"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"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<!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">
|
||||
<!--
|
||||
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.
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
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 | React demo</title>
|
||||
<title>React demo | JSONEditor</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>
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
.app .contents {
|
||||
width: 500px;
|
||||
height: 400px;
|
||||
.app {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.app .contents .menu {
|
||||
padding: 10px 0;
|
||||
.app h1 {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.app .contents .code {
|
||||
background: #f5f5f5;
|
||||
}
|
|
@ -1,57 +1,47 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { Component } from 'react'
|
||||
import './App.css'
|
||||
|
||||
import JSONEditorDemo from './JSONEditorDemo';
|
||||
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: {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
};
|
||||
json
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="app">
|
||||
<h1>JSONEditor React demo</h1>
|
||||
<div className="contents">
|
||||
<div className="menu">
|
||||
<button onClick={this.updateTime}>
|
||||
Create/update a field "time"
|
||||
</button>
|
||||
</div>
|
||||
<JSONEditorDemo
|
||||
<div className="app">
|
||||
<h1>JSONEditor React demo</h1>
|
||||
<JSONEditor
|
||||
mode="tree"
|
||||
modes={['text', 'code', 'tree', 'form', 'view']}
|
||||
json={this.state.json}
|
||||
onChangeJSON={this.onChangeJSON}
|
||||
onChange={this.onChange}
|
||||
onChangeText={this.onChangeText}
|
||||
/>
|
||||
<div className="code">
|
||||
<pre>
|
||||
<code>
|
||||
{JSON.stringify(this.state.json, null, 2)}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
onChangeJSON = (json) => {
|
||||
this.setState({ json });
|
||||
};
|
||||
onChange = (json) => {
|
||||
console.log('onChange', json)
|
||||
}
|
||||
|
||||
updateTime = () => {
|
||||
const time = new Date().toISOString();
|
||||
onChangeText = (text) => {
|
||||
console.log('onChangeText', text)
|
||||
|
||||
this.setState({
|
||||
json: Object.assign({}, this.state.json, { time })
|
||||
})
|
||||
};
|
||||
this.setState({ text })
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
|
@ -1,4 +0,0 @@
|
|||
.jsoneditor-react-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import React, {Component} from 'react';
|
||||
|
||||
import JSONEditor from 'jsoneditor';
|
||||
import 'jsoneditor/dist/jsoneditor.css';
|
||||
|
||||
import './JSONEditorDemo.css';
|
||||
|
||||
export default class JSONEditorDemo extends Component {
|
||||
componentDidMount () {
|
||||
const options = {
|
||||
mode: 'tree',
|
||||
onChangeJSON: this.props.onChangeJSON
|
||||
};
|
||||
|
||||
this.jsoneditor = new JSONEditor(this.container, options);
|
||||
this.jsoneditor.set(this.props.json);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.jsoneditor) {
|
||||
this.jsoneditor.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.jsoneditor.update(this.props.json);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="jsoneditor-react-container" ref={elem => this.container = elem} />
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
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="https://requirejs.org/docs/release/2.3.6/minified/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,25 +1,26 @@
|
|||
const module = '../../../dist/jsoneditor'
|
||||
require([module], function (JSONEditor) {
|
||||
var module = '../../../dist/jsoneditor'
|
||||
|
||||
require([module], function (jsoneditor) {
|
||||
// create the editor
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const editor = new JSONEditor(container)
|
||||
var container = document.getElementById('jsoneditor')
|
||||
var editor = jsoneditor(container)
|
||||
|
||||
// set json
|
||||
document.getElementById('setJSON').onclick = function () {
|
||||
const json = {
|
||||
array: [1, 2, 3],
|
||||
boolean: true,
|
||||
null: null,
|
||||
number: 123,
|
||||
object: { a: 'b', c: 'd' },
|
||||
string: 'Hello World'
|
||||
var json = {
|
||||
'array': [1, 2, 3],
|
||||
'boolean': true,
|
||||
'null': null,
|
||||
'number': 123,
|
||||
'object': {'a': 'b', 'c': 'd'},
|
||||
'string': 'Hello World'
|
||||
}
|
||||
editor.set(json)
|
||||
}
|
||||
|
||||
// get json
|
||||
document.getElementById('getJSON').onclick = function () {
|
||||
const json = editor.get()
|
||||
window.alert(JSON.stringify(json, null, 2))
|
||||
var json = editor.get()
|
||||
alert(JSON.stringify(json, null, 2))
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"groups": {
|
||||
"default": {
|
||||
"packages": [
|
||||
"examples/react_advanced_demo/package.json",
|
||||
"examples/react_demo/package.json",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
243
gulpfile.js
243
gulpfile.js
|
@ -1,243 +0,0 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const gulp = require('gulp')
|
||||
const log = require('fancy-log')
|
||||
const format = require('date-format')
|
||||
const concatCss = require('gulp-concat-css')
|
||||
const minifyCSS = require('gulp-clean-css')
|
||||
const sass = require('gulp-sass')
|
||||
const mkdirp = require('mkdirp')
|
||||
const webpack = require('webpack')
|
||||
const uglify = require('uglify-js')
|
||||
const btoa = require('btoa')
|
||||
|
||||
const NAME = 'jsoneditor'
|
||||
const NAME_MINIMALIST = 'jsoneditor-minimalist'
|
||||
const ENTRY = './src/js/JSONEditor.js'
|
||||
const HEADER = './src/js/header.js'
|
||||
const IMAGE = './src/scss/img/jsoneditor-icons.svg'
|
||||
const DOCS = './src/docs/*'
|
||||
const DIST = path.join(__dirname, 'dist')
|
||||
|
||||
// generate banner with today's date and correct version
|
||||
function createBanner () {
|
||||
const today = format.asString('yyyy-MM-dd', new Date()) // today, formatted as yyyy-MM-dd
|
||||
const version = require('./package.json').version // math.js version
|
||||
|
||||
return String(fs.readFileSync(HEADER))
|
||||
.replace('@@date', today)
|
||||
.replace('@@version', version)
|
||||
}
|
||||
|
||||
const bannerPlugin = new webpack.BannerPlugin({
|
||||
banner: createBanner(),
|
||||
entryOnly: true,
|
||||
raw: true
|
||||
})
|
||||
|
||||
const webpackConfigModule = {
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: ['source-map-loader'],
|
||||
enforce: 'pre'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// create a single instance of the compiler to allow caching
|
||||
const compiler = webpack({
|
||||
entry: ENTRY,
|
||||
output: {
|
||||
library: 'JSONEditor',
|
||||
libraryTarget: 'umd',
|
||||
path: DIST,
|
||||
filename: NAME + '.js'
|
||||
},
|
||||
plugins: [bannerPlugin],
|
||||
optimization: {
|
||||
// We no not want to minimize our code.
|
||||
minimize: false
|
||||
},
|
||||
module: webpackConfigModule,
|
||||
resolve: {
|
||||
extensions: ['.js'],
|
||||
mainFields: ['main'] // pick ES5 version of vanilla-picker
|
||||
},
|
||||
cache: true
|
||||
})
|
||||
|
||||
// create a single instance of the compiler to allow caching
|
||||
const compilerMinimalist = webpack({
|
||||
entry: ENTRY,
|
||||
output: {
|
||||
library: 'JSONEditor',
|
||||
libraryTarget: 'umd',
|
||||
path: DIST,
|
||||
filename: NAME_MINIMALIST + '.js'
|
||||
},
|
||||
module: webpackConfigModule,
|
||||
plugins: [
|
||||
bannerPlugin,
|
||||
new webpack.IgnorePlugin(new RegExp('^ace-builds')),
|
||||
new webpack.IgnorePlugin(new RegExp('worker-json-data-url')),
|
||||
new webpack.IgnorePlugin(new RegExp('^ajv')),
|
||||
new webpack.IgnorePlugin(new RegExp('^vanilla-picker'))
|
||||
],
|
||||
optimization: {
|
||||
// We no not want to minimize our code.
|
||||
minimize: false
|
||||
},
|
||||
cache: true
|
||||
})
|
||||
|
||||
function minify (name) {
|
||||
const code = String(fs.readFileSync(DIST + '/' + name + '.js'))
|
||||
const result = uglify.minify(code, {
|
||||
sourceMap: {
|
||||
url: name + '.map'
|
||||
},
|
||||
output: {
|
||||
comments: /@license/,
|
||||
max_line_len: 64000 // extra large because we have embedded code for workers
|
||||
}
|
||||
})
|
||||
|
||||
if (result.error) {
|
||||
throw result.error
|
||||
}
|
||||
|
||||
const fileMin = DIST + '/' + name + '.min.js'
|
||||
const fileMap = DIST + '/' + name + '.map'
|
||||
|
||||
fs.writeFileSync(fileMin, result.code)
|
||||
fs.writeFileSync(fileMap, result.map)
|
||||
|
||||
log('Minified ' + fileMin)
|
||||
log('Mapped ' + fileMap)
|
||||
}
|
||||
|
||||
// make dist folder structure
|
||||
gulp.task('mkdir', function (done) {
|
||||
mkdirp.sync(DIST)
|
||||
mkdirp.sync(DIST + '/img')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// Create an embedded version of the json worker code: a data url
|
||||
gulp.task('embed-json-worker', function (done) {
|
||||
const workerBundleFile = './node_modules/ace-builds/src-noconflict/worker-json.js'
|
||||
const workerEmbeddedFile = './src/js/generated/worker-json-data-url.js'
|
||||
const workerScript = String(fs.readFileSync(workerBundleFile))
|
||||
|
||||
const workerDataUrl = 'data:application/javascript;base64,' + btoa(workerScript)
|
||||
|
||||
fs.writeFileSync(workerEmbeddedFile, 'module.exports = \'' + workerDataUrl + '\'\n')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// bundle javascript
|
||||
gulp.task('bundle', 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) {
|
||||
log(err)
|
||||
}
|
||||
|
||||
log('bundled ' + NAME + '.js')
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
// bundle minimalist version of javascript
|
||||
gulp.task('bundle-minimalist', 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) {
|
||||
log(err)
|
||||
}
|
||||
|
||||
log('bundled ' + NAME_MINIMALIST + '.js')
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
// bundle css
|
||||
gulp.task('bundle-css', function (done) {
|
||||
gulp
|
||||
.src(['src/scss/jsoneditor.scss'])
|
||||
.pipe(
|
||||
sass({
|
||||
// importer: tildeImporter
|
||||
})
|
||||
)
|
||||
.pipe(concatCss(NAME + '.css'))
|
||||
.pipe(gulp.dest(DIST))
|
||||
.pipe(concatCss(NAME + '.min.css'))
|
||||
.pipe(minifyCSS())
|
||||
.pipe(gulp.dest(DIST))
|
||||
done()
|
||||
})
|
||||
|
||||
// create a folder img and copy the icons
|
||||
gulp.task('copy-img', function (done) {
|
||||
gulp.src(IMAGE).pipe(gulp.dest(DIST + '/img'))
|
||||
log('Copied images')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// create a folder img and copy the icons
|
||||
gulp.task('copy-docs', function (done) {
|
||||
gulp.src(DOCS).pipe(gulp.dest(DIST))
|
||||
log('Copied doc')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
gulp.task('minify', function (done) {
|
||||
minify(NAME)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
gulp.task('minify-minimalist', function (done) {
|
||||
minify(NAME_MINIMALIST)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// 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', gulp.series('bundle', 'bundle-css', 'copy-img', function () {
|
||||
gulp.watch(['src/**/*'], gulp.series('bundle', 'bundle-css', 'copy-img'))
|
||||
}))
|
||||
|
||||
// The default task (called when you run `gulp`)
|
||||
gulp.task('default', gulp.series(
|
||||
'mkdir',
|
||||
'embed-json-worker',
|
||||
gulp.parallel(
|
||||
'copy-img',
|
||||
'copy-docs',
|
||||
'bundle-css',
|
||||
gulp.series('bundle', 'minify'),
|
||||
gulp.series('bundle-minimalist', 'minify-minimalist')
|
||||
)
|
||||
))
|
Binary file not shown.
Before Width: | Height: | Size: 8.3 KiB |
12954
misc/browserstack.svg
12954
misc/browserstack.svg
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 950 KiB |
|
@ -7,10 +7,6 @@ This document describes the steps required to publish a new version of jsonedito
|
|||
|
||||
Update the version number in package.json.
|
||||
|
||||
Update package-lock.json:
|
||||
|
||||
npm install
|
||||
|
||||
|
||||
## Update history
|
||||
|
||||
|
@ -37,8 +33,13 @@ correct date and version number in the header.
|
|||
|
||||
## Test
|
||||
|
||||
Test whether the npm library is ok by opening some examples, and check whether
|
||||
the files under `dists` are created and have contents.
|
||||
Test whether the npm library is ok by installing it locally:
|
||||
|
||||
cd ../tmp-folder
|
||||
npm install ./path/to/jsoneditor
|
||||
|
||||
Check whether the examples in the library work ok, and whether the necessary
|
||||
files are included.
|
||||
|
||||
|
||||
## Commit
|
||||
|
@ -59,6 +60,9 @@ Publish to npm:
|
|||
|
||||
npm publish
|
||||
|
||||
Publish at cdnjs: test after 30 to 60 minutes whether the new version is
|
||||
published at cdnjs (should auto update).
|
||||
|
||||
|
||||
## Test published library
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
117
package.json
117
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "jsoneditor",
|
||||
"version": "9.1.1",
|
||||
"main": "./dist/jsoneditor.min.js",
|
||||
"version": "6.0.0-BETA",
|
||||
"main": "./index.js",
|
||||
"description": "A web-based tool to view, edit, format, and validate JSON",
|
||||
"tags": [
|
||||
"json",
|
||||
|
@ -10,67 +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",
|
||||
"minify": "gulp minify",
|
||||
"start": "gulp watch",
|
||||
"test": "mocha test --require @babel/register",
|
||||
"lint": "standard --env=mocha",
|
||||
"prepublishOnly": "npm test && npm run build"
|
||||
},
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.4.12",
|
||||
"ajv": "^6.12.5",
|
||||
"javascript-natural-sort": "^0.7.1",
|
||||
"jmespath": "^0.15.0",
|
||||
"json-source-map": "^0.6.1",
|
||||
"mobius1-selectr": "^2.4.13",
|
||||
"picomodal": "^3.0.0",
|
||||
"vanilla-picker": "^2.10.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": {
|
||||
"@babel/core": "7.11.6",
|
||||
"@babel/preset-env": "7.11.5",
|
||||
"@babel/register": "7.11.5",
|
||||
"babel-loader": "8.1.0",
|
||||
"btoa": "1.2.1",
|
||||
"date-format": "3.0.0",
|
||||
"fancy-log": "1.3.3",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-clean-css": "4.3.0",
|
||||
"gulp-concat-css": "3.1.0",
|
||||
"gulp-sass": "4.1.0",
|
||||
"jsdom": "16.4.0",
|
||||
"json-loader": "0.5.7",
|
||||
"mkdirp": "1.0.4",
|
||||
"mocha": "8.1.3",
|
||||
"source-map-loader": "1.1.0",
|
||||
"standard": "14.3.4",
|
||||
"uglify-js": "3.11.1",
|
||||
"webpack": "4.44.2"
|
||||
"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"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"docs",
|
||||
"examples",
|
||||
"src",
|
||||
"HISTORY.md",
|
||||
"index.js",
|
||||
"LICENSE",
|
||||
"NOTICE",
|
||||
"README.md"
|
||||
],
|
||||
"standard": {
|
||||
"ignore": [
|
||||
"src/js/assets",
|
||||
"examples/react*"
|
||||
"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 |
|
@ -4,6 +4,12 @@
|
|||
<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.
|
||||
|
@ -13,7 +19,7 @@
|
|||
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 | React advanced demo</title>
|
||||
<title>JSONEditor demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
|
@ -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')
|
|
@ -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>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,44 +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.
|
||||
- `vanilla-picker`, used as color picker.
|
||||
|
||||
This reduces the the size of the minified and gzipped JavaScript file
|
||||
from about 210 kB to about 70 kB (one third).
|
||||
|
||||
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.
|
||||
- You don't need the color picker, or want to provide your own
|
||||
color picker using `onColorPicker`.
|
||||
|
||||
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,435 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
import { createAbsoluteAnchor } from './createAbsoluteAnchor'
|
||||
import { addClassName, getSelection, removeClassName, setSelection } from './util'
|
||||
import { translate } from './i18n'
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export class ContextMenu {
|
||||
constructor (items, options) {
|
||||
this.dom = {}
|
||||
|
||||
const me = this
|
||||
const 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
|
||||
const root = document.createElement('div')
|
||||
root.className = 'jsoneditor-contextmenu-root'
|
||||
dom.root = root
|
||||
|
||||
// create a container element
|
||||
const menu = document.createElement('div')
|
||||
menu.className = 'jsoneditor-contextmenu'
|
||||
dom.menu = menu
|
||||
root.appendChild(menu)
|
||||
|
||||
// create a list to hold the menu items
|
||||
const 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
|
||||
const focusButton = document.createElement('button')
|
||||
focusButton.type = 'button'
|
||||
dom.focusButton = focusButton
|
||||
const 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(item => {
|
||||
if (item.type === 'separator') {
|
||||
// create a separator
|
||||
const separator = document.createElement('div')
|
||||
separator.className = 'jsoneditor-separator'
|
||||
const li = document.createElement('li')
|
||||
li.appendChild(separator)
|
||||
list.appendChild(li)
|
||||
} else {
|
||||
const domItem = {}
|
||||
|
||||
// create a menu item
|
||||
const li = document.createElement('li')
|
||||
list.appendChild(li)
|
||||
|
||||
// create a button in the menu item
|
||||
const button = document.createElement('button')
|
||||
button.type = 'button'
|
||||
button.className = item.className
|
||||
domItem.button = button
|
||||
if (item.title) {
|
||||
button.title = item.title
|
||||
}
|
||||
if (item.click) {
|
||||
button.onclick = 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
|
||||
const divIcon = document.createElement('div')
|
||||
divIcon.className = 'jsoneditor-icon'
|
||||
button.appendChild(divIcon)
|
||||
const divText = document.createElement('div')
|
||||
divText.className = 'jsoneditor-text' +
|
||||
(item.click ? '' : ' jsoneditor-right-margin')
|
||||
divText.appendChild(document.createTextNode(item.text))
|
||||
button.appendChild(divText)
|
||||
|
||||
let buttonSubmenu
|
||||
if (item.click) {
|
||||
// submenu and a button with a click handler
|
||||
button.className += ' jsoneditor-default'
|
||||
|
||||
const buttonExpand = document.createElement('button')
|
||||
buttonExpand.type = 'button'
|
||||
domItem.buttonExpand = buttonExpand
|
||||
buttonExpand.className = 'jsoneditor-expand'
|
||||
const buttonExpandInner = document.createElement('div')
|
||||
buttonExpandInner.className = 'jsoneditor-expand'
|
||||
buttonExpand.appendChild(buttonExpandInner)
|
||||
li.appendChild(buttonExpand)
|
||||
if (item.submenuTitle) {
|
||||
buttonExpand.title = item.submenuTitle
|
||||
}
|
||||
|
||||
buttonSubmenu = buttonExpand
|
||||
} else {
|
||||
// submenu and a button without a click handler
|
||||
const divExpand = document.createElement('div')
|
||||
divExpand.className = 'jsoneditor-expand'
|
||||
button.appendChild(divExpand)
|
||||
|
||||
buttonSubmenu = button
|
||||
}
|
||||
|
||||
// attach a handler to expand/collapse the submenu
|
||||
buttonSubmenu.onclick = event => {
|
||||
event.preventDefault()
|
||||
me._onExpandItem(domItem)
|
||||
buttonSubmenu.focus()
|
||||
}
|
||||
|
||||
// create the submenu
|
||||
const domSubItems = []
|
||||
domItem.subItems = domSubItems
|
||||
const 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
|
||||
const icon = document.createElement('div')
|
||||
icon.className = 'jsoneditor-icon'
|
||||
button.appendChild(icon)
|
||||
|
||||
const text = document.createElement('div')
|
||||
text.className = 'jsoneditor-text'
|
||||
text.appendChild(document.createTextNode(translate(item.text)))
|
||||
button.appendChild(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(item => {
|
||||
const 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
|
||||
*/
|
||||
_getVisibleButtons () {
|
||||
const buttons = []
|
||||
const me = this
|
||||
this.dom.items.forEach(item => {
|
||||
buttons.push(item.button)
|
||||
if (item.buttonExpand) {
|
||||
buttons.push(item.buttonExpand)
|
||||
}
|
||||
if (item.subItems && item === me.expandedItem) {
|
||||
item.subItems.forEach(subItem => {
|
||||
buttons.push(subItem.button)
|
||||
if (subItem.buttonExpand) {
|
||||
buttons.push(subItem.buttonExpand)
|
||||
}
|
||||
// TODO: change to fully recursive method
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return buttons
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the menu to an anchor
|
||||
* @param {HTMLElement} anchor Anchor where the menu will be attached as sibling.
|
||||
* @param {HTMLElement} frame The root of the JSONEditor window
|
||||
* @param {Boolean=} ignoreParent ignore anchor parent in regard to the calculation of the position, needed when the parent position is absolute
|
||||
*/
|
||||
show (anchor, frame, ignoreParent) {
|
||||
this.hide()
|
||||
|
||||
// determine whether to display the menu below or above the anchor
|
||||
let showBelow = true
|
||||
const parent = anchor.parentNode
|
||||
const anchorRect = anchor.getBoundingClientRect()
|
||||
const parentRect = parent.getBoundingClientRect()
|
||||
const frameRect = frame.getBoundingClientRect()
|
||||
|
||||
const me = this
|
||||
this.dom.absoluteAnchor = createAbsoluteAnchor(anchor, frame, () => {
|
||||
me.hide()
|
||||
})
|
||||
|
||||
if (anchorRect.bottom + this.maxHeight < frameRect.bottom) {
|
||||
// fits below -> show below
|
||||
} else if (anchorRect.top - this.maxHeight > frameRect.top) {
|
||||
// fits above -> show above
|
||||
showBelow = false
|
||||
} else {
|
||||
// doesn't fit above nor below -> show below
|
||||
}
|
||||
|
||||
const topGap = ignoreParent ? 0 : (anchorRect.top - parentRect.top)
|
||||
|
||||
// position the menu
|
||||
if (showBelow) {
|
||||
// display the menu below the anchor
|
||||
const anchorHeight = anchor.offsetHeight
|
||||
this.dom.menu.style.left = '0'
|
||||
this.dom.menu.style.top = topGap + anchorHeight + 'px'
|
||||
this.dom.menu.style.bottom = ''
|
||||
} else {
|
||||
// display the menu above the anchor
|
||||
this.dom.menu.style.left = '0'
|
||||
this.dom.menu.style.top = ''
|
||||
this.dom.menu.style.bottom = '0px'
|
||||
}
|
||||
|
||||
// attach the menu to the temporary, absolute anchor
|
||||
// parent.insertBefore(this.dom.root, anchor);
|
||||
this.dom.absoluteAnchor.appendChild(this.dom.root)
|
||||
|
||||
// move focus to the first button in the context menu
|
||||
this.selection = getSelection()
|
||||
this.anchor = anchor
|
||||
setTimeout(() => {
|
||||
me.dom.focusButton.focus()
|
||||
}, 0)
|
||||
|
||||
if (ContextMenu.visibleMenu) {
|
||||
ContextMenu.visibleMenu.hide()
|
||||
}
|
||||
ContextMenu.visibleMenu = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the context menu if visible
|
||||
*/
|
||||
hide () {
|
||||
// remove temporary absolutely positioned anchor
|
||||
if (this.dom.absoluteAnchor) {
|
||||
this.dom.absoluteAnchor.destroy()
|
||||
delete this.dom.absoluteAnchor
|
||||
}
|
||||
|
||||
// remove the menu from the DOM
|
||||
if (this.dom.root.parentNode) {
|
||||
this.dom.root.parentNode.removeChild(this.dom.root)
|
||||
if (this.onClose) {
|
||||
this.onClose()
|
||||
}
|
||||
}
|
||||
|
||||
if (ContextMenu.visibleMenu === this) {
|
||||
ContextMenu.visibleMenu = undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a submenu
|
||||
* Any currently expanded submenu will be hided.
|
||||
* @param {Object} domItem
|
||||
* @private
|
||||
*/
|
||||
_onExpandItem (domItem) {
|
||||
const me = this
|
||||
const alreadyVisible = (domItem === this.expandedItem)
|
||||
|
||||
// hide the currently visible submenu
|
||||
const expandedItem = this.expandedItem
|
||||
if (expandedItem) {
|
||||
// var ul = expandedItem.ul;
|
||||
expandedItem.ul.style.height = '0'
|
||||
expandedItem.ul.style.padding = ''
|
||||
setTimeout(() => {
|
||||
if (me.expandedItem !== expandedItem) {
|
||||
expandedItem.ul.style.display = ''
|
||||
removeClassName(expandedItem.ul.parentNode, 'jsoneditor-selected')
|
||||
}
|
||||
}, 300) // timeout duration must match the css transition duration
|
||||
this.expandedItem = undefined
|
||||
}
|
||||
|
||||
if (!alreadyVisible) {
|
||||
const ul = domItem.ul
|
||||
ul.style.display = 'block'
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
ul.clientHeight // force a reflow in Firefox
|
||||
setTimeout(() => {
|
||||
if (me.expandedItem === domItem) {
|
||||
let childsHeight = 0
|
||||
for (let i = 0; i < ul.childNodes.length; i++) {
|
||||
childsHeight += ul.childNodes[i].clientHeight
|
||||
}
|
||||
ul.style.height = childsHeight + 'px'
|
||||
ul.style.padding = '5px 10px'
|
||||
}
|
||||
}, 0)
|
||||
addClassName(ul.parentNode, 'jsoneditor-selected')
|
||||
this.expandedItem = domItem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle onkeydown event
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_onKeyDown (event) {
|
||||
const target = event.target
|
||||
const keynum = event.which
|
||||
let handled = false
|
||||
let buttons, targetIndex, prevButton, nextButton
|
||||
|
||||
if (keynum === 27) { // ESC
|
||||
// hide the menu on ESC key
|
||||
|
||||
// restore previous selection and focus
|
||||
if (this.selection) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// currently displayed context menu, a singleton. We may only have one visible context menu
|
||||
ContextMenu.visibleMenu = undefined
|
|
@ -1,194 +0,0 @@
|
|||
/**
|
||||
* Show errors and schema warnings in a clickable table view
|
||||
* @param {Object} config
|
||||
* @property {boolean} errorTableVisible
|
||||
* @property {function (boolean) : void} onToggleVisibility
|
||||
* @property {function (number)} [onFocusLine]
|
||||
* @property {function (number)} onChangeHeight
|
||||
* @constructor
|
||||
*/
|
||||
export class ErrorTable {
|
||||
constructor (config) {
|
||||
this.errorTableVisible = config.errorTableVisible
|
||||
this.onToggleVisibility = config.onToggleVisibility
|
||||
this.onFocusLine = config.onFocusLine || (() => {})
|
||||
this.onChangeHeight = config.onChangeHeight
|
||||
|
||||
this.dom = {}
|
||||
|
||||
const validationErrorsContainer = document.createElement('div')
|
||||
validationErrorsContainer.className = 'jsoneditor-validation-errors-container'
|
||||
this.dom.validationErrorsContainer = validationErrorsContainer
|
||||
|
||||
const additionalErrorsIndication = document.createElement('div')
|
||||
additionalErrorsIndication.style.display = 'none'
|
||||
additionalErrorsIndication.className = 'jsoneditor-additional-errors fadein'
|
||||
additionalErrorsIndication.textContent = 'Scroll for more \u25BF'
|
||||
this.dom.additionalErrorsIndication = additionalErrorsIndication
|
||||
validationErrorsContainer.appendChild(additionalErrorsIndication)
|
||||
|
||||
const validationErrorIcon = document.createElement('span')
|
||||
validationErrorIcon.className = 'jsoneditor-validation-error-icon'
|
||||
validationErrorIcon.style.display = 'none'
|
||||
this.dom.validationErrorIcon = validationErrorIcon
|
||||
|
||||
const validationErrorCount = document.createElement('span')
|
||||
validationErrorCount.className = 'jsoneditor-validation-error-count'
|
||||
validationErrorCount.style.display = 'none'
|
||||
this.dom.validationErrorCount = validationErrorCount
|
||||
|
||||
this.dom.parseErrorIndication = document.createElement('span')
|
||||
this.dom.parseErrorIndication.className = 'jsoneditor-parse-error-icon'
|
||||
this.dom.parseErrorIndication.style.display = 'none'
|
||||
}
|
||||
|
||||
getErrorTable () {
|
||||
return this.dom.validationErrorsContainer
|
||||
}
|
||||
|
||||
getErrorCounter () {
|
||||
return this.dom.validationErrorCount
|
||||
}
|
||||
|
||||
getWarningIcon () {
|
||||
return this.dom.validationErrorIcon
|
||||
}
|
||||
|
||||
getErrorIcon () {
|
||||
return this.dom.parseErrorIndication
|
||||
}
|
||||
|
||||
toggleTableVisibility () {
|
||||
this.errorTableVisible = !this.errorTableVisible
|
||||
this.onToggleVisibility(this.errorTableVisible)
|
||||
}
|
||||
|
||||
setErrors (errors, errorLocations) {
|
||||
// clear any previous errors
|
||||
if (this.dom.validationErrors) {
|
||||
this.dom.validationErrors.parentNode.removeChild(this.dom.validationErrors)
|
||||
this.dom.validationErrors = null
|
||||
this.dom.additionalErrorsIndication.style.display = 'none'
|
||||
}
|
||||
|
||||
// create the table with errors
|
||||
// keep default behavior for parse errors
|
||||
if (this.errorTableVisible && errors.length > 0) {
|
||||
const validationErrors = document.createElement('div')
|
||||
validationErrors.className = 'jsoneditor-validation-errors'
|
||||
|
||||
const table = document.createElement('table')
|
||||
table.className = 'jsoneditor-text-errors'
|
||||
validationErrors.appendChild(table)
|
||||
|
||||
const tbody = document.createElement('tbody')
|
||||
table.appendChild(tbody)
|
||||
|
||||
errors.forEach(error => {
|
||||
let line
|
||||
|
||||
if (!isNaN(error.line)) {
|
||||
line = error.line
|
||||
} else if (error.dataPath) {
|
||||
const errLoc = errorLocations.find(loc => loc.path === error.dataPath)
|
||||
if (errLoc) {
|
||||
line = errLoc.line + 1
|
||||
}
|
||||
}
|
||||
|
||||
const trEl = document.createElement('tr')
|
||||
trEl.className = !isNaN(line) ? 'jump-to-line' : ''
|
||||
if (error.type === 'error') {
|
||||
trEl.className += ' parse-error'
|
||||
} else {
|
||||
trEl.className += ' validation-error'
|
||||
}
|
||||
|
||||
const td1 = document.createElement('td')
|
||||
const button = document.createElement('button')
|
||||
button.className = 'jsoneditor-schema-error'
|
||||
td1.appendChild(button)
|
||||
trEl.appendChild(td1)
|
||||
|
||||
const td2 = document.createElement('td')
|
||||
td2.style = 'white-space: nowrap;'
|
||||
td2.textContent = (!isNaN(line) ? ('Ln ' + line) : '')
|
||||
trEl.appendChild(td2)
|
||||
|
||||
if (typeof error === 'string') {
|
||||
const td34 = document.createElement('td')
|
||||
td34.colSpan = 2
|
||||
const pre = document.createElement('pre')
|
||||
pre.appendChild(document.createTextNode(error))
|
||||
td34.appendChild(pre)
|
||||
trEl.appendChild(td34)
|
||||
} else {
|
||||
const td3 = document.createElement('td')
|
||||
td3.appendChild(document.createTextNode(error.dataPath || ''))
|
||||
trEl.appendChild(td3)
|
||||
|
||||
const td4 = document.createElement('td')
|
||||
const pre = document.createElement('pre')
|
||||
pre.appendChild(document.createTextNode(error.message))
|
||||
td4.appendChild(pre)
|
||||
trEl.appendChild(td4)
|
||||
}
|
||||
|
||||
trEl.onclick = () => {
|
||||
this.onFocusLine(line)
|
||||
}
|
||||
|
||||
tbody.appendChild(trEl)
|
||||
})
|
||||
|
||||
this.dom.validationErrors = validationErrors
|
||||
this.dom.validationErrorsContainer.appendChild(validationErrors)
|
||||
this.dom.additionalErrorsIndication.title = errors.length + ' errors total'
|
||||
|
||||
if (this.dom.validationErrorsContainer.clientHeight < this.dom.validationErrorsContainer.scrollHeight) {
|
||||
this.dom.additionalErrorsIndication.style.display = 'block'
|
||||
this.dom.validationErrorsContainer.onscroll = () => {
|
||||
this.dom.additionalErrorsIndication.style.display =
|
||||
(this.dom.validationErrorsContainer.clientHeight > 0 && this.dom.validationErrorsContainer.scrollTop === 0) ? 'block' : 'none'
|
||||
}
|
||||
} else {
|
||||
this.dom.validationErrorsContainer.onscroll = undefined
|
||||
}
|
||||
|
||||
const height = this.dom.validationErrorsContainer.clientHeight + (this.dom.statusBar ? this.dom.statusBar.clientHeight : 0)
|
||||
// this.content.style.marginBottom = (-height) + 'px';
|
||||
// this.content.style.paddingBottom = height + 'px';
|
||||
this.onChangeHeight(height)
|
||||
} else {
|
||||
this.onChangeHeight(0)
|
||||
}
|
||||
|
||||
// update the status bar
|
||||
const validationErrorsCount = errors.filter(error => error.type !== 'error').length
|
||||
if (validationErrorsCount > 0) {
|
||||
this.dom.validationErrorCount.style.display = 'inline'
|
||||
this.dom.validationErrorCount.innerText = validationErrorsCount
|
||||
this.dom.validationErrorCount.onclick = this.toggleTableVisibility.bind(this)
|
||||
|
||||
this.dom.validationErrorIcon.style.display = 'inline'
|
||||
this.dom.validationErrorIcon.title = validationErrorsCount + ' schema validation error(s) found'
|
||||
this.dom.validationErrorIcon.onclick = this.toggleTableVisibility.bind(this)
|
||||
} else {
|
||||
this.dom.validationErrorCount.style.display = 'none'
|
||||
this.dom.validationErrorIcon.style.display = 'none'
|
||||
}
|
||||
|
||||
// update the parse error icon
|
||||
const hasParseErrors = errors.some(error => error.type === 'error')
|
||||
if (hasParseErrors) {
|
||||
const line = errors[0].line
|
||||
this.dom.parseErrorIndication.style.display = 'block'
|
||||
this.dom.parseErrorIndication.title = !isNaN(line)
|
||||
? ('parse error on line ' + line)
|
||||
: 'parse error - check that the json is valid'
|
||||
this.dom.parseErrorIndication.onclick = this.toggleTableVisibility.bind(this)
|
||||
} else {
|
||||
this.dom.parseErrorIndication.style.display = 'none'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/**
|
||||
* @constructor FocusTracker
|
||||
* A custom focus tracker for a DOM element with complex internal DOM structure
|
||||
* @param {[Object]} config A set of configurations for the FocusTracker
|
||||
* {DOM Object} target * The DOM object to track (required)
|
||||
* {Function} onFocus onFocus callback
|
||||
* {Function} onBlur onBlur callback
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
export class FocusTracker {
|
||||
constructor (config) {
|
||||
this.target = config.target || null
|
||||
if (!this.target) {
|
||||
throw new Error('FocusTracker constructor called without a "target" to track.')
|
||||
}
|
||||
|
||||
this.onFocus = (typeof config.onFocus === 'function') ? config.onFocus : null
|
||||
this.onBlur = (typeof config.onBlur === 'function') ? config.onBlur : null
|
||||
this._onClick = this._onEvent.bind(this)
|
||||
this._onKeyUp = function (event) {
|
||||
if (event.which === 9 || event.keyCode === 9) {
|
||||
this._onEvent(event)
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
this.focusFlag = false
|
||||
this.firstEventFlag = true
|
||||
|
||||
/*
|
||||
Adds required (click and keyup) event listeners to the 'document' object
|
||||
to track the focus of the given 'target'
|
||||
*/
|
||||
if (this.onFocus || this.onBlur) {
|
||||
document.addEventListener('click', this._onClick)
|
||||
document.addEventListener('keyup', this._onKeyUp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the event listeners on the 'document' object
|
||||
* that were added to track the focus of the given 'target'
|
||||
*/
|
||||
destroy () {
|
||||
document.removeEventListener('click', this._onClick)
|
||||
document.removeEventListener('keyup', this._onKeyUp)
|
||||
this._onEvent({ target: document.body }) // calling _onEvent with body element in the hope that the FocusTracker is added to an element inside the body tag
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks the focus of the target and calls the onFocus and onBlur
|
||||
* event callbacks if available.
|
||||
* @param {Event} [event] The 'click' or 'keyup' event object,
|
||||
* from the respective events set on
|
||||
* document object
|
||||
* @private
|
||||
*/
|
||||
|
||||
_onEvent (event) {
|
||||
const target = event.target
|
||||
let focusFlag
|
||||
if (target === this.target) {
|
||||
focusFlag = true
|
||||
} else if (this.target.contains(target) || this.target.contains(document.activeElement)) {
|
||||
focusFlag = true
|
||||
} else {
|
||||
focusFlag = false
|
||||
}
|
||||
|
||||
if (focusFlag) {
|
||||
if (!this.focusFlag) {
|
||||
// trigger the onFocus callback
|
||||
if (this.onFocus) {
|
||||
this.onFocus({ type: 'focus', target: this.target })
|
||||
}
|
||||
this.focusFlag = true
|
||||
}
|
||||
} else {
|
||||
if (this.focusFlag || this.firstEventFlag) {
|
||||
// trigger the onBlur callback
|
||||
if (this.onBlur) {
|
||||
this.onBlur({ type: 'blur', target: this.target })
|
||||
}
|
||||
this.focusFlag = false
|
||||
|
||||
/*
|
||||
When switching from one mode to another in the editor, the FocusTracker gets recreated.
|
||||
At that time, this.focusFlag will be init to 'false' and will fail the above if condition, when blur occurs
|
||||
this.firstEventFlag is added to overcome that issue
|
||||
*/
|
||||
if (this.firstEventFlag) {
|
||||
this.firstEventFlag = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/**
|
||||
* The highlighter can highlight/unhighlight a node, and
|
||||
* animate the visibility of a context menu.
|
||||
* @constructor Highlighter
|
||||
*/
|
||||
export class Highlighter {
|
||||
constructor () {
|
||||
this.locked = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Hightlight given node and its childs
|
||||
* @param {Node} node
|
||||
*/
|
||||
highlight (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
|
||||
*/
|
||||
unhighlight () {
|
||||
if (this.locked) {
|
||||
return
|
||||
}
|
||||
|
||||
const 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(() => {
|
||||
me.node.setHighlight(false)
|
||||
me.node = undefined
|
||||
me.unhighlightTimer = undefined
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel an unhighlight action (if before the timeout of the unhighlight action)
|
||||
* @private
|
||||
*/
|
||||
_cancelUnhighlight () {
|
||||
if (this.unhighlightTimer) {
|
||||
clearTimeout(this.unhighlightTimer)
|
||||
this.unhighlightTimer = undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock highlighting or unhighlighting nodes.
|
||||
* methods highlight and unhighlight do not work while locked.
|
||||
*/
|
||||
lock () {
|
||||
this.locked = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock highlighting or unhighlighting nodes
|
||||
*/
|
||||
unlock () {
|
||||
this.locked = false
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
|
||||
/**
|
||||
* Keep track on any history, be able
|
||||
* @param {function} onChange
|
||||
* @param {function} calculateItemSize
|
||||
* @param {number} limit Maximum size of all items in history
|
||||
* @constructor
|
||||
*/
|
||||
export class History {
|
||||
constructor (onChange, calculateItemSize, limit) {
|
||||
this.onChange = onChange
|
||||
this.calculateItemSize = calculateItemSize || (() => 1)
|
||||
this.limit = limit
|
||||
|
||||
this.items = []
|
||||
this.index = -1
|
||||
}
|
||||
|
||||
add (item) {
|
||||
// limit number of items in history so that the total size doesn't
|
||||
// always keep at least one item in memory
|
||||
while (this._calculateHistorySize() > this.limit && this.items.length > 1) {
|
||||
this.items.shift()
|
||||
this.index--
|
||||
}
|
||||
|
||||
// cleanup any redo action that are not valid anymore
|
||||
this.items = this.items.slice(0, this.index + 1)
|
||||
|
||||
this.items.push(item)
|
||||
this.index++
|
||||
|
||||
this.onChange()
|
||||
}
|
||||
|
||||
_calculateHistorySize () {
|
||||
const calculateItemSize = this.calculateItemSize
|
||||
let totalSize = 0
|
||||
|
||||
this.items.forEach(item => {
|
||||
totalSize += calculateItemSize(item)
|
||||
})
|
||||
|
||||
return totalSize
|
||||
}
|
||||
|
||||
undo () {
|
||||
if (!this.canUndo()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.index--
|
||||
|
||||
this.onChange()
|
||||
|
||||
return this.items[this.index]
|
||||
}
|
||||
|
||||
redo () {
|
||||
if (!this.canRedo()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.index++
|
||||
|
||||
this.onChange()
|
||||
|
||||
return this.items[this.index]
|
||||
}
|
||||
|
||||
canUndo () {
|
||||
return this.index > 0
|
||||
}
|
||||
|
||||
canRedo () {
|
||||
return this.index < this.items.length - 1
|
||||
}
|
||||
|
||||
clear () {
|
||||
this.items = []
|
||||
this.index = -1
|
||||
|
||||
this.onChange()
|
||||
}
|
||||
}
|
|
@ -1,492 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const ace = require('./ace') // may be undefined in case of minimalist bundle
|
||||
const VanillaPicker = require('./vanilla-picker') // may be undefined in case of minimalist bundle
|
||||
const { treeModeMixins } = require('./treemode')
|
||||
const { textModeMixins } = require('./textmode')
|
||||
const { previewModeMixins } = require('./previewmode')
|
||||
const { clear, extend, getInnerText, getInternetExplorerVersion, parse } = require('./util')
|
||||
const { tryRequireAjv } = require('./tryRequireAjv')
|
||||
const { showTransformModal } = require('./showTransformModal')
|
||||
const { showSortModal } = require('./showSortModal')
|
||||
|
||||
const Ajv = tryRequireAjv()
|
||||
|
||||
if (typeof Promise === 'undefined') {
|
||||
console.error('Promise undefined. Please load a Promise polyfill in the browser in order to use JSONEditor')
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* Does not pass the contents itself.
|
||||
* See also `onChangeJSON` and
|
||||
* `onChangeText`.
|
||||
* {function} onChangeJSON Callback method, triggered
|
||||
* in modes on change of contents,
|
||||
* passing the changed contents
|
||||
* as JSON.
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'.
|
||||
* {function} onChangeText Callback method, triggered
|
||||
* in modes on change of contents,
|
||||
* passing the changed contents
|
||||
* as stringified JSON.
|
||||
* {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.
|
||||
* {function} onSelectionChange Callback method,
|
||||
* triggered on node selection change
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {function} onTextSelectionChange Callback method,
|
||||
* triggered on text selection change
|
||||
* Only applicable for modes
|
||||
* {HTMLElement} modalAnchor The anchor element to apply an
|
||||
* overlay and display the modals in a
|
||||
* centered location.
|
||||
* Defaults to document.body
|
||||
* 'text' and 'code'
|
||||
* {function} onEvent Callback method, triggered
|
||||
* when an event occurs in
|
||||
* a JSON field or value.
|
||||
* Only applicable for
|
||||
* modes 'form', 'tree' and
|
||||
* 'view'
|
||||
* {function} onFocus Callback method, triggered
|
||||
* when the editor comes into focus,
|
||||
* passing an object {type, target},
|
||||
* Applicable for all modes
|
||||
* {function} onBlur Callback method, triggered
|
||||
* when the editor goes out of focus,
|
||||
* passing an object {type, target},
|
||||
* Applicable for all modes
|
||||
* {function} onClassName Callback method, triggered
|
||||
* when a Node DOM is rendered. Function returns
|
||||
* a css class name to be set on a node.
|
||||
* Only applicable for
|
||||
* modes 'form', 'tree' and
|
||||
* 'view'
|
||||
* {Number} maxVisibleChilds Number of children allowed for a node
|
||||
* in 'tree', 'view', or 'form' mode before
|
||||
* the "show more/show all" buttons appear.
|
||||
* 100 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)
|
||||
const ieVersion = 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
|
||||
}
|
||||
|
||||
// warn if onChangeJSON is used when mode can be `text` or `code`
|
||||
if (options.onChangeJSON) {
|
||||
if (options.mode === 'text' || options.mode === 'code' ||
|
||||
(options.modes && (options.modes.indexOf('text') !== -1 || options.modes.indexOf('code') !== -1))) {
|
||||
console.warn('Option "onChangeJSON" is not applicable to modes "text" and "code". ' +
|
||||
'Use "onChangeText" or "onChange" instead.')
|
||||
}
|
||||
}
|
||||
|
||||
// validate options
|
||||
if (options) {
|
||||
Object.keys(options).forEach(option => {
|
||||
if (JSONEditor.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 validation in milliseconds
|
||||
JSONEditor.prototype.DEBOUNCE_INTERVAL = 150
|
||||
|
||||
JSONEditor.VALID_OPTIONS = [
|
||||
'ajv', 'schema', 'schemaRefs', 'templates',
|
||||
'ace', 'theme', 'autocomplete',
|
||||
'onChange', 'onChangeJSON', 'onChangeText',
|
||||
'onEditable', 'onError', 'onEvent', 'onModeChange', 'onNodeName', 'onValidate', 'onCreateMenu',
|
||||
'onSelectionChange', 'onTextSelectionChange', 'onClassName',
|
||||
'onFocus', 'onBlur',
|
||||
'colorPicker', 'onColorPicker',
|
||||
'timestampTag', 'timestampFormat',
|
||||
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation',
|
||||
'sortObjectKeys', 'navigationBar', 'statusBar', 'mainMenuBar', 'languages', 'language', 'enableSort', 'enableTransform', 'limitDragging',
|
||||
'maxVisibleChilds', 'onValidationError',
|
||||
'modalAnchor', 'popupAnchor',
|
||||
'createQuery', 'executeQuery', 'queryDescription'
|
||||
]
|
||||
|
||||
/**
|
||||
* 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 || {}
|
||||
|
||||
const mode = this.options.mode || (this.options.modes && this.options.modes[0]) || 'tree'
|
||||
this.setMode(mode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
||||
*/
|
||||
JSONEditor.prototype.destroy = () => {}
|
||||
|
||||
/**
|
||||
* 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 = 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) {
|
||||
// if the mode is the same as current mode (and it's not the first time), do nothing.
|
||||
if (mode === this.options.mode && this.create) {
|
||||
return
|
||||
}
|
||||
|
||||
const container = this.container
|
||||
const options = extend({}, this.options)
|
||||
const oldMode = options.mode
|
||||
let data
|
||||
let name
|
||||
|
||||
options.mode = mode
|
||||
const config = JSONEditor.modes[mode]
|
||||
if (config) {
|
||||
try {
|
||||
const asText = (config.data === 'text')
|
||||
name = this.getName()
|
||||
data = this[asText ? 'getText' : 'get']() // get text or json
|
||||
|
||||
this.destroy()
|
||||
clear(this)
|
||||
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
|
||||
* @param {Object.<string, Object>=} schemaRefs Schemas that are referenced using the `$ref` property from the JSON schema that are set in the `schema` option,
|
||||
+ the object structure in the form of `{reference_key: schemaObject}`
|
||||
*/
|
||||
JSONEditor.prototype.setSchema = function (schema, schemaRefs) {
|
||||
// compile a JSON schema validator if a JSON schema is provided
|
||||
if (schema) {
|
||||
let ajv
|
||||
try {
|
||||
// grab ajv from options if provided, else create a new instance
|
||||
if (this.options.ajv) {
|
||||
ajv = this.options.ajv
|
||||
} else {
|
||||
ajv = Ajv({
|
||||
allErrors: true,
|
||||
verbose: true,
|
||||
schemaId: 'auto',
|
||||
$data: true
|
||||
})
|
||||
|
||||
// support both draft-04 and draft-06 alongside the latest draft-07
|
||||
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'))
|
||||
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'))
|
||||
}
|
||||
} 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) {
|
||||
if (schemaRefs) {
|
||||
for (const ref in schemaRefs) {
|
||||
ajv.removeSchema(ref) // When updating a schema - old refs has to be removed first
|
||||
if (schemaRefs[ref]) {
|
||||
ajv.addSchema(schemaRefs[ref], ref)
|
||||
}
|
||||
}
|
||||
this.options.schemaRefs = schemaRefs
|
||||
}
|
||||
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.options.schemaRefs = 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 = () => {
|
||||
// must be implemented by treemode and textmode
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the rendered contents
|
||||
*/
|
||||
JSONEditor.prototype.refresh = () => {
|
||||
// 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 = mode => {
|
||||
let i, prop
|
||||
|
||||
if (Array.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')
|
||||
const 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')
|
||||
}
|
||||
const 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, text, and preview modes
|
||||
JSONEditor.registerMode(treeModeMixins)
|
||||
JSONEditor.registerMode(textModeMixins)
|
||||
JSONEditor.registerMode(previewModeMixins)
|
||||
|
||||
// expose some of the libraries that can be used customized
|
||||
JSONEditor.ace = ace
|
||||
JSONEditor.Ajv = Ajv
|
||||
JSONEditor.VanillaPicker = VanillaPicker
|
||||
|
||||
// expose some utils (this is undocumented, unofficial)
|
||||
JSONEditor.showTransformModal = showTransformModal
|
||||
JSONEditor.showSortModal = showSortModal
|
||||
JSONEditor.getInnerText = getInnerText
|
||||
|
||||
// default export for TypeScript ES6 projects
|
||||
JSONEditor.default = JSONEditor
|
||||
|
||||
module.exports = JSONEditor
|
|
@ -1,123 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
import { ContextMenu } from './ContextMenu'
|
||||
import { translate } from './i18n'
|
||||
|
||||
/**
|
||||
* 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', 'preview'
|
||||
* @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view', 'preview'
|
||||
* @param {function(mode: string)} onSwitch Callback invoked on switch
|
||||
* @constructor
|
||||
*/
|
||||
export class ModeSwitcher {
|
||||
constructor (container, modes, current, onSwitch) {
|
||||
// available modes
|
||||
const availableModes = {
|
||||
code: {
|
||||
text: translate('modeCodeText'),
|
||||
title: translate('modeCodeTitle'),
|
||||
click: function () {
|
||||
onSwitch('code')
|
||||
}
|
||||
},
|
||||
form: {
|
||||
text: translate('modeFormText'),
|
||||
title: translate('modeFormTitle'),
|
||||
click: function () {
|
||||
onSwitch('form')
|
||||
}
|
||||
},
|
||||
text: {
|
||||
text: translate('modeTextText'),
|
||||
title: translate('modeTextTitle'),
|
||||
click: function () {
|
||||
onSwitch('text')
|
||||
}
|
||||
},
|
||||
tree: {
|
||||
text: translate('modeTreeText'),
|
||||
title: translate('modeTreeTitle'),
|
||||
click: function () {
|
||||
onSwitch('tree')
|
||||
}
|
||||
},
|
||||
view: {
|
||||
text: translate('modeViewText'),
|
||||
title: translate('modeViewTitle'),
|
||||
click: function () {
|
||||
onSwitch('view')
|
||||
}
|
||||
},
|
||||
preview: {
|
||||
text: translate('modePreviewText'),
|
||||
title: translate('modePreviewTitle'),
|
||||
click: function () {
|
||||
onSwitch('preview')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// list the selected modes
|
||||
const items = []
|
||||
for (let i = 0; i < modes.length; i++) {
|
||||
const mode = modes[i]
|
||||
const 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
|
||||
const currentMode = availableModes[current]
|
||||
if (!currentMode) {
|
||||
throw new Error('Unknown mode "' + current + '"')
|
||||
}
|
||||
const currentTitle = currentMode.text
|
||||
|
||||
// create the html element
|
||||
const box = document.createElement('button')
|
||||
box.type = 'button'
|
||||
box.className = 'jsoneditor-modes jsoneditor-separator'
|
||||
box.textContent = currentTitle + ' \u25BE'
|
||||
box.title = translate('modeEditorTitle')
|
||||
box.onclick = () => {
|
||||
const menu = new ContextMenu(items)
|
||||
menu.show(box, container)
|
||||
}
|
||||
|
||||
const 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
|
||||
*/
|
||||
focus () {
|
||||
this.dom.box.focus()
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the ModeSwitcher, remove from DOM
|
||||
*/
|
||||
destroy () {
|
||||
if (this.dom && this.dom.frame && this.dom.frame.parentNode) {
|
||||
this.dom.frame.parentNode.removeChild(this.dom.frame)
|
||||
}
|
||||
this.dom = null
|
||||
}
|
||||
}
|
4666
src/js/Node.js
4666
src/js/Node.js
File diff suppressed because it is too large
Load Diff
|
@ -1,333 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
import { findUniqueName } from './util'
|
||||
|
||||
/**
|
||||
* @constructor History
|
||||
* Store action history, enables undo and redo
|
||||
* @param {JSONEditor} editor
|
||||
*/
|
||||
export class NodeHistory {
|
||||
constructor (editor) {
|
||||
this.editor = editor
|
||||
this.history = []
|
||||
this.index = -1
|
||||
|
||||
this.clear()
|
||||
|
||||
// helper function to find a Node from a path
|
||||
function findNode (path) {
|
||||
return editor.node.findNodeByInternalPath(path)
|
||||
}
|
||||
|
||||
// map with all supported actions
|
||||
this.actions = {
|
||||
editField: {
|
||||
undo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
const node = parentNode.childs[params.index]
|
||||
node.updateField(params.oldValue)
|
||||
},
|
||||
redo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
const node = parentNode.childs[params.index]
|
||||
node.updateField(params.newValue)
|
||||
}
|
||||
},
|
||||
editValue: {
|
||||
undo: function (params) {
|
||||
findNode(params.path).updateValue(params.oldValue)
|
||||
},
|
||||
redo: function (params) {
|
||||
findNode(params.path).updateValue(params.newValue)
|
||||
}
|
||||
},
|
||||
changeType: {
|
||||
undo: function (params) {
|
||||
findNode(params.path).changeType(params.oldType)
|
||||
},
|
||||
redo: function (params) {
|
||||
findNode(params.path).changeType(params.newType)
|
||||
}
|
||||
},
|
||||
|
||||
appendNodes: {
|
||||
undo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
params.paths.map(findNode).forEach(node => {
|
||||
parentNode.removeChild(node)
|
||||
})
|
||||
},
|
||||
redo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
params.nodes.forEach(node => {
|
||||
parentNode.appendChild(node)
|
||||
})
|
||||
}
|
||||
},
|
||||
insertBeforeNodes: {
|
||||
undo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
params.paths.map(findNode).forEach(node => {
|
||||
parentNode.removeChild(node)
|
||||
})
|
||||
},
|
||||
redo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
const beforeNode = findNode(params.beforePath)
|
||||
params.nodes.forEach(node => {
|
||||
parentNode.insertBefore(node, beforeNode)
|
||||
})
|
||||
}
|
||||
},
|
||||
insertAfterNodes: {
|
||||
undo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
params.paths.map(findNode).forEach(node => {
|
||||
parentNode.removeChild(node)
|
||||
})
|
||||
},
|
||||
redo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
let afterNode = findNode(params.afterPath)
|
||||
params.nodes.forEach(node => {
|
||||
parentNode.insertAfter(node, afterNode)
|
||||
afterNode = node
|
||||
})
|
||||
}
|
||||
},
|
||||
removeNodes: {
|
||||
undo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
const beforeNode = parentNode.childs[params.index] || parentNode.append
|
||||
params.nodes.forEach(node => {
|
||||
parentNode.insertBefore(node, beforeNode)
|
||||
})
|
||||
},
|
||||
redo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
params.paths.map(findNode).forEach(node => {
|
||||
parentNode.removeChild(node)
|
||||
})
|
||||
}
|
||||
},
|
||||
duplicateNodes: {
|
||||
undo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
params.clonePaths.map(findNode).forEach(node => {
|
||||
parentNode.removeChild(node)
|
||||
})
|
||||
},
|
||||
redo: function (params) {
|
||||
const parentNode = findNode(params.parentPath)
|
||||
let afterNode = findNode(params.afterPath)
|
||||
const nodes = params.paths.map(findNode)
|
||||
nodes.forEach(node => {
|
||||
const clone = node.clone()
|
||||
if (parentNode.type === 'object') {
|
||||
const existingFieldNames = parentNode.getFieldNames()
|
||||
clone.field = findUniqueName(node.field, existingFieldNames)
|
||||
}
|
||||
parentNode.insertAfter(clone, afterNode)
|
||||
afterNode = clone
|
||||
})
|
||||
}
|
||||
},
|
||||
moveNodes: {
|
||||
undo: function (params) {
|
||||
const oldParentNode = findNode(params.oldParentPath)
|
||||
const newParentNode = findNode(params.newParentPath)
|
||||
const oldBeforeNode = oldParentNode.childs[params.oldIndex] || oldParentNode.append
|
||||
|
||||
// first copy the nodes, then move them
|
||||
const nodes = newParentNode.childs.slice(params.newIndex, params.newIndex + params.count)
|
||||
|
||||
nodes.forEach((node, index) => {
|
||||
node.field = params.fieldNames[index]
|
||||
oldParentNode.moveBefore(node, oldBeforeNode)
|
||||
})
|
||||
|
||||
// This is a hack to work around an issue that we don't know tha original
|
||||
// path of the new parent after dragging, as the node is already moved at that time.
|
||||
if (params.newParentPathRedo === null) {
|
||||
params.newParentPathRedo = newParentNode.getInternalPath()
|
||||
}
|
||||
},
|
||||
redo: function (params) {
|
||||
const oldParentNode = findNode(params.oldParentPathRedo)
|
||||
const newParentNode = findNode(params.newParentPathRedo)
|
||||
const newBeforeNode = newParentNode.childs[params.newIndexRedo] || newParentNode.append
|
||||
|
||||
// first copy the nodes, then move them
|
||||
const nodes = oldParentNode.childs.slice(params.oldIndexRedo, params.oldIndexRedo + params.count)
|
||||
|
||||
nodes.forEach((node, index) => {
|
||||
node.field = params.fieldNames[index]
|
||||
newParentNode.moveBefore(node, newBeforeNode)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
sort: {
|
||||
undo: function (params) {
|
||||
const node = findNode(params.path)
|
||||
node.hideChilds()
|
||||
node.childs = params.oldChilds
|
||||
node.updateDom({ updateIndexes: true })
|
||||
node.showChilds()
|
||||
},
|
||||
redo: function (params) {
|
||||
const node = findNode(params.path)
|
||||
node.hideChilds()
|
||||
node.childs = params.newChilds
|
||||
node.updateDom({ updateIndexes: true })
|
||||
node.showChilds()
|
||||
}
|
||||
},
|
||||
|
||||
transform: {
|
||||
undo: function (params) {
|
||||
findNode(params.path).setInternalValue(params.oldValue)
|
||||
|
||||
// TODO: would be nice to restore the state of the node and childs
|
||||
},
|
||||
redo: function (params) {
|
||||
findNode(params.path).setInternalValue(params.newValue)
|
||||
|
||||
// TODO: would be nice to restore the state of the node and childs
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
onChange () {}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
add (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
|
||||
*/
|
||||
clear () {
|
||||
this.history = []
|
||||
this.index = -1
|
||||
|
||||
// fire onchange event
|
||||
this.onChange()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is an action available for undo
|
||||
* @return {Boolean} canUndo
|
||||
*/
|
||||
canUndo () {
|
||||
return (this.index >= 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is an action available for redo
|
||||
* @return {Boolean} canRedo
|
||||
*/
|
||||
canRedo () {
|
||||
return (this.index < this.history.length - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the last action
|
||||
*/
|
||||
undo () {
|
||||
if (this.canUndo()) {
|
||||
const obj = this.history[this.index]
|
||||
if (obj) {
|
||||
const action = this.actions[obj.action]
|
||||
if (action && action.undo) {
|
||||
action.undo(obj.params)
|
||||
if (obj.params.oldSelection) {
|
||||
try {
|
||||
this.editor.setDomSelection(obj.params.oldSelection)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error(new Error('unknown action "' + obj.action + '"'))
|
||||
}
|
||||
}
|
||||
this.index--
|
||||
|
||||
// fire onchange event
|
||||
this.onChange()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redo the last action
|
||||
*/
|
||||
redo () {
|
||||
if (this.canRedo()) {
|
||||
this.index++
|
||||
|
||||
const obj = this.history[this.index]
|
||||
if (obj) {
|
||||
const action = this.actions[obj.action]
|
||||
if (action && action.redo) {
|
||||
action.redo(obj.params)
|
||||
if (obj.params.newSelection) {
|
||||
try {
|
||||
this.editor.setDomSelection(obj.params.newSelection)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error(new Error('unknown action "' + obj.action + '"'))
|
||||
}
|
||||
}
|
||||
|
||||
// fire onchange event
|
||||
this.onChange()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy history
|
||||
*/
|
||||
destroy () {
|
||||
this.editor = null
|
||||
|
||||
this.history = []
|
||||
this.index = -1
|
||||
}
|
||||
}
|
|
@ -1,325 +0,0 @@
|
|||
'use strict'
|
||||
import { translate } from './i18n'
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
export class SearchBox {
|
||||
constructor (editor, container) {
|
||||
const searchBox = this
|
||||
|
||||
this.editor = editor
|
||||
this.timeout = undefined
|
||||
this.delay = 200 // ms
|
||||
this.lastText = undefined
|
||||
this.results = null
|
||||
|
||||
this.dom = {}
|
||||
this.dom.container = container
|
||||
|
||||
const wrapper = document.createElement('div')
|
||||
this.dom.wrapper = wrapper
|
||||
wrapper.className = 'jsoneditor-search'
|
||||
container.appendChild(wrapper)
|
||||
|
||||
const results = document.createElement('div')
|
||||
this.dom.results = results
|
||||
results.className = 'jsoneditor-results'
|
||||
wrapper.appendChild(results)
|
||||
|
||||
const divInput = document.createElement('div')
|
||||
this.dom.input = divInput
|
||||
divInput.className = 'jsoneditor-frame'
|
||||
divInput.title = translate('searchTitle')
|
||||
wrapper.appendChild(divInput)
|
||||
|
||||
const refreshSearch = document.createElement('button')
|
||||
refreshSearch.type = 'button'
|
||||
refreshSearch.className = 'jsoneditor-refresh'
|
||||
divInput.appendChild(refreshSearch)
|
||||
|
||||
const search = document.createElement('input')
|
||||
search.type = 'text'
|
||||
this.dom.search = search
|
||||
search.oninput = event => {
|
||||
searchBox._onDelayedSearch(event)
|
||||
}
|
||||
search.onchange = event => {
|
||||
// For IE 9
|
||||
searchBox._onSearch()
|
||||
}
|
||||
search.onkeydown = event => {
|
||||
searchBox._onKeyDown(event)
|
||||
}
|
||||
search.onkeyup = event => {
|
||||
searchBox._onKeyUp(event)
|
||||
}
|
||||
refreshSearch.onclick = event => {
|
||||
search.select()
|
||||
}
|
||||
|
||||
// TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819
|
||||
divInput.appendChild(search)
|
||||
|
||||
const searchNext = document.createElement('button')
|
||||
searchNext.type = 'button'
|
||||
searchNext.title = translate('searchNextResultTitle')
|
||||
searchNext.className = 'jsoneditor-next'
|
||||
searchNext.onclick = () => {
|
||||
searchBox.next()
|
||||
}
|
||||
|
||||
divInput.appendChild(searchNext)
|
||||
|
||||
const searchPrevious = document.createElement('button')
|
||||
searchPrevious.type = 'button'
|
||||
searchPrevious.title = translate('searchPreviousResultTitle')
|
||||
searchPrevious.className = 'jsoneditor-previous'
|
||||
searchPrevious.onclick = () => {
|
||||
searchBox.previous()
|
||||
}
|
||||
|
||||
divInput.appendChild(searchPrevious)
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the next search result
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result
|
||||
* focus is false by default.
|
||||
*/
|
||||
next (focus) {
|
||||
if (this.results) {
|
||||
let index = this.resultIndex !== null ? 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.
|
||||
*/
|
||||
previous (focus) {
|
||||
if (this.results) {
|
||||
const max = this.results.length - 1
|
||||
let index = this.resultIndex !== null ? 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
|
||||
*/
|
||||
_setActiveResult (index, focus) {
|
||||
// de-activate current active result
|
||||
if (this.activeResult) {
|
||||
const prevNode = this.activeResult.node
|
||||
const 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
|
||||
const node = this.results[this.resultIndex].node
|
||||
const 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(() => {
|
||||
if (focus) {
|
||||
node.focus(elem)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel any running onDelayedSearch.
|
||||
* @private
|
||||
*/
|
||||
_clearDelay () {
|
||||
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
|
||||
*/
|
||||
_onDelayedSearch (event) {
|
||||
// execute the search after a short delay (reduces the number of
|
||||
// search actions while typing in the search text box)
|
||||
this._clearDelay()
|
||||
const searchBox = this
|
||||
this.timeout = setTimeout(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
|
||||
*/
|
||||
_onSearch (forceSearch) {
|
||||
this._clearDelay()
|
||||
|
||||
const value = this.dom.search.value
|
||||
const 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)
|
||||
const MAX_SEARCH_RESULTS = this.results[0]
|
||||
? this.results[0].node.MAX_SEARCH_RESULTS
|
||||
: Infinity
|
||||
|
||||
// try to maintain the current active result if this is still part of the new search results
|
||||
let activeResultIndex = 0
|
||||
if (this.activeResult) {
|
||||
for (let i = 0; i < this.results.length; i++) {
|
||||
if (this.results[i].node === this.activeResult.node) {
|
||||
activeResultIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._setActiveResult(activeResultIndex, false)
|
||||
|
||||
// display search results
|
||||
if (text !== undefined) {
|
||||
const resultCount = this.results.length
|
||||
if (resultCount === 0) {
|
||||
this.dom.results.textContent = 'no\u00A0results'
|
||||
} else if (resultCount === 1) {
|
||||
this.dom.results.textContent = '1\u00A0result'
|
||||
} else if (resultCount > MAX_SEARCH_RESULTS) {
|
||||
this.dom.results.textContent = MAX_SEARCH_RESULTS + '+\u00A0results'
|
||||
} else {
|
||||
this.dom.results.textContent = resultCount + '\u00A0results'
|
||||
}
|
||||
} else {
|
||||
this.dom.results.textContent = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle onKeyDown event in the input box
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_onKeyDown (event) {
|
||||
const 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
|
||||
*/
|
||||
_onKeyUp (event) {
|
||||
const keynum = event.keyCode
|
||||
if (keynum !== 27 && keynum !== 13) {
|
||||
// !show and !Enter
|
||||
this._onDelayedSearch(event) // For IE 9
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the search results
|
||||
*/
|
||||
clear () {
|
||||
this.dom.search.value = ''
|
||||
this._onSearch()
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh searchResults if there is a search value
|
||||
*/
|
||||
forceSearch () {
|
||||
this._onSearch(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether the search box value is empty
|
||||
* @returns {boolean} Returns true when empty.
|
||||
*/
|
||||
isEmpty () {
|
||||
return this.dom.search.value === ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the search box
|
||||
*/
|
||||
destroy () {
|
||||
this.editor = null
|
||||
this.dom.container.removeChild(this.dom.wrapper)
|
||||
this.dom = null
|
||||
|
||||
this.results = null
|
||||
this.activeResult = null
|
||||
|
||||
this._clearDelay()
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue