Skip to content

Commit b76c57a

Browse files
Merge branch 'josdejong:master' into master
2 parents 7188a60 + eb84c4e commit b76c57a

File tree

10 files changed

+1044
-1785
lines changed

10 files changed

+1044
-1785
lines changed

HISTORY.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,25 @@
33
https://github.com/josdejong/jsoneditor
44

55

6+
## 2021-06-30, version 9.5.1
7+
8+
- Upgrade to `jsonrepair@2.2.1`.
9+
10+
11+
## 2021-06-05, version 9.5.0
12+
13+
- Implemented new method `JSONEditor.validate(): Promise<ValidationError[]>`.
14+
Thanks @ChrisAcrobat.
15+
16+
17+
## 2021-06-02, version 9.4.2
18+
19+
- Fix #1311: exception being thrown under certain conditions when switching
20+
from `code` mode to `preview` mode.
21+
- Rename spin animation of `selectr` to prevent conflicts with tailwind,
22+
see #1333. Thanks @mdix.
23+
24+
625
## 2021-04-25, version 9.4.1
726

827
- Improvements in the Korean translation. Thanks @luasenvy.

docs/api.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,17 @@ Constructs a new JSONEditor.
173173

174174
Also see the option `schema` for JSON schema validation.
175175

176-
- `{function} onValidationError(errors)`
176+
- `{function} onValidationError(errors: ValidationError[])`
177177

178178
Set a callback function for validation and parse errors. Available in all modes.
179+
The `ValidationError` contains a `type`, `path`, and `message`.
179180

180181
On validation of the json, if errors of any kind were found this callback is invoked with the errors data.
181182

182183
On change, the callback will be invoked only if errors were changed.
183184

185+
See also method `JSONEditor.validate()`.
186+
184187
Example:
185188

