@json-editor/json-editor 2.11.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.env +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +90 -6
  4. package/README_ADDON.md +5 -1
  5. package/dist/jsoneditor.js +1 -1
  6. package/dist/jsoneditor.js.LICENSE.txt +1 -1
  7. package/dist/nonmin/jsoneditor.js +417 -227
  8. package/dist/nonmin/jsoneditor.js.map +1 -1
  9. package/docs/cleave.html +1 -1
  10. package/docs/datetime.html +1 -1
  11. package/docs/describedby.html +1 -1
  12. package/docs/index.html +66 -14
  13. package/docs/meta_schema.json +16 -1
  14. package/docs/radio.html +1 -1
  15. package/docs/scripts/ajv-validator.js +8695 -0
  16. package/docs/select2.html +1 -1
  17. package/docs/selectize.html +2 -5
  18. package/docs/signature.html +12 -11
  19. package/docs/wysiwyg.html +1 -1
  20. package/package.json +1 -1
  21. package/src/core.js +10 -1
  22. package/src/defaults.js +3 -1
  23. package/src/editor.js +19 -1
  24. package/src/editors/array/selectize.js +0 -2
  25. package/src/editors/array.js +24 -13
  26. package/src/editors/base64.js +9 -0
  27. package/src/editors/button.js +8 -2
  28. package/src/editors/integer.js +3 -2
  29. package/src/editors/multiple.js +3 -0
  30. package/src/editors/number.js +4 -2
  31. package/src/editors/object.js +71 -3
  32. package/src/editors/signature.js +16 -16
  33. package/src/editors/simplemde.js +2 -1
  34. package/src/editors/string.js +4 -0
  35. package/src/editors/table.js +17 -14
  36. package/src/resolvers.js +13 -6
  37. package/src/schemaloader.js +13 -0
  38. package/src/theme.js +4 -0
  39. package/src/themes/bootstrap3.js +6 -0
  40. package/src/themes/bootstrap4.js +6 -0
  41. package/src/themes/bootstrap5.js +6 -0
  42. package/src/validator.js +28 -0
  43. package/tests/codeceptjs/codecept.json +1 -1
  44. package/tests/codeceptjs/constrains/contains_test.js +3 -2
  45. package/tests/codeceptjs/constrains/dependentRequired_test.js +33 -0
  46. package/tests/codeceptjs/constrains/dependentSchemas_test.js +1 -0
  47. package/tests/codeceptjs/core_test.js +14 -4
  48. package/tests/codeceptjs/editors/advanced_test.js +1 -1
  49. package/tests/codeceptjs/editors/array_test.js +59 -0
  50. package/tests/codeceptjs/editors/autocomplete_test.js +3 -1
  51. package/tests/codeceptjs/editors/object_test.js +20 -0
  52. package/tests/codeceptjs/editors/tabs_test.js +1 -1
  53. package/tests/codeceptjs/issues/issue-gh-1330_test.js +8 -0
  54. package/tests/codeceptjs/issues/issue-gh-1338_test.js +2 -0
  55. package/tests/codeceptjs/issues/issue-gh-1364_test.js +13 -0
  56. package/tests/codeceptjs/issues/issue-gh-1367_test.js +11 -0
  57. package/tests/codeceptjs/issues/issue-gh-1383_test.js +9 -0
  58. package/tests/codeceptjs/issues/issue-gh-1384_test.js +9 -0
  59. package/tests/codeceptjs/issues/issue-gh-1410_test.js +13 -0
  60. package/tests/codeceptjs/issues/issue-gh-1422_test.js +9 -0
  61. package/tests/codeceptjs/issues/issue-gh-1431_test.js +12 -0
  62. package/tests/codeceptjs/issues/issue-gh-1439_test.js +12 -0
  63. package/tests/docker-compose-local.yml +4 -0
  64. package/tests/pages/array-header-template.html +59 -0
  65. package/tests/pages/array-selectize-create.html +62 -0
  66. package/tests/pages/array-table-responsive.html +65 -0
  67. package/tests/pages/button-icons.html +1 -1
  68. package/tests/pages/button_state_mode_1.html +34 -0
  69. package/tests/pages/button_state_mode_2.html +35 -0
  70. package/tests/pages/dependentRequired.html +71 -0
  71. package/tests/pages/issues/issue-gh-1330.html +52 -0
  72. package/tests/pages/issues/issue-gh-1364.html +64 -0
  73. package/tests/pages/issues/issue-gh-1367.html +49 -0
  74. package/tests/pages/issues/issue-gh-1383.html +31 -0
  75. package/tests/pages/issues/issue-gh-1383.json +14 -0
  76. package/tests/pages/issues/issue-gh-1384.html +31 -0
  77. package/tests/pages/issues/issue-gh-1384.json +36 -0
  78. package/tests/pages/issues/issue-gh-1410.html +57 -0
  79. package/tests/pages/issues/issue-gh-1422.html +68 -0
  80. package/tests/pages/issues/issue-gh-1431.html +49 -0
  81. package/tests/pages/issues/issue-gh-1439.html +69 -0
  82. package/tests/pages/keep_only_existing_values.html +1 -1
  83. package/tests/pages/load-events.html +60 -0
  84. package/tests/pages/meta-schema.html +16 -2
  85. package/tests/pages/meta_schema.json +5 -1
  86. package/tests/pages/object-case-sensitive-property-search-false.html +41 -0
  87. package/tests/pages/object-case-sensitive-property-search-true.html +41 -0
  88. package/dist/dev/jsoneditor.js +0 -3687
  89. package/dist/jsoneditor.js.map +0 -1
package/docs/select2.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <title>JSON Editor Select2 Integration Example</title>
6
6
  <!-- jQuery -->
7
- <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
7
+ <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
8
8
 
9
9
  <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
10
10
 
@@ -3,12 +3,9 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <title>JSON Editor Selectize Integration Example</title>
6
- <!-- jQuery -->
7
- <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
8
-
9
- <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
10
-
6
+ <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
11
7
  <script src="https://cdn.jsdelivr.net/npm/selectize@0.12.6/dist/js/standalone/selectize.min.js"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
12
9
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/selectize@0.12.6/dist/css/selectize.default.css">
13
10
  </head>
14
11
  <body>
@@ -10,24 +10,25 @@
10
10
  <h1>JSON Editor: signature editor</h1>
11
11
 
12
12
  <div>Using this editor a signature (or any other simple drawing) can be made using the mouse or touchpad. The result is saved in base64 format. The editor will span the full width of the container, so no columns/grids are possible. With big thank you to <a href='https://github.com/szimek'>Szimek</a> for creating SignaturePad. <a href='https://github.com/szimek/signature_pad'>SignaturePad</a> must be included before the json-editor to load the signature editor properly. Set the type of the property to 'string' and the format to 'signature' to use the editor. The canvas is set to a default height of 300px, but this can be easily overriden by adding for example 'canvas_height': 500 in the properties.</div>
13
-
13
+
14
14
  <div id='editor_holder'></div>
15
15
  <button id='submit'>Submit (console.log)</button>
16
-
16
+
17
17
  <script>
18
18
  // Initialize the editor with a JSON schema
19
19
  var editor = new JSONEditor(document.getElementById('editor_holder'),{
20
20
  "schema": {
21
- "type": "object",
22
- "title": "Document",
23
- "properties": {
24
- "signature": {
25
- "type": "signature",
26
- "title": "Signature",
27
- "options": {
28
- "canvas_height": 500
29
- }
21
+ 'type': 'object',
22
+ 'title': 'Document',
23
+ 'properties': {
24
+ 'signature': {
25
+ 'type': 'string',
26
+ 'format': 'signature',
27
+ 'title': 'Signature',
28
+ 'options': {
29
+ 'canvas_height': 500
30
30
  }
31
+ }
31
32
  }
32
33
  }
33
34
  });
package/docs/wysiwyg.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <title>JSON Editor WYSIWYG Example</title>
6
6
  <!-- jQuery -->
7
- <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
7
+ <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
8
8
 
9
9
  <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
10
10
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@json-editor/json-editor",
3
3
  "title": "JSONEditor",
4
4
  "description": "JSON Schema based editor",
5
- "version": "2.11.0",
5
+ "version": "2.13.0",
6
6
  "main": "dist/jsoneditor.js",
7
7
  "author": {
8
8
  "name": "Jeremy Dorn",
package/src/core.js CHANGED
@@ -64,6 +64,15 @@ export class JSONEditor {
64
64
  async load () {
65
65
  const fetchUrl = document.location.origin + document.location.pathname.toString()
66
66
  const loader = new SchemaLoader(this.options)
67
+
68
+ loader.onSchemaLoaded = (payload) => {
69
+ this.trigger('schemaLoaded', payload)
70
+ }
71
+
72
+ loader.onAllSchemasLoaded = () => {
73
+ this.trigger('allSchemasLoaded')
74
+ }
75
+
67
76
  this.expandSchema = (schema) => loader.expandSchema(schema)
68
77
  this.expandRefs = (schema, fileBase) => loader.expandRefs(schema, fileBase)
69
78
  const location = document.location.toString()
@@ -211,7 +220,7 @@ export class JSONEditor {
211
220
  schema = this.expandSchema(schema)
212
221
 
213
222
  JSONEditor.defaults.resolvers.find(resolver => {
214
- classname = resolver(schema)
223
+ classname = resolver(schema, this)
215
224
  return classname && JSONEditor.defaults.editors[classname]
216
225
  })
217
226
  if (!classname) throw new Error(`Unknown editor for schema ${JSON.stringify(schema)}`)
package/src/defaults.js CHANGED
@@ -406,7 +406,9 @@ const options = {
406
406
  use_name_attributes: true,
407
407
  prompt_before_delete: true,
408
408
  use_default_values: true,
409
- max_depth: 0
409
+ max_depth: 0,
410
+ button_state_mode: 1,
411
+ case_sensitive_property_search: true
410
412
  }
411
413
 
412
414
  /* This assignment was previously in index.js but makes more sense here */
package/src/editor.js CHANGED
@@ -248,9 +248,9 @@ export class AbstractEditor {
248
248
  postBuild () {
249
249
  this.setupWatchListeners()
250
250
  this.addLinks()
251
+ this.register()
251
252
  this.setValue(this.getDefault(), true)
252
253
  this.updateHeaderText()
253
- this.register()
254
254
  this.onWatchedFieldChange()
255
255
  }
256
256
 
@@ -510,6 +510,7 @@ export class AbstractEditor {
510
510
 
511
511
  onWatchedFieldChange () {
512
512
  let vars
513
+
513
514
  if (this.header_template) {
514
515
  vars = extend(this.getWatchedFieldValues(), {
515
516
  key: this.key,
@@ -518,6 +519,23 @@ export class AbstractEditor {
518
519
  i1: (this.key * 1 + 1),
519
520
  title: this.getTitle()
520
521
  })
522
+
523
+ // object properties
524
+ if (Object.keys(this.editors).length) {
525
+ Object.keys(this.editors).forEach((key) => {
526
+ const editor = this.editors[key]
527
+
528
+ if (editor.schema && editor.schema.enum && editor.schema.options && editor.schema.options.enum_titles) {
529
+ const enumIndex = editor.schema.enum.indexOf(editor.value)
530
+ const enumTitle = editor.options.enum_titles[enumIndex]
531
+ vars.properties = {}
532
+ vars.properties[key] = {
533
+ enumTitle: enumTitle
534
+ }
535
+ }
536
+ })
537
+ }
538
+
521
539
  const headerText = this.header_template(vars)
522
540
 
523
541
  if (headerText !== this.header_text) {
@@ -75,8 +75,6 @@ export class ArraySelectizeEditor extends MultiSelectEditor {
75
75
  this.option_keys.push(`${value}`)
76
76
  this.option_titles.push(`${value}`)
77
77
  this.select_values[`${value}`] = value
78
- /* Update Schema enum to prevent triggering "Value must be one of the enumerated values" */
79
- this.schema.items.enum.push(value)
80
78
  /* Add new value and label to selectize */
81
79
  this.selectize_instance.addOption({ text: value, value })
82
80
 
@@ -383,36 +383,47 @@ export class ArrayEditor extends AbstractEditor {
383
383
  /* TODO: sortable */
384
384
  }
385
385
 
386
- setVisibility (element, display) {
387
- element.style.display = display ? '' : 'none'
386
+ setButtonState (element, display) {
387
+ const buttonStateMode = this.options.button_state_mode || this.jsoneditor.options.button_state_mode
388
+
389
+ switch (buttonStateMode) {
390
+ case 1:
391
+ element.style.display = display ? '' : 'none'
392
+ break
393
+ case 2:
394
+ element.disabled = !display
395
+ break
396
+ default:
397
+ element.style.display = display ? '' : 'none'
398
+ }
388
399
  }
389
400
 
390
401
  setupButtons (minItems) {
391
402
  const controlsNeeded = []
392
403
 
393
404
  if (!this.value.length) {
394
- this.delete_last_row_button.style.display = 'none'
395
- this.remove_all_rows_button.style.display = 'none'
405
+ this.setButtonState(this.delete_last_row_button, false)
406
+ this.setButtonState(this.remove_all_rows_button, false)
396
407
  } else if (this.value.length === 1) {
397
- this.remove_all_rows_button.style.display = 'none'
408
+ this.setButtonState(this.remove_all_rows_button, false)
398
409
 
399
410
  /* If there are minItems items in the array, or configured to hide the delete_last_row button, hide the delete button beneath the rows */
400
411
  const display = !(minItems || this.hide_delete_last_row_buttons)
401
- this.setVisibility(this.delete_last_row_button, display)
412
+ this.setButtonState(this.delete_last_row_button, display)
402
413
  controlsNeeded.push(display)
403
414
  } else {
404
415
  const display1 = !(minItems || this.hide_delete_last_row_buttons)
405
- this.setVisibility(this.delete_last_row_button, display1)
416
+ this.setButtonState(this.delete_last_row_button, display1)
406
417
  controlsNeeded.push(display1)
407
418
 
408
419
  const display2 = !(minItems || this.hide_delete_all_rows_buttons)
409
- this.setVisibility(this.remove_all_rows_button, display2)
420
+ this.setButtonState(this.remove_all_rows_button, display2)
410
421
  controlsNeeded.push(display2)
411
422
  }
412
423
 
413
424
  /* If there are maxItems in the array, hide the add button beneath the rows */
414
425
  const display = !((this.getMax() && this.getMax() <= this.rows.length) || this.hide_add_button)
415
- this.setVisibility(this.add_row_button, display)
426
+ this.setButtonState(this.add_row_button, display)
416
427
  controlsNeeded.push(display)
417
428
 
418
429
  return controlsNeeded.some(e => e)
@@ -431,12 +442,12 @@ export class ArrayEditor extends AbstractEditor {
431
442
  /* Hide the move down button for the last row */
432
443
  if (editor.movedown_button) {
433
444
  const display = (i !== this.rows.length - 1)
434
- this.setVisibility(editor.movedown_button, display)
445
+ this.setButtonState(editor.movedown_button, display)
435
446
  }
436
447
 
437
448
  /* Hide the delete button if we have minItems items */
438
449
  if (editor.delete_button) {
439
- this.setVisibility(editor.delete_button, !minItems)
450
+ this.setButtonState(editor.delete_button, !minItems)
440
451
  }
441
452
 
442
453
  /* Get the value for this editor */
@@ -677,8 +688,8 @@ export class ArrayEditor extends AbstractEditor {
677
688
  button.addEventListener('click', e => {
678
689
  e.preventDefault()
679
690
  e.stopPropagation()
680
- if (this.panel) this.setVisibility(this.panel, this.collapsed)
681
- if (this.tabs_holder) this.setVisibility(this.tabs_holder, this.collapsed)
691
+ if (this.panel) this.setButtonState(this.panel, this.collapsed)
692
+ if (this.tabs_holder) this.setButtonState(this.tabs_holder, this.collapsed)
682
693
  if (this.collapsed) {
683
694
  this.collapsed = false
684
695
  this.row_holder.style.display = rowHolderDisplay
@@ -44,6 +44,7 @@ export class Base64Editor extends AbstractEditor {
44
44
 
45
45
  /* File uploader */
46
46
  this.uploader = this.theme.getFormInputField('file')
47
+ this.uploader.style.display = 'none'
47
48
 
48
49
  /* Set attribute of file input field to 'multiple' if: */
49
50
  /* 'multiple' key has been set to 'true' in the schema */
@@ -95,6 +96,14 @@ export class Base64Editor extends AbstractEditor {
95
96
 
96
97
  this.control = this.theme.getFormControl(this.label, this.uploader || this.input, this.preview, this.infoButton)
97
98
  this.container.appendChild(this.control)
99
+
100
+ const uploadButton = this.getButton('button_upload', 'upload', 'button_upload')
101
+
102
+ uploadButton.addEventListener('click', () => {
103
+ this.uploader.click()
104
+ })
105
+
106
+ this.control.appendChild(uploadButton)
98
107
  }
99
108
 
100
109
  refreshPreview () {
@@ -24,7 +24,6 @@ export class ButtonEditor extends AbstractEditor {
24
24
 
25
25
  /* Get options, either global options from "this.defaults.options.button" or */
26
26
  /* single property options from schema "options.button" */
27
- const title = this.translateProperty(this.schema.title) || this.key
28
27
  const options = this.expandCallbacks('button', extend({}, {
29
28
  icon: '',
30
29
  validated: false,
@@ -34,8 +33,15 @@ export class ButtonEditor extends AbstractEditor {
34
33
  }
35
34
  }, this.defaults.options.button || {}, this.options.button || {}))
36
35
 
36
+ const title = this.translateProperty(options.text || this.schema.title) || this.key
37
+
37
38
  this.input = this.getButton(title, options.icon, title)
38
- this.input.addEventListener('click', options.action, false)
39
+
40
+ if (typeof options.action !== 'function') {
41
+ window.alert(`No button action defined for "${this.path}"`)
42
+ } else {
43
+ this.input.addEventListener('click', options.action, false)
44
+ }
39
45
 
40
46
  if (this.schema.readOnly || this.schema.readonly || this.schema.template) {
41
47
  this.disable(true)
@@ -12,8 +12,9 @@ export class IntegerEditor extends NumberEditor {
12
12
  }
13
13
 
14
14
  if (!this.schema.default && !this.jsoneditor.options.use_default_values && this.value === '') {
15
- this.input.value = ''
16
- return undefined
15
+ if (this.shouldBeUnset()) {
16
+ return undefined
17
+ }
17
18
  } else {
18
19
  return isInteger(this.value) ? parseInt(this.value) : this.value
19
20
  }
@@ -276,6 +276,9 @@ export class MultipleEditor extends AbstractEditor {
276
276
  }
277
277
 
278
278
  refreshValue () {
279
+ if (!this.editors[this.type]) {
280
+ return
281
+ }
279
282
  this.value = this.editors[this.type].getValue()
280
283
  }
281
284
 
@@ -44,8 +44,10 @@ export class NumberEditor extends StringEditor {
44
44
  }
45
45
 
46
46
  if (!this.schema.default && !this.jsoneditor.options.use_default_values && this.value === '') {
47
- this.input.value = ''
48
- return undefined
47
+ if (this.shouldBeUnset()) {
48
+ this.input.value = ''
49
+ return undefined
50
+ }
49
51
  } else {
50
52
  return isNumber(this.value) ? parseFloat(this.value) : this.value
51
53
  }
@@ -624,7 +624,17 @@ export class ObjectEditor extends AbstractEditor {
624
624
  })
625
625
  this.addproperty_input.addEventListener('input', (e) => {
626
626
  e.target.previousSibling.childNodes.forEach((value) => {
627
- if (value.innerText.includes(e.target.value)) {
627
+ let searchTerm = value.innerText
628
+ let propertyTitle = e.target.value
629
+
630
+ const caseSensitivePropertySearch = this.options.case_sensitive_property_search || this.jsoneditor.options.case_sensitive_property_search
631
+
632
+ if (!caseSensitivePropertySearch) {
633
+ searchTerm = searchTerm.toLowerCase()
634
+ propertyTitle = propertyTitle.toLowerCase()
635
+ }
636
+
637
+ if (searchTerm.includes(propertyTitle)) {
628
638
  value.style.display = ''
629
639
  } else {
630
640
  value.style.display = 'none'
@@ -1058,10 +1068,31 @@ export class ObjectEditor extends AbstractEditor {
1058
1068
  }
1059
1069
 
1060
1070
  canHaveAdditionalProperties () {
1071
+ // schemas have priority over options
1072
+ // local options have priority over global options
1073
+ // lastly global options are evaluated
1074
+
1075
+ // If the schema additionalProperties keyword is a boolean let the keyword decide
1061
1076
  if (typeof this.schema.additionalProperties === 'boolean') {
1062
1077
  return this.schema.additionalProperties
1063
1078
  }
1064
- return !this.jsoneditor.options.no_additional_properties
1079
+
1080
+ // If the schema additionalProperties keyword is a schema then additional properties are allowed and limited by such schema
1081
+ if (typeof this.schema.additionalProperties === 'object' && this.schema.additionalProperties !== null) {
1082
+ return true
1083
+ }
1084
+
1085
+ // If the schema options no_additional_properties is a boolean let the option decide
1086
+ if (typeof this.options.no_additional_properties === 'boolean') {
1087
+ return !this.options.no_additional_properties
1088
+ }
1089
+
1090
+ // If the global options no_additional_properties is a boolean let the option decide
1091
+ if (typeof this.jsoneditor.options.no_additional_properties === 'boolean') {
1092
+ return !this.jsoneditor.options.no_additional_properties
1093
+ }
1094
+
1095
+ return true
1065
1096
  }
1066
1097
 
1067
1098
  destroy () {
@@ -1109,11 +1140,47 @@ export class ObjectEditor extends AbstractEditor {
1109
1140
 
1110
1141
  Object.keys(this.editors).forEach(i => {
1111
1142
  if (this.editors[i].isActive()) {
1143
+ this.editors[i].refreshValue()
1112
1144
  this.value[i] = this.editors[i].getValue()
1113
1145
  }
1114
1146
  })
1115
1147
 
1116
- if (this.adding_property) this.refreshAddProperties()
1148
+ Object.keys(this.editors).forEach(i => {
1149
+ if (this.editors[i].isActive()) {
1150
+ this.activateDependentRequired(this.editors[i].key)
1151
+ }
1152
+ })
1153
+
1154
+ if (this.adding_property) {
1155
+ this.refreshAddProperties()
1156
+ }
1157
+ }
1158
+
1159
+ activateDependentRequired (key) {
1160
+ const dependentRequired = this.getDependentRequired(key)
1161
+ dependentRequired.forEach((requiredProperty) => {
1162
+ let dependentRequiredEditor
1163
+
1164
+ Object.entries(this.cached_editors).forEach(([i, cachedEditor]) => {
1165
+ if (cachedEditor.key === requiredProperty) {
1166
+ dependentRequiredEditor = cachedEditor
1167
+ }
1168
+ })
1169
+
1170
+ if (dependentRequiredEditor && !dependentRequiredEditor.isActive()) {
1171
+ dependentRequiredEditor.activate()
1172
+ }
1173
+ })
1174
+ }
1175
+
1176
+ getDependentRequired (property) {
1177
+ if (this.schema.dependentRequired) {
1178
+ if (hasOwnProperty(this.schema.dependentRequired, property)) {
1179
+ return this.schema.dependentRequired[property]
1180
+ }
1181
+ }
1182
+
1183
+ return []
1117
1184
  }
1118
1185
 
1119
1186
  refreshAddProperties () {
@@ -1190,6 +1257,7 @@ export class ObjectEditor extends AbstractEditor {
1190
1257
  if (!editor) {
1191
1258
  return
1192
1259
  }
1260
+
1193
1261
  if (typeof editor.schema.required === 'boolean') return editor.schema.required
1194
1262
  else if (Array.isArray(this.schema.required)) return this.schema.required.includes(editor.key)
1195
1263
  else if (this.jsoneditor.options.required_by_default) return true
@@ -26,23 +26,23 @@ export class SignatureEditor extends StringEditor {
26
26
  canvas.classList.add('signature')
27
27
  signatureContainer.appendChild(canvas)
28
28
 
29
- this.signaturePad = new window.SignaturePad(canvas, {
30
- onEnd () {
31
- /* check if the signature is not empty before setting a value */
32
- if (!this.signaturePad.isEmpty()) {
33
- this.input.value = this.signaturePad.toDataURL()
34
- } else {
35
- this.input.value = ''
36
- }
37
-
38
- this.is_dirty = true
39
- this.refreshValue()
40
- this.watch_listener()
41
- this.jsoneditor.notifyWatchers(this.path)
42
- if (this.parent) this.parent.onChildEditorChange(this)
43
- else this.jsoneditor.onChange()
29
+ this.signaturePad = new window.SignaturePad(canvas)
30
+
31
+ this.signaturePad.onEnd = () => {
32
+ /* check if the signature is not empty before setting a value */
33
+ if (!this.signaturePad.isEmpty()) {
34
+ this.input.value = this.signaturePad.toDataURL()
35
+ } else {
36
+ this.input.value = ''
44
37
  }
45
- })
38
+
39
+ this.is_dirty = true
40
+ this.refreshValue()
41
+ this.watch_listener()
42
+ this.jsoneditor.notifyWatchers(this.path)
43
+ if (this.parent) this.parent.onChildEditorChange(this)
44
+ else this.jsoneditor.onChange()
45
+ }
46
46
 
47
47
  /* create button containers and add clear signature button */
48
48
  const buttons = document.createElement('div')
@@ -23,7 +23,8 @@ export class SimplemdeEditor extends StringEditor {
23
23
  options = this.expandCallbacks('simplemde', extend({}, {
24
24
  height: 300
25
25
  }, this.defaults.options.simplemde || {}, this.options.simplemde || {}, {
26
- element: this.input
26
+ element: this.input,
27
+ forceSync: true
27
28
  }))
28
29
 
29
30
  this.simplemde_instance = new window.SimpleMDE(options)
@@ -291,6 +291,7 @@ export class StringEditor extends AbstractEditor {
291
291
 
292
292
  getValue () {
293
293
  const hasValueSet = !!(this.input && this.input.value)
294
+
294
295
  if (this.shouldBeUnset() && !hasValueSet) {
295
296
  return undefined
296
297
  }
@@ -319,6 +320,9 @@ export class StringEditor extends AbstractEditor {
319
320
  }
320
321
 
321
322
  refreshValue () {
323
+ if (!this.input) {
324
+ return
325
+ }
322
326
  this.value = this.input.value
323
327
  if (typeof this.value !== 'string' && !this.shouldBeUnset()) this.value = ''
324
328
  this.serialized = this.value
@@ -36,8 +36,11 @@ export class TableEditor extends ArrayEditor {
36
36
  }
37
37
 
38
38
  build () {
39
+ this.tableContainer = this.theme.getTableContainer()
39
40
  this.table = this.theme.getTable()
40
- this.container.appendChild(this.table)
41
+ this.tableContainer.appendChild(this.table)
42
+
43
+ this.container.appendChild(this.tableContainer)
41
44
  this.thead = this.theme.getTableHead()
42
45
  this.table.appendChild(this.thead)
43
46
  this.header_row = this.theme.getTableRow()
@@ -74,7 +77,7 @@ export class TableEditor extends ArrayEditor {
74
77
  this.container.appendChild(this.panel)
75
78
  }
76
79
 
77
- this.panel.appendChild(this.table)
80
+ this.panel.appendChild(this.tableContainer)
78
81
  this.controls = this.theme.getButtonHolder()
79
82
  if (this.array_controls_top) {
80
83
  this.title.appendChild(this.controls)
@@ -232,28 +235,28 @@ export class TableEditor extends ArrayEditor {
232
235
  if (editor.delete_button) {
233
236
  /* Hide the delete button if we have minItems items */
234
237
  const display = !minItems
235
- this.setVisibility(editor.delete_button, display)
238
+ this.setButtonState(editor.delete_button, display)
236
239
  needRowButtons.push(display)
237
240
  }
238
241
 
239
242
  if (editor.copy_button) {
240
243
  /* Hide the copy button if we have maxItems items */
241
244
  const display = !maxItems
242
- this.setVisibility(editor.copy_button, display)
245
+ this.setButtonState(editor.copy_button, display)
243
246
  needRowButtons.push(display)
244
247
  }
245
248
 
246
249
  if (editor.moveup_button) {
247
250
  /* Hide the moveup button for the first row */
248
251
  const display = i !== 0
249
- this.setVisibility(editor.moveup_button, display)
252
+ this.setButtonState(editor.moveup_button, display)
250
253
  needRowButtons.push(display)
251
254
  }
252
255
 
253
256
  if (editor.movedown_button) {
254
257
  /* Hide the movedown button for the last row */
255
258
  const display = i !== this.rows.length - 1
256
- this.setVisibility(editor.movedown_button, display)
259
+ this.setButtonState(editor.movedown_button, display)
257
260
  needRowButtons.push(display)
258
261
  }
259
262
  })
@@ -261,26 +264,26 @@ export class TableEditor extends ArrayEditor {
261
264
  const need = needRowButtons.some(e => e)
262
265
  /* Show/hide controls column in table */
263
266
  this.rows.forEach((editor) =>
264
- this.setVisibility(editor.controls_cell, need)
267
+ this.setButtonState(editor.controls_cell, need)
265
268
  )
266
- this.setVisibility(this.controls_header_cell, need)
269
+ this.setButtonState(this.controls_header_cell, need)
267
270
 
268
- this.setVisibility(this.table, this.value.length)
271
+ this.setButtonState(this.table, this.value.length)
269
272
 
270
273
  /* If there are maxItems items in the array, or configured to hide the add_row_button button, hide the button beneath the rows */
271
274
  const display1 = !(maxItems || this.hide_add_button)
272
- this.setVisibility(this.add_row_button, display1)
275
+ this.setButtonState(this.add_row_button, display1)
273
276
 
274
277
  /* If there are minItems items in the array, or configured to hide the delete_last_row button, hide the button beneath the rows */
275
278
  const display2 = !(!this.value.length || minItems || this.hide_delete_last_row_buttons)
276
- this.setVisibility(this.delete_last_row_button, display2)
279
+ this.setButtonState(this.delete_last_row_button, display2)
277
280
 
278
281
  /* If there are minItems items in the array, or configured to hide the remove_all_rows_button button, hide the button beneath the rows */
279
282
  const display3 = !(this.value.length <= 1 || minItems || this.hide_delete_all_rows_buttons)
280
- this.setVisibility(this.remove_all_rows_button, display3)
283
+ this.setButtonState(this.remove_all_rows_button, display3)
281
284
 
282
285
  const controlsNeeded = display1 || display2 || display3
283
- this.setVisibility(this.controls, controlsNeeded)
286
+ this.setButtonState(this.controls, controlsNeeded)
284
287
  }
285
288
 
286
289
  refreshValue () {
@@ -439,7 +442,7 @@ export class TableEditor extends ArrayEditor {
439
442
  e.preventDefault()
440
443
  e.stopPropagation()
441
444
 
442
- this.setVisibility(this.panel, this.collapsed)
445
+ this.setButtonState(this.panel, this.collapsed)
443
446
  if (this.collapsed) {
444
447
  this.collapsed = false
445
448
  this.setButtonText(e.currentTarget, '', 'collapse', 'button_collapse')