@json-editor/json-editor 2.5.1 → 2.6.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 (92) hide show
  1. package/.eslintrc +5 -2
  2. package/.github/PULL_REQUEST_TEMPLATE.md +6 -6
  3. package/.github/workflows/build.yml +58 -0
  4. package/CHANGELOG.md +41 -1
  5. package/CONTRIBUTING.md +1 -1
  6. package/README.md +12 -4
  7. package/README_ADDON.md +65 -0
  8. package/config/codeceptjs_helpers.js +4 -0
  9. package/dist/jsoneditor.js +2 -2
  10. package/dist/nonmin/jsoneditor.js +3849 -3304
  11. package/dist/nonmin/jsoneditor.js.map +1 -1
  12. package/docs/index.html +2 -0
  13. package/docs/meta_schema.json +0 -1
  14. package/package.json +27 -26
  15. package/release-notes.md +9 -0
  16. package/src/core.js +1 -0
  17. package/src/defaults.js +182 -94
  18. package/src/editor.js +32 -12
  19. package/src/editors/array.js +20 -16
  20. package/src/editors/autocomplete.js +1 -0
  21. package/src/editors/base64.js +5 -4
  22. package/src/editors/button.js +2 -2
  23. package/src/editors/checkbox.js +3 -3
  24. package/src/editors/datetime.js +2 -2
  25. package/src/editors/info.js +1 -1
  26. package/src/editors/multiple.js +8 -2
  27. package/src/editors/multiselect.js +5 -3
  28. package/src/editors/object.js +32 -17
  29. package/src/editors/radio.js +9 -4
  30. package/src/editors/select.js +6 -6
  31. package/src/editors/signature.js +3 -2
  32. package/src/editors/starrating.js +5 -5
  33. package/src/editors/stepper.js +3 -0
  34. package/src/editors/string.js +6 -4
  35. package/src/editors/table.js +24 -14
  36. package/src/editors/upload.js +4 -3
  37. package/src/editors/uuid.js +1 -1
  38. package/src/iconlibs/index.js +2 -0
  39. package/src/iconlibs/openiconic.js +28 -0
  40. package/src/schemaloader.js +112 -28
  41. package/src/theme.js +36 -5
  42. package/src/themes/bootstrap3.js +4 -4
  43. package/src/themes/bootstrap4.js +41 -5
  44. package/src/themes/html.js +1 -2
  45. package/src/themes/materialize.js +1 -1
  46. package/src/themes/spectre.js +11 -8
  47. package/src/themes/tailwind.js +1 -1
  48. package/src/validator.js +129 -17
  49. package/tests/codeceptjs/core_test.js +204 -50
  50. package/tests/codeceptjs/editors/array_test.js +13 -11
  51. package/tests/codeceptjs/editors/button_test.js +6 -1
  52. package/tests/codeceptjs/editors/issues/issue-gh-812_test.js +32 -0
  53. package/tests/codeceptjs/editors/number_test.js +1 -1
  54. package/tests/codeceptjs/editors/object_test.js +194 -98
  55. package/tests/codeceptjs/editors/programmatic-changes_test.js +3 -1
  56. package/tests/codeceptjs/editors/radio_test.js +10 -0
  57. package/tests/codeceptjs/editors/rating_test.js +10 -11
  58. package/tests/codeceptjs/editors/select_test.js +17 -15
  59. package/tests/codeceptjs/editors/stepper_test.js +13 -1
  60. package/tests/codeceptjs/editors/string_test.js +81 -80
  61. package/tests/codeceptjs/editors/table-confirm-delete_test.js +58 -56
  62. package/tests/codeceptjs/editors/tabs_test.js +12 -10
  63. package/tests/codeceptjs/editors/validation_test.js +10 -8
  64. package/tests/codeceptjs/meta-schema_test.js +13 -14
  65. package/tests/codeceptjs/schemaloader_test.js +13 -0
  66. package/tests/codeceptjs/steps_file.js +4 -3
  67. package/tests/codeceptjs/themes_test.js +31 -0
  68. package/tests/docker-compose.yml +4 -3
  69. package/tests/fixtures/validation.json +382 -1
  70. package/tests/pages/_demo.html +2 -0
  71. package/tests/pages/anyof.html +80 -0
  72. package/tests/pages/form-name.html +108 -0
  73. package/tests/pages/issues/issue-gh-812.html +110 -0
  74. package/tests/pages/issues/issue-gh-848.html +81 -0
  75. package/tests/pages/meta_schema.json +0 -1
  76. package/tests/pages/object-no-additional-properties.html +27 -12
  77. package/tests/pages/object-required-properties.html +43 -9
  78. package/tests/pages/object-show-opt-in.html +110 -0
  79. package/tests/pages/object-with-dependencies-array.html +46 -0
  80. package/tests/pages/oneof.html +103 -0
  81. package/tests/pages/read-only.html +19 -4
  82. package/tests/pages/stepper-manual.html +57 -0
  83. package/tests/pages/themes.html +2 -0
  84. package/tests/pages/translate-property.html +247 -0
  85. package/tests/pages/urn.html +93 -0
  86. package/tests/unit/core.spec.js +2 -0
  87. package/tests/unit/defaults.spec.js +4 -2
  88. package/tests/unit/editor.spec.js +2 -0
  89. package/tests/unit/editors/array.spec.js +86 -0
  90. package/tests/unit/editors/table.spec.js +91 -0
  91. package/tests/unit/schemaloader.spec.js +362 -3
  92. package/tests/unit/validator.spec.js +14 -2
@@ -95,12 +95,16 @@ export class ArrayEditor extends AbstractEditor {
95
95
  if (!this.options.compact) {
96
96
  this.header = document.createElement('label')
97
97
  this.header.textContent = this.getTitle()
98
- this.title = this.theme.getHeader(this.header)
98
+ this.title = this.theme.getHeader(this.header, this.getPathDepth())
99
99
  this.container.appendChild(this.title)
100
+ if (this.options.infoText) {
101
+ this.infoButton = this.theme.getInfoButton(this.translateProperty(this.options.infoText))
102
+ this.container.appendChild(this.infoButton)
103
+ }
100
104
  this.title_controls = this.theme.getHeaderButtonHolder()
101
105
  this.title.appendChild(this.title_controls)
102
106
  if (this.schema.description) {
103
- this.description = this.theme.getDescription(this.schema.description)
107
+ this.description = this.theme.getDescription(this.translateProperty(this.schema.description))
104
108
  this.container.appendChild(this.description)
105
109
  }
106
110
  this.error_holder = document.createElement('div')
@@ -136,7 +140,7 @@ export class ArrayEditor extends AbstractEditor {
136
140
  }
137
141
  } else {
138
142
  /* compact mode */
139
- this.title = this.theme.getHeader('')
143
+ this.title = this.theme.getHeader('', this.getPathDepth())
140
144
  this.container.appendChild(this.title)
141
145
  this.panel = this.theme.getIndentedPanel()
142
146
  this.container.appendChild(this.panel)
@@ -162,7 +166,7 @@ export class ArrayEditor extends AbstractEditor {
162
166
  if (!this.item_title) {
163
167
  if (this.schema.items && !Array.isArray(this.schema.items)) {
164
168
  const tmp = this.jsoneditor.expandRefs(this.schema.items)
165
- this.item_title = tmp.title || this.translate('default_array_item_title')
169
+ this.item_title = this.translateProperty(tmp.title) || this.translate('default_array_item_title')
166
170
  } else {
167
171
  this.item_title = this.translate('default_array_item_title')
168
172
  }
@@ -200,7 +204,7 @@ export class ArrayEditor extends AbstractEditor {
200
204
  schema = this.jsoneditor.expandRefs(schema)
201
205
 
202
206
  this.item_info[stringified] = {
203
- title: schema.title || this.translate('default_array_item_title'),
207
+ title: this.translateProperty(schema.title) || this.translate('default_array_item_title'),
204
208
  default: schema.default,
205
209
  width: 12,
206
210
  child_editors: schema.properties || schema.items
@@ -492,14 +496,14 @@ export class ArrayEditor extends AbstractEditor {
492
496
  this.rows[i].movedown_button = this._createMoveDownButton(i, controlsHolder)
493
497
  }
494
498
 
495
- if (value) this.rows[i].setValue(value, initial)
499
+ if (typeof value !== 'undefined') this.rows[i].setValue(value, initial)
496
500
  this.refreshTabs()
497
501
 
498
502
  return this.rows[i]
499
503
  }
500
504
 
501
505
  _createDeleteButton (i, holder) {
502
- const button = this.getButton(this.getItemTitle(), 'delete', this.translate('button_delete_row_title', [this.getItemTitle()]))
506
+ const button = this.getButton(this.getItemTitle(), 'delete', 'button_delete_row_title', [this.getItemTitle()])
503
507
  button.classList.add('delete', 'json-editor-btntype-delete')
504
508
  button.setAttribute('data-i', i)
505
509
  button.addEventListener('click', e => {
@@ -540,7 +544,7 @@ export class ArrayEditor extends AbstractEditor {
540
544
  }
541
545
 
542
546
  _createCopyButton (i, holder) {
543
- const button = this.getButton(this.getItemTitle(), 'copy', `Copy ${this.getItemTitle()}`)
547
+ const button = this.getButton(this.getItemTitle(), 'copy', 'button_copy_row_title', [this.getItemTitle()])
544
548
  button.classList.add('copy', 'json-editor-btntype-copy')
545
549
  button.setAttribute('data-i', i)
546
550
  button.addEventListener('click', e => {
@@ -565,7 +569,7 @@ export class ArrayEditor extends AbstractEditor {
565
569
  }
566
570
 
567
571
  _createMoveUpButton (i, holder) {
568
- const button = this.getButton('', (this.schema.format === 'tabs-top' ? 'moveleft' : 'moveup'), this.translate('button_move_up_title'))
572
+ const button = this.getButton('', (this.schema.format === 'tabs-top' ? 'moveleft' : 'moveup'), 'button_move_up_title')
569
573
  button.classList.add('moveup', 'json-editor-btntype-move')
570
574
  button.setAttribute('data-i', i)
571
575
  button.addEventListener('click', e => {
@@ -595,7 +599,7 @@ export class ArrayEditor extends AbstractEditor {
595
599
  }
596
600
 
597
601
  _createMoveDownButton (i, holder) {
598
- const button = this.getButton('', (this.schema.format === 'tabs-top' ? 'moveright' : 'movedown'), this.translate('button_move_down_title'))
602
+ const button = this.getButton('', (this.schema.format === 'tabs-top' ? 'moveright' : 'movedown'), 'button_move_down_title')
599
603
  button.classList.add('movedown', 'json-editor-btntype-move')
600
604
  button.setAttribute('data-i', i)
601
605
  button.addEventListener('click', e => {
@@ -652,7 +656,7 @@ export class ArrayEditor extends AbstractEditor {
652
656
  }
653
657
 
654
658
  _createToggleButton () {
655
- const button = this.getButton('', 'collapse', this.translate('button_collapse'))
659
+ const button = this.getButton('', 'collapse', 'button_collapse')
656
660
  button.classList.add('json-editor-btntype-toggle')
657
661
  this.title.insertBefore(button, this.title.childNodes[0])
658
662
 
@@ -667,19 +671,19 @@ export class ArrayEditor extends AbstractEditor {
667
671
  this.collapsed = false
668
672
  this.row_holder.style.display = rowHolderDisplay
669
673
  this.controls.style.display = controlsDisplay
670
- this.setButtonText(e.currentTarget, '', 'collapse', this.translate('button_collapse'))
674
+ this.setButtonText(e.currentTarget, '', 'collapse', 'button_collapse')
671
675
  } else {
672
676
  this.collapsed = true
673
677
  this.row_holder.style.display = 'none'
674
678
  this.controls.style.display = 'none'
675
- this.setButtonText(e.currentTarget, '', 'expand', this.translate('button_expand'))
679
+ this.setButtonText(e.currentTarget, '', 'expand', 'button_expand')
676
680
  }
677
681
  })
678
682
  return button
679
683
  }
680
684
 
681
685
  _createAddRowButton () {
682
- const button = this.getButton(this.getItemTitle(), 'add', this.translate('button_add_row_title', [this.getItemTitle()]))
686
+ const button = this.getButton(this.getItemTitle(), 'add', 'button_add_row_title', [this.getItemTitle()])
683
687
  button.classList.add('json-editor-btntype-add')
684
688
  button.addEventListener('click', (e) => {
685
689
  e.preventDefault()
@@ -706,7 +710,7 @@ export class ArrayEditor extends AbstractEditor {
706
710
  }
707
711
 
708
712
  _createDeleteLastRowButton () {
709
- const button = this.getButton(this.translate('button_delete_last', [this.getItemTitle()]), 'subtract', this.translate('button_delete_last_title', [this.getItemTitle()]))
713
+ const button = this.getButton('button_delete_last', 'subtract', 'button_delete_last_title', [this.getItemTitle()])
710
714
  button.classList.add('json-editor-btntype-deletelast')
711
715
  button.addEventListener('click', (e) => {
712
716
  e.preventDefault()
@@ -740,7 +744,7 @@ export class ArrayEditor extends AbstractEditor {
740
744
  }
741
745
 
742
746
  _createRemoveAllRowsButton () {
743
- const button = this.getButton(this.translate('button_delete_all'), 'delete', this.translate('button_delete_all_title'))
747
+ const button = this.getButton('button_delete_all', 'delete', 'button_delete_all_title')
744
748
  button.classList.add('json-editor-btntype-deleteall')
745
749
  button.addEventListener('click', (e) => {
746
750
  e.preventDefault()
@@ -30,6 +30,7 @@ export class AutocompleteEditor extends StringEditor {
30
30
  /* single property options from schema "options.autocomplete" */
31
31
  options = this.expandCallbacks('autocomplete', extend({}, {
32
32
  search: (jseditor, input) => {
33
+ // eslint-disable-next-line no-console
33
34
  console.log(`No "search" callback defined for autocomplete in property "${jseditor.key}"`)
34
35
  return []
35
36
  },
@@ -31,8 +31,8 @@ export class Base64Editor extends AbstractEditor {
31
31
  }
32
32
 
33
33
  build () {
34
- this.title = this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired())
35
- if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText)
34
+ if (!this.options.compact) this.title = this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired())
35
+ if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.translateProperty(this.options.infoText))
36
36
 
37
37
  /* Input that holds the base64 string */
38
38
  this.input = this.theme.getFormInputField('hidden')
@@ -90,7 +90,7 @@ export class Base64Editor extends AbstractEditor {
90
90
  })
91
91
  }
92
92
 
93
- this.preview = this.theme.getFormInputDescription(this.schema.description)
93
+ this.preview = this.theme.getFormInputDescription(this.translateProperty(this.schema.description))
94
94
  this.container.appendChild(this.preview)
95
95
 
96
96
  this.control = this.theme.getFormControl(this.label, this.uploader || this.input, this.preview, this.infoButton)
@@ -138,7 +138,8 @@ export class Base64Editor extends AbstractEditor {
138
138
 
139
139
  setValue (val) {
140
140
  if (this.value !== val) {
141
- this.value = val
141
+ if (this.schema.readOnly && this.schema.enum && !this.schema.enum.includes(val)) this.value = this.schema.enum[0]
142
+ else this.value = val
142
143
  this.input.value = this.value
143
144
  this.refreshPreview()
144
145
  this.onChange()
@@ -24,7 +24,7 @@ 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.schema.title || this.key
27
+ const title = this.translateProperty(this.schema.title) || this.key
28
28
  const options = this.expandCallbacks('button', extend({}, {
29
29
  icon: '',
30
30
  validated: false,
@@ -38,7 +38,7 @@ export class ButtonEditor extends AbstractEditor {
38
38
  this.input.addEventListener('click', options.action, false)
39
39
 
40
40
  if (this.schema.readOnly || this.schema.readonly || this.schema.template) {
41
- this.always_disabled = true
41
+ this.disable(true)
42
42
  this.input.setAttribute('readonly', 'true')
43
43
  }
44
44
 
@@ -31,8 +31,8 @@ export class CheckboxEditor extends AbstractEditor {
31
31
  this.label.htmlFor = this.formname
32
32
  }
33
33
 
34
- if (this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description)
35
- if (this.options.infoText && !this.options.compact) this.infoButton = this.theme.getInfoButton(this.options.infoText)
34
+ if (this.schema.description) this.description = this.theme.getFormInputDescription(this.translateProperty(this.schema.description))
35
+ if (this.options.infoText && !this.options.compact) this.infoButton = this.theme.getInfoButton(this.translateProperty(this.options.infoText))
36
36
  if (this.options.compact) this.container.classList.add('compact')
37
37
 
38
38
  this.input = this.theme.getCheckbox()
@@ -40,7 +40,7 @@ export class CheckboxEditor extends AbstractEditor {
40
40
  this.control = this.theme.getFormControl(this.label, this.input, this.description, this.infoButton)
41
41
 
42
42
  if (this.schema.readOnly || this.schema.readonly) {
43
- this.always_disabled = true
43
+ this.disable(true)
44
44
  this.input.disabled = true
45
45
  }
46
46
 
@@ -51,13 +51,13 @@ export class DatetimeEditor extends StringEditor {
51
51
  /* Create buttons for input group */
52
52
  const buttons = []
53
53
  if (this.options.flatpickr.showToggleButton !== false) {
54
- const toggleButton = this.getButton('', this.schema.format === 'time' ? 'time' : 'calendar', this.translate('flatpickr_toggle_button'))
54
+ const toggleButton = this.getButton('', this.schema.format === 'time' ? 'time' : 'calendar', 'flatpickr_toggle_button')
55
55
  /* Attribute for flatpicker */
56
56
  toggleButton.setAttribute('data-toggle', '')
57
57
  buttons.push(toggleButton)
58
58
  }
59
59
  if (this.options.flatpickr.showClearButton !== false) {
60
- const clearButton = this.getButton('', 'clear', this.translate('flatpickr_clear_button'))
60
+ const clearButton = this.getButton('', 'clear', 'flatpickr_clear_button')
61
61
  /* Attribute for flatpicker */
62
62
  clearButton.setAttribute('data-clear', '')
63
63
  buttons.push(clearButton)
@@ -11,7 +11,7 @@ export class InfoEditor extends ButtonEditor {
11
11
  }
12
12
 
13
13
  getTitle () {
14
- return this.schema.title
14
+ return this.translateProperty(this.schema.title)
15
15
  }
16
16
 
17
17
  getNumColumns () {
@@ -267,6 +267,8 @@ export class MultipleEditor extends AbstractEditor {
267
267
  if (fitTestResult !== null) {
268
268
  validVal.match = fitTestResult.match
269
269
  }
270
+ } else {
271
+ fitTestVal = validVal
270
272
  }
271
273
  })
272
274
  let finalI = validVal.i
@@ -311,9 +313,13 @@ export class MultipleEditor extends AbstractEditor {
311
313
  if (!editor) return
312
314
  const check = `${this.path}.${checkPart}[${i}]`
313
315
  const filterError = (newErrors, error) => {
314
- if (error.path === check.substr(0, error.path.length)) {
316
+ if (error.path.startsWith(check) || error.path === check.substr(0, error.path.length)) {
315
317
  const newError = extend({}, error)
316
- newError.path = this.path + newError.path.substr(check.length)
318
+
319
+ if (error.path.startsWith(check)) {
320
+ newError.path = this.path + newError.path.substr(check.length)
321
+ }
322
+
317
323
  newErrors.push(newError)
318
324
  }
319
325
  return newErrors
@@ -51,8 +51,8 @@ export class MultiSelectEditor extends AbstractEditor {
51
51
  build () {
52
52
  let i
53
53
  if (!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired())
54
- if (this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description)
55
- if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText)
54
+ if (this.schema.description) this.description = this.theme.getFormInputDescription(this.translateProperty(this.schema.description))
55
+ if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.translateProperty(this.options.infoText))
56
56
  if (this.options.compact) this.container.classList.add('compact')
57
57
 
58
58
  if ((!this.schema.format && this.option_keys.length < 8) || this.schema.format === 'checkbox') {
@@ -86,7 +86,9 @@ export class MultiSelectEditor extends AbstractEditor {
86
86
  this.control = this.theme.getFormControl(this.label, this.input, this.description, this.infoButton)
87
87
  }
88
88
 
89
- if (this.schema.readOnly || this.schema.readonly) this.disable(true)
89
+ if (this.schema.readOnly || this.schema.readonly) {
90
+ this.disable(true)
91
+ }
90
92
 
91
93
  this.container.appendChild(this.control)
92
94
 
@@ -230,7 +230,7 @@ export class ObjectEditor extends AbstractEditor {
230
230
  const containerSimple = document.createElement('div')
231
231
  /* This will be the place to (re)build tabs and panes */
232
232
  /* tabs_holder has 2 childs, [0]: ul.nav.nav-tabs and [1]: div.tab-content */
233
- const newTabsHolder = this.theme.getTopTabHolder(this.schema.title)
233
+ const newTabsHolder = this.theme.getTopTabHolder(this.translateProperty(this.schema.title))
234
234
  /* child [1] of previous, stores panes */
235
235
  const newTabPanesContainer = this.theme.getTopTabContentHolder(newTabsHolder)
236
236
 
@@ -555,7 +555,7 @@ export class ObjectEditor extends AbstractEditor {
555
555
  this.header = document.createElement('label')
556
556
  this.header.textContent = this.getTitle()
557
557
  }
558
- this.title = this.theme.getHeader(this.header)
558
+ this.title = this.theme.getHeader(this.header, this.getPathDepth())
559
559
  this.title.classList.add('je-object__title')
560
560
  this.controls = this.theme.getButtonHolder()
561
561
  this.controls.classList.add('je-object__controls')
@@ -568,21 +568,21 @@ export class ObjectEditor extends AbstractEditor {
568
568
  this.editjson_holder = this.theme.getModal()
569
569
  this.editjson_textarea = this.theme.getTextareaInput()
570
570
  this.editjson_textarea.classList.add('je-edit-json--textarea')
571
- this.editjson_save = this.getButton('Save', 'save', 'Save')
571
+ this.editjson_save = this.getButton('button_save', 'save', 'button_save')
572
572
  this.editjson_save.classList.add('json-editor-btntype-save')
573
573
  this.editjson_save.addEventListener('click', (e) => {
574
574
  e.preventDefault()
575
575
  e.stopPropagation()
576
576
  this.saveJSON()
577
577
  })
578
- this.editjson_copy = this.getButton('Copy', 'copy', 'Copy')
578
+ this.editjson_copy = this.getButton('button_copy', 'copy', 'button_copy')
579
579
  this.editjson_copy.classList.add('json-editor-btntype-copy')
580
580
  this.editjson_copy.addEventListener('click', (e) => {
581
581
  e.preventDefault()
582
582
  e.stopPropagation()
583
583
  this.copyJSON()
584
584
  })
585
- this.editjson_cancel = this.getButton('Cancel', 'cancel', 'Cancel')
585
+ this.editjson_cancel = this.getButton('button_cancel', 'cancel', 'button_cancel')
586
586
  this.editjson_cancel.classList.add('json-editor-btntype-cancel')
587
587
  this.editjson_cancel.addEventListener('click', (e) => {
588
588
  e.preventDefault()
@@ -598,7 +598,7 @@ export class ObjectEditor extends AbstractEditor {
598
598
  this.addproperty_holder = this.theme.getModal()
599
599
  this.addproperty_list = document.createElement('div')
600
600
  this.addproperty_list.classList.add('property-selector')
601
- this.addproperty_add = this.getButton('add', 'add', 'add')
601
+ this.addproperty_add = this.getButton('button_add', 'add', 'button_add')
602
602
  this.addproperty_add.classList.add('json-editor-btntype-add')
603
603
 
604
604
  this.addproperty_input = this.theme.getFormInputField('text')
@@ -641,7 +641,7 @@ export class ObjectEditor extends AbstractEditor {
641
641
 
642
642
  /* Description */
643
643
  if (this.schema.description) {
644
- this.description = this.theme.getDescription(this.schema.description)
644
+ this.description = this.theme.getDescription(this.translateProperty(this.schema.description))
645
645
  this.container.appendChild(this.description)
646
646
  }
647
647
 
@@ -657,11 +657,11 @@ export class ObjectEditor extends AbstractEditor {
657
657
  this.row_container = this.theme.getGridContainer()
658
658
 
659
659
  if (isCategoriesFormat) {
660
- this.tabs_holder = this.theme.getTopTabHolder(this.getValidId(this.schema.title))
660
+ this.tabs_holder = this.theme.getTopTabHolder(this.getValidId(this.translateProperty(this.schema.title)))
661
661
  this.tabPanesContainer = this.theme.getTopTabContentHolder(this.tabs_holder)
662
662
  this.editor_holder.appendChild(this.tabs_holder)
663
663
  } else {
664
- this.tabs_holder = this.theme.getTabHolder(this.getValidId(this.schema.title))
664
+ this.tabs_holder = this.theme.getTabHolder(this.getValidId(this.translateProperty(this.schema.title)))
665
665
  this.tabPanesContainer = this.theme.getTabContentHolder(this.tabs_holder)
666
666
  this.editor_holder.appendChild(this.row_container)
667
667
  }
@@ -711,7 +711,7 @@ export class ObjectEditor extends AbstractEditor {
711
711
 
712
712
  /* Show/Hide button */
713
713
  this.collapsed = false
714
- this.collapse_control = this.getButton('', 'collapse', this.translate('button_collapse'))
714
+ this.collapse_control = this.getButton('', 'collapse', 'button_collapse')
715
715
  this.collapse_control.classList.add('json-editor-btntype-toggle')
716
716
  this.title.insertBefore(this.collapse_control, this.title.childNodes[0])
717
717
 
@@ -721,11 +721,11 @@ export class ObjectEditor extends AbstractEditor {
721
721
  if (this.collapsed) {
722
722
  this.editor_holder.style.display = ''
723
723
  this.collapsed = false
724
- this.setButtonText(this.collapse_control, '', 'collapse', this.translate('button_collapse'))
724
+ this.setButtonText(this.collapse_control, '', 'collapse', 'button_collapse')
725
725
  } else {
726
726
  this.editor_holder.style.display = 'none'
727
727
  this.collapsed = true
728
- this.setButtonText(this.collapse_control, '', 'expand', this.translate('button_expand'))
728
+ this.setButtonText(this.collapse_control, '', 'expand', 'button_expand')
729
729
  }
730
730
  })
731
731
 
@@ -742,7 +742,7 @@ export class ObjectEditor extends AbstractEditor {
742
742
  }
743
743
 
744
744
  /* Edit JSON Button */
745
- this.editjson_control = this.getButton('JSON', 'edit', 'Edit JSON')
745
+ this.editjson_control = this.getButton('JSON', 'edit', 'button_edit_json')
746
746
  this.editjson_control.classList.add('json-editor-btntype-editjson')
747
747
  this.editjson_control.addEventListener('click', (e) => {
748
748
  e.preventDefault()
@@ -760,7 +760,7 @@ export class ObjectEditor extends AbstractEditor {
760
760
  }
761
761
 
762
762
  /* Object Properties Button */
763
- this.addproperty_button = this.getButton('Properties', 'edit_properties', this.translate('button_object_properties'))
763
+ this.addproperty_button = this.getButton('properties', 'edit_properties', 'button_object_properties')
764
764
  this.addproperty_button.classList.add('json-editor-btntype-properties')
765
765
  this.addproperty_button.addEventListener('click', (e) => {
766
766
  e.preventDefault()
@@ -793,7 +793,11 @@ export class ObjectEditor extends AbstractEditor {
793
793
 
794
794
  deactivateNonRequiredProperties () {
795
795
  /* the show_opt_in editor option is for backward compatibility */
796
- if (this.jsoneditor.options.show_opt_in || this.options.show_opt_in) {
796
+ const globalOptIn = this.jsoneditor.options.show_opt_in
797
+ const editorOptInDefined = (typeof this.options.show_opt_in !== 'undefined')
798
+ const editorOptInEnabled = (editorOptInDefined && this.options.show_opt_in === true)
799
+ const editorOptInDisabled = (editorOptInDefined && this.options.show_opt_in === false)
800
+ if (editorOptInEnabled || (!editorOptInDisabled && globalOptIn) || (!editorOptInDefined && globalOptIn)) {
797
801
  Object.entries(this.editors).forEach(([key, editor]) => {
798
802
  if (!this.isRequiredObject(editor)) {
799
803
  this.editors[key].deactivate()
@@ -968,6 +972,7 @@ export class ObjectEditor extends AbstractEditor {
968
972
  [key]: {}
969
973
  }
970
974
  case 'additionalProperties':
975
+ case 'propertyNames':
971
976
  return {
972
977
  ...acc,
973
978
  [key]: true
@@ -992,7 +997,8 @@ export class ObjectEditor extends AbstractEditor {
992
997
  this.editors[name].register()
993
998
  /* New property */
994
999
  } else {
995
- if (!this.canHaveAdditionalProperties() && (!this.schema.properties || !this.schema.properties[name])) {
1000
+ if (!this.canHaveAdditionalProperties() && (!this.schema.properties || !this.schema.properties[name]) &&
1001
+ (!this.schema.patternProperties || !(Object.keys(this.schema.patternProperties).find(i => new RegExp(i).test(name))))) {
996
1002
  return
997
1003
  }
998
1004
 
@@ -1096,6 +1102,10 @@ export class ObjectEditor extends AbstractEditor {
1096
1102
  refreshValue () {
1097
1103
  this.value = {}
1098
1104
 
1105
+ if (!this.editors) {
1106
+ return
1107
+ }
1108
+
1099
1109
  Object.keys(this.editors).forEach(i => {
1100
1110
  if (this.editors[i].isActive()) {
1101
1111
  this.value[i] = this.editors[i].getValue()
@@ -1196,9 +1206,14 @@ export class ObjectEditor extends AbstractEditor {
1196
1206
  if (typeof value[i] !== 'undefined') {
1197
1207
  this.addObjectProperty(i)
1198
1208
  editor.setValue(value[i], initial)
1209
+ editor.activate()
1199
1210
  /* Otherwise, remove value unless this is the initial set or it's required */
1200
1211
  } else if (!initial && !this.isRequiredObject(editor)) {
1201
- this.removeObjectProperty(i)
1212
+ if (this.jsoneditor.options.show_opt_in || this.options.show_opt_in) {
1213
+ editor.deactivate()
1214
+ } else {
1215
+ this.removeObjectProperty(i)
1216
+ }
1202
1217
  /* Otherwise, set the value to the default */
1203
1218
  } else {
1204
1219
  editor.setValue(editor.getDefault(), initial)
@@ -2,15 +2,14 @@ import { SelectEditor } from './select.js'
2
2
 
3
3
  export class RadioEditor extends SelectEditor {
4
4
  preBuild () {
5
- this.schema.required = true /* force editor into required mode to prevent creation of empty radio button */
6
5
  super.preBuild()
7
6
  }
8
7
 
9
8
  build () {
10
9
  this.label = ''
11
10
  if (!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired())
12
- if (this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description)
13
- if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText)
11
+ if (this.schema.description) this.description = this.theme.getFormInputDescription(this.translateProperty(this.schema.description))
12
+ if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.translateProperty(this.options.infoText))
14
13
  if (this.options.compact) this.container.classList.add('compact')
15
14
 
16
15
  this.radioContainer = document.createElement('div')
@@ -22,6 +21,12 @@ export class RadioEditor extends SelectEditor {
22
21
  this.onChange(true)
23
22
  }
24
23
 
24
+ if (!this.isRequired()) {
25
+ this.enum_display.shift()
26
+ this.enum_options.shift()
27
+ this.enum_values.shift()
28
+ }
29
+
25
30
  for (let i = 0; i < this.enum_values.length; i++) {
26
31
  /* form radio elements */
27
32
  this.input = this.theme.getFormRadio({
@@ -46,7 +51,7 @@ export class RadioEditor extends SelectEditor {
46
51
  }
47
52
 
48
53
  if (this.schema.readOnly || this.schema.readonly) {
49
- this.always_disabled = true
54
+ this.disable(true)
50
55
  for (let j = 0; j < this.radioGroup.length; j++) {
51
56
  this.radioGroup[j].disabled = true
52
57
  }
@@ -9,7 +9,7 @@ export class SelectEditor extends AbstractEditor {
9
9
  const haveToUseDefaultValue = !!this.jsoneditor.options.use_default_values || typeof this.schema.default !== 'undefined'
10
10
 
11
11
  if (
12
- !this.enum_values.includes(sanitized) ||
12
+ (this.enum_options.length > 0 && !this.enum_values.includes(sanitized)) ||
13
13
  (initial && !this.isRequired() && !haveToUseDefaultValue)
14
14
  ) {
15
15
  sanitized = this.enum_values[0]
@@ -77,7 +77,7 @@ export class SelectEditor extends AbstractEditor {
77
77
 
78
78
  this.schema.enum.forEach((option, i) => {
79
79
  this.enum_options[i] = `${option}`
80
- this.enum_display[i] = `${display[i] || option}`
80
+ this.enum_display[i] = `${this.translateProperty(display[i]) || option}`
81
81
  this.enum_values[i] = this.typecast(option)
82
82
  })
83
83
 
@@ -162,15 +162,15 @@ export class SelectEditor extends AbstractEditor {
162
162
 
163
163
  build () {
164
164
  if (!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired())
165
- if (this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description)
166
- if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText)
165
+ if (this.schema.description) this.description = this.theme.getFormInputDescription(this.translateProperty(this.schema.description))
166
+ if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.translateProperty(this.options.infoText))
167
167
  if (this.options.compact) this.container.classList.add('compact')
168
168
 
169
169
  this.input = this.theme.getSelectInput(this.enum_options, false)
170
170
  this.theme.setSelectOptions(this.input, this.enum_options, this.enum_display)
171
171
 
172
172
  if (this.schema.readOnly || this.schema.readonly) {
173
- this.always_disabled = true
173
+ this.disable(true)
174
174
  this.input.disabled = true
175
175
  }
176
176
 
@@ -330,8 +330,8 @@ export class SelectEditor extends AbstractEditor {
330
330
  enable () {
331
331
  if (!this.always_disabled) {
332
332
  this.input.disabled = false
333
+ super.enable()
333
334
  }
334
- super.enable()
335
335
  }
336
336
 
337
337
  disable (alwaysDisabled) {
@@ -6,7 +6,7 @@ import { StringEditor } from './string.js'
6
6
  export class SignatureEditor extends StringEditor {
7
7
  build () {
8
8
  if (!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired())
9
- if (this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description)
9
+ if (this.schema.description) this.description = this.theme.getFormInputDescription(this.translateProperty(this.schema.description))
10
10
  const formname = this.formname.replace(/\W/g, '')
11
11
 
12
12
  if (typeof SignaturePad === 'function') {
@@ -53,7 +53,8 @@ export class SignatureEditor extends StringEditor {
53
53
  if (this.options.compact) this.container.setAttribute('class', `${this.container.getAttribute('class')} compact`)
54
54
 
55
55
  if (this.schema.readOnly || this.schema.readonly) {
56
- this.always_disabled = true
56
+ this.disable(true)
57
+
57
58
  Array.from(this.inputs).forEach(input => {
58
59
  canvas.setAttribute('readOnly', 'readOnly')
59
60
  input.disabled = true
@@ -4,8 +4,8 @@ import rules from './starrating.css.js'
4
4
  export class StarratingEditor extends StringEditor {
5
5
  build () {
6
6
  if (!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired())
7
- if (this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description)
8
- if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText)
7
+ if (this.schema.description) this.description = this.theme.getFormInputDescription(this.translateProperty(this.schema.description))
8
+ if (this.options.infoText) this.infoButton = this.theme.getInfoButton(this.translateProperty(this.options.infoText))
9
9
  if (this.options.compact) this.container.classList.add('compact')
10
10
 
11
11
  this.ratingContainer = document.createElement('div')
@@ -59,7 +59,7 @@ export class StarratingEditor extends StringEditor {
59
59
  }
60
60
 
61
61
  if (this.schema.readOnly || this.schema.readonly) {
62
- this.always_disabled = true
62
+ this.disable(true)
63
63
  for (let j = 0; j < this.radioGroup.length; j++) {
64
64
  this.radioGroup[j].disabled = true
65
65
  }
@@ -83,7 +83,7 @@ export class StarratingEditor extends StringEditor {
83
83
  this.radioGroup[i].disabled = false
84
84
  }
85
85
  this.ratingContainer.classList.remove('readonly')
86
- super.enable()
86
+ this.disabled = false
87
87
  }
88
88
  }
89
89
 
@@ -93,7 +93,7 @@ export class StarratingEditor extends StringEditor {
93
93
  this.radioGroup[i].disabled = true
94
94
  }
95
95
  this.ratingContainer.classList.add('readonly')
96
- super.disable()
96
+ this.disabled = true
97
97
  }
98
98
 
99
99
  destroy () {
@@ -4,6 +4,9 @@ export class StepperEditor extends IntegerEditor {
4
4
  build () {
5
5
  super.build()
6
6
  this.input.setAttribute('type', 'number')
7
+ if (!this.input.getAttribute('step')) {
8
+ this.input.setAttribute('step', '1')
9
+ }
7
10
  const stepperButtons = this.theme.getStepperButtons(this.input)
8
11
  this.control.appendChild(stepperButtons)
9
12
  this.stepperDown = this.control.querySelector('.stepper-down')