186189
```js
@@ -882,6 +885,20 @@ valid JSON and the editor is in mode `tree`, `view`, or `form`.
882885

883886
Contents of the editor as string.
884887

888+
#### `JSONEditor.validate()`
889+
890+
Validate the JSON document against the configured JSON schema or custom validator.
891+
See also the `onValidationError` callback.
892+
893+
*Returns:*
894+
895+
- `{Promise<ValidationError[]>} errorsPromise`
896+
897+
Returns a promise which resolves with the current validation errors,
898+
or an empty list when there are no errors. The `ValidationError` contains
899+
a `type`, `path`, and `message`.
900+
901+
885902
### Static properties
886903

887904
- `{string[]} JSONEditor.VALID_OPTIONS`

gulpfile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const log = require('fancy-log')
55
const format = require('date-format')
66
const concatCss = require('gulp-concat-css')
77
const minifyCSS = require('gulp-clean-css')
8-
const sass = require('gulp-sass')
8+
const sass = require('gulp-sass')(require('sass'))
99
const mkdirp = require('mkdirp')
1010
const webpack = require('webpack')
1111
const uglify = require('uglify-js')

package-lock.json

Lines changed: 916 additions & 1731 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jsoneditor",
3-
"version": "9.4.1",
3+
"version": "9.5.1",
44
"main": "./dist/jsoneditor.min.js",
55
"description": "A web-based tool to view, edit, format, and validate JSON",
66
"tags": [
@@ -30,32 +30,33 @@
3030
"ajv": "^6.12.6",
3131
"javascript-natural-sort": "^0.7.1",
3232
"jmespath": "^0.15.0",
33-
"jsonrepair": "^2.2.0",
33+
"jsonrepair": "^2.2.1",
3434
"json-source-map": "^0.6.1",
3535
"mobius1-selectr": "^2.4.13",
3636
"picomodal": "^3.0.0",
3737
"vanilla-picker": "^2.11.2"
3838
},
3939
"devDependencies": {
40-
"@babel/core": "7.13.16",
41-
"@babel/preset-env": "7.13.15",
42-
"@babel/register": "7.13.16",
40+
"@babel/core": "7.14.6",
41+
"@babel/preset-env": "7.14.7",
42+
"@babel/register": "7.14.5",
4343
"babel-loader": "8.2.2",
4444
"btoa": "1.2.1",
4545
"date-format": "3.0.0",
4646
"fancy-log": "1.3.3",
4747
"gulp": "4.0.2",
4848
"gulp-clean-css": "4.3.0",
4949
"gulp-concat-css": "3.1.0",
50-
"gulp-sass": "4.1.0",
51-
"jsdom": "16.5.3",
50+
"gulp-sass": "5.0.0",
51+
"jsdom": "16.6.0",
5252
"json-loader": "0.5.7",
5353
"mkdirp": "1.0.4",
54-
"mocha": "8.3.2",
55-
"source-map-loader": "2.0.1",
54+
"mocha": "9.0.1",
55+
"sass": "1.35.1",
56+
"source-map-loader": "3.0.0",
5657
"standard": "16.0.3",
57-
"uglify-js": "3.13.4",
58-
"webpack": "5.35.1"
58+
"uglify-js": "3.13.10",
59+
"webpack": "5.41.1"
5960
},
6061
"files": [
6162
"dist",

src/js/assets/selectr/selectr.scss

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -405,16 +405,16 @@
405405
-webkit-transform-origin: 50% 0 0;
406406
transform-origin: 50% 0 0;
407407

408-
-moz-animation: 500ms linear 0s normal forwards infinite running spin;
409-
-webkit-animation: 500ms linear 0s normal forwards infinite running spin;
410-
animation: 500ms linear 0s normal forwards infinite running spin;
408+
-moz-animation: 500ms linear 0s normal forwards infinite running selectr-spin;
409+
-webkit-animation: 500ms linear 0s normal forwards infinite running selectr-spin;
410+
animation: 500ms linear 0s normal forwards infinite running selectr-spin;
411411
border-width: 3px;
412412
border-style: solid;
413413
border-color: #aaa #ddd #ddd;
414414
border-radius: 50%;
415415
}
416416

417-
@-webkit-keyframes spin {
417+
@-webkit-keyframes selectr-spin {
418418
0% {
419419
-webkit-transform: rotate(0deg) translate3d(0px, -50%, 0px);
420420
transform: rotate(0deg) translate3d(0px, -50%, 0px);
@@ -424,7 +424,7 @@
424424
transform: rotate(360deg) translate3d(0px, -50%, 0px);
425425
}
426426
}
427-
@keyframes spin {
427+
@keyframes selectr-spin {
428428
0% {
429429
-webkit-transform: rotate(0deg) translate3d(0px, -50%, 0px);
430430
transform: rotate(0deg) translate3d(0px, -50%, 0px);

src/js/textmode.js

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ textmode.create = function (container, options = {}) {
9797
this.lastSchemaErrors = undefined
9898

9999
// create a debounced validate function
100-
this._debouncedValidate = debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL)
100+
this._debouncedValidate = debounce(this._validateAndCatch.bind(this), this.DEBOUNCE_INTERVAL)
101101

102102
this.width = container.clientWidth
103103
this.height = container.clientHeight
@@ -346,7 +346,7 @@ textmode.create = function (container, options = {}) {
346346
this.errorTable = new ErrorTable({
347347
errorTableVisible: this.mode === 'text',
348348
onToggleVisibility: function () {
349-
me.validate()
349+
me._validateAndCatch()
350350
},
351351
onFocusLine: function (line) {
352352
me.isFocused = true
@@ -434,7 +434,11 @@ textmode._onChange = function () {
434434
}
435435

436436
// enable/disable undo/redo buttons
437-
setTimeout(() => this._updateHistoryButtons())
437+
setTimeout(() => {
438+
if (this._updateHistoryButtons) {
439+
this._updateHistoryButtons()
440+
}
441+
})
438442

439443
// validate JSON schema (if configured)
440444
this._debouncedValidate()
@@ -822,7 +826,11 @@ textmode._setText = function (jsonText, clearHistory) {
822826
})
823827
}
824828

825-
setTimeout(() => this._updateHistoryButtons())
829+
setTimeout(() => {
830+
if (this._updateHistoryButtons) {
831+
this._updateHistoryButtons()
832+
}
833+
})
826834
}
827835

828836
// validate JSON schema
@@ -877,22 +885,22 @@ textmode.validate = function () {
877885
this.validationSequence = (this.validationSequence || 0) + 1
878886
const me = this
879887
const seq = this.validationSequence
880-
validateCustom(json, this.options.onValidate)
888+
return validateCustom(json, this.options.onValidate)
881889
.then(customValidationErrors => {
882890
// only apply when there was no other validation started whilst resolving async results
883891
if (seq === me.validationSequence) {
884892
const errors = schemaErrors.concat(parseErrors).concat(customValidationErrors)
885893
me._renderErrors(errors)
886-
if (typeof this.options.onValidationError === 'function') {
887-
if (isValidationErrorChanged(errors, this.lastSchemaErrors)) {
888-
this.options.onValidationError.call(this, errors)
889-
}
890-
this.lastSchemaErrors = errors
894+
if (
895+
typeof this.options.onValidationError === 'function' &&
896+
isValidationErrorChanged(errors, this.lastSchemaErrors)
897+
) {
898+
this.options.onValidationError.call(this, errors)
891899
}
900+
this.lastSchemaErrors = errors
892901
}
893-
})
894-
.catch(err => {
895-
console.error('Custom validation function did throw an error', err)
902+
903+
return this.lastSchemaErrors
896904
})
897905
} catch (err) {
898906
if (this.getText()) {
@@ -911,15 +919,24 @@ textmode.validate = function () {
911919

912920
this._renderErrors(parseErrors)
913921

914-
if (typeof this.options.onValidationError === 'function') {
915-
if (isValidationErrorChanged(parseErrors, this.lastSchemaErrors)) {
916-
this.options.onValidationError.call(this, parseErrors)
917-
}
918-
this.lastSchemaErrors = parseErrors
922+
if (
923+
typeof this.options.onValidationError === 'function' &&
924+
isValidationErrorChanged(parseErrors, this.lastSchemaErrors)
925+
) {
926+
this.options.onValidationError.call(this, parseErrors)
919927
}
928+
this.lastSchemaErrors = parseErrors
929+
930+
return Promise.resolve(this.lastSchemaErrors)
920931
}
921932
}
922933

934+
textmode._validateAndCatch = function () {
935+
this.validate().catch(err => {
936+
console.error('Error running validation:', err)
937+
})
938+
}
939+
923940
textmode._renderErrors = function (errors) {
924941
const jsonText = this.getText()
925942
const errorPaths = []

src/js/treemode.js

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ treemode._setOptions = function (options) {
182182
this.setSchema(this.options.schema, this.options.schemaRefs)
183183

184184
// create a debounced validate function
185-
this._debouncedValidate = debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL)
185+
this._debouncedValidate = debounce(this._validateAndCatch.bind(this), this.DEBOUNCE_INTERVAL)
186186

187187
if (options.onSelectionChange) {
188188
this.onSelectionChange(options.onSelectionChange)
@@ -214,7 +214,7 @@ treemode.set = function (json) {
214214
this._setRoot(node)
215215

216216
// validate JSON schema (if configured)
217-
this.validate()
217+
this._validateAndCatch()
218218

219219
// expand
220220
const recurse = false
@@ -254,7 +254,7 @@ treemode.update = function (json) {
254254
this.onChangeDisabled = false
255255

256256
// validate JSON schema
257-
this.validate()
257+
this._validateAndCatch()
258258

259259
// update search result if any
260260
if (this.searchBox && !this.searchBox.isEmpty()) {
@@ -588,28 +588,35 @@ treemode.validate = function () {
588588
this.validationSequence++
589589
const me = this
590590
const seq = this.validationSequence
591-
this._validateCustom(json)
591+
return this._validateCustom(json)
592592
.then(customValidationErrors => {
593593
// only apply when there was no other validation started whilst resolving async results
594594
if (seq === me.validationSequence) {
595595
const errorNodes = [].concat(schemaErrors, customValidationErrors || [])
596596
me._renderValidationErrors(errorNodes)
597-
if (typeof this.options.onValidationError === 'function') {
598-
if (isValidationErrorChanged(errorNodes, this.lastSchemaErrors)) {
599-
this.options.onValidationError.call(this, errorNodes)
600-
}
601-
this.lastSchemaErrors = errorNodes
597+
if (
598+
typeof this.options.onValidationError === 'function' &&
599+
isValidationErrorChanged(errorNodes, this.lastSchemaErrors)
600+
) {
601+
this.options.onValidationError.call(this, errorNodes)
602602
}
603+
604+
this.lastSchemaErrors = errorNodes
603605
}
604-
})
605-
.catch(err => {
606-
console.error(err)
606+
607+
return this.lastSchemaErrors
607608
})
608609
} catch (err) {
609-
console.error(err)
610+
return Promise.reject(err)
610611
}
611612
}
612613

614+
treemode._validateAndCatch = function () {
615+
this.validate().catch(err => {
616+
console.error('Error running validation:', err)
617+
})
618+
}
619+
613620
treemode._renderValidationErrors = function (errorNodes) {
614621
// clear all current errors
615622
if (this.errorNodes) {

src/scss/jsoneditor/_editor.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ div {
208208
margin: 0 4px 0 0;
209209
background-image: $jse-icons-url;
210210
background-position: -168px -48px;
211+
background-color: transparent;
211212
}
212213
}
213214
}
@@ -536,11 +537,13 @@ pre.jsoneditor-preview,
536537
.jsoneditor-schema-error {
537538
background-image: $jse-icons-url;
538539
background-position: -168px -48px;
540+
background-color: transparent;
539541
}
540542
&.parse-error {
541543
.jsoneditor-schema-error {
542544
background-image: $jse-icons-url;
543545
background-position: -25px 0px;
546+
background-color: transparent;
544547
}
545548
}
546549
}

test/test_schema.html

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,29 @@
7979
};
8080

8181
var options = {
82-
mode: 'tree',
82+
mode: 'code',
8383
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
84+
schema: schema,
85+
schemaRefs: {"hobbies.json": hobbiesSchema},
8486
onError: function (err) {
85-
console.error(err);
87+
console.error('ERROR', err);
8688
},
87-
schema: schema,
88-
schemaRefs: {"hobbies.json": hobbiesSchema}
89+
onChange: async () => {
90+
const errors = await editor.validate()
91+
if (errors.length === 0) {
92+
console.log('validation errors: NONE')
93+
// do something, like persisting the JSON
94+
} else {
95+
// show error to the user or something
96+
console.log('validation errors', errors)
97+
}
98+
}
8999
};
90100

91101
var json = {
92102
"firstName": "Jos",
93103
"lastName": "de Jong",
94-
gender: null,
104+
"gender": null,
95105
"age": 34.2,
96106
"hobbies": [
97107
"programming",

0 commit comments

Comments
 (0)