@json-editor/json-editor 2.6.1 → 2.9.0-beta.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 (74) hide show
  1. package/.github/workflows/build.yml +12 -3
  2. package/CHANGELOG.md +32 -0
  3. package/README.md +148 -18
  4. package/dist/jsoneditor.js +2 -2
  5. package/dist/nonmin/jsoneditor.js +2920 -643
  6. package/dist/nonmin/jsoneditor.js.map +1 -1
  7. package/docs/form-submission.html +162 -0
  8. package/package.json +4 -3
  9. package/release-notes.md +2 -0
  10. package/src/core.js +36 -37
  11. package/src/defaults.js +10 -2
  12. package/src/editors/array.js +12 -1
  13. package/src/editors/autocomplete.js +4 -3
  14. package/src/editors/button.js +1 -1
  15. package/src/editors/checkbox.js +3 -1
  16. package/src/editors/hidden.js +3 -1
  17. package/src/editors/multiselect.js +17 -6
  18. package/src/editors/radio.js +8 -3
  19. package/src/editors/select.js +3 -1
  20. package/src/editors/signature.js +3 -1
  21. package/src/editors/string.js +7 -1
  22. package/src/editors/table.js +20 -2
  23. package/src/editors/upload.js +1 -1
  24. package/src/editors/uuid.js +2 -12
  25. package/src/iconlib.js +1 -1
  26. package/src/schemaloader.js +319 -103
  27. package/src/style.css +3 -0
  28. package/src/style.css.js +1 -1
  29. package/src/theme.js +0 -2
  30. package/src/themes/bootstrap3.js +1 -0
  31. package/src/themes/spectre.js +2 -1
  32. package/src/utilities.js +18 -0
  33. package/src/validator.js +36 -34
  34. package/tests/codeceptjs/codecept.json +1 -1
  35. package/tests/codeceptjs/core_test.js +125 -0
  36. package/tests/codeceptjs/editors/advanced_test.js +1 -1
  37. package/tests/codeceptjs/editors/array_test.js +74 -0
  38. package/tests/codeceptjs/editors/autocomplete_test.js +16 -0
  39. package/tests/codeceptjs/editors/button_test.js +10 -3
  40. package/tests/codeceptjs/editors/integer_test.js +7 -2
  41. package/tests/codeceptjs/editors/jodit_test.js +3 -3
  42. package/tests/codeceptjs/editors/range_test.js +12 -0
  43. package/tests/codeceptjs/editors/uuid_test.js +31 -4
  44. package/tests/docker-compose.yml +1 -1
  45. package/tests/fixtures/definitions.json +22 -0
  46. package/tests/fixtures/properties.json +20 -0
  47. package/tests/pages/array-checkboxes-infotext.html +52 -0
  48. package/tests/pages/array-move-events.html +4 -2
  49. package/tests/pages/array-unique-items-sort.html +78 -0
  50. package/tests/pages/autocomplete.html +69 -0
  51. package/tests/pages/button-icons.html +38 -0
  52. package/tests/pages/core.html +4 -2
  53. package/tests/pages/error-messages.html +47 -0
  54. package/tests/pages/grid-strict.html +6 -10
  55. package/tests/pages/grid.html +0 -4
  56. package/tests/pages/issues/issue-gh-812.html +4 -2
  57. package/tests/pages/meta_schema.json +14 -0
  58. package/tests/pages/object-required-properties.html +7 -7
  59. package/tests/pages/range.html +60 -0
  60. package/tests/pages/ready.html +43 -0
  61. package/tests/pages/references.html +168 -0
  62. package/tests/pages/string-simplemde-editor.html +81 -0
  63. package/tests/pages/table-move-events.html +4 -1
  64. package/tests/pages/urn.html +11 -8
  65. package/tests/pages/use-name-attributes.html +206 -0
  66. package/tests/pages/uuid.html +89 -50
  67. package/tests/pages/validation-messages.json +705 -0
  68. package/tests/unit/core.spec.js +79 -66
  69. package/tests/unit/editor.spec.js +20 -8
  70. package/tests/unit/editors/array.spec.js +3 -2
  71. package/tests/unit/editors/object.spec.js +3 -1
  72. package/tests/unit/editors/table.spec.js +4 -2
  73. package/tests/unit/schemaloader.spec.js +181 -103
  74. package/tests/unit/validator.spec.js +2 -2
@@ -0,0 +1,162 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <title>JSONEditor with forms</title>
6
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
7
+ </head>
8
+ <body>
9
+ <div class="container">
10
+ <p>
11
+ The form is set to send a <code>GET</code> request to the same page.
12
+ A change event listener has been added to the editor so that whenever the form changes, the editor value
13
+ is stored in a hidden input using <code>JSON.stringify()</code>.
14
+ </p>
15
+
16
+ <p>
17
+ The option <code>use_name_attributes</code> was set to <code>false</code>
18
+ to avoid sending the other field with the request.
19
+ </p>
20
+ <p>
21
+ When the form is submitted only the hidden input is sent in the request.
22
+ This allows to send data structures like arrays and object.
23
+ Also the same schema that is used to build the form can be used as parameter to backend json validators tools.
24
+ Try yourself, submit the form and look in the network tab of the developer tool.
25
+ </p>
26
+ <div class="form-group"></div>
27
+ <form action="/docs/form-submission.html" method="get">
28
+ <input id="input" type="hidden" name="json">
29
+ <div id='editor-container'></div>
30
+ <input id="submit" class="btn btn-primary" width="100" type="submit">
31
+ </form>
32
+ <br>
33
+ <h2>Get params</h2>
34
+ <pre id="get-params"></pre>
35
+ </div>
36
+ <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
37
+ <script>
38
+ var params = {}
39
+ for (const [key, value] of new URLSearchParams(window.location.search).entries()) {
40
+ params[key] = value
41
+ console.log(key, value)
42
+ }
43
+ document.querySelector('#get-params').textContent = JSON.stringify(params, null, 2)
44
+ var config = {
45
+ use_name_attributes: false,
46
+ theme: 'bootstrap4',
47
+ disable_edit_json: true,
48
+ disable_properties: true,
49
+ disable_collapse: true,
50
+ schema: {
51
+ 'title': 'Person',
52
+ 'type': 'object',
53
+ 'required': [
54
+ 'name',
55
+ 'age',
56
+ 'date',
57
+ 'favorite_color',
58
+ 'gender',
59
+ 'location',
60
+ 'pets'
61
+ ],
62
+ 'properties': {
63
+ 'name': {
64
+ 'type': 'string',
65
+ 'description': 'First and Last name',
66
+ 'minLength': 4,
67
+ 'default': 'Jeremy Dorn'
68
+ },
69
+ 'age': {
70
+ 'type': 'integer',
71
+ 'default': 25,
72
+ 'minimum': 18,
73
+ 'maximum': 99
74
+ },
75
+ 'favorite_color': {
76
+ 'type': 'string',
77
+ 'format': 'color',
78
+ 'title': 'favorite color',
79
+ 'default': '#ffa500'
80
+ },
81
+ 'gender': {
82
+ 'type': 'string',
83
+ 'enum': [
84
+ 'male',
85
+ 'female',
86
+ 'other'
87
+ ]
88
+ },
89
+ 'date': {
90
+ 'type': 'string',
91
+ 'format': 'date',
92
+ 'options': {
93
+ 'flatpickr': {}
94
+ }
95
+ },
96
+ 'location': {
97
+ 'type': 'object',
98
+ 'title': 'Location',
99
+ 'properties': {
100
+ 'city': {
101
+ 'type': 'string',
102
+ 'default': 'San Francisco'
103
+ },
104
+ 'state': {
105
+ 'type': 'string',
106
+ 'default': 'CA'
107
+ },
108
+ 'citystate': {
109
+ 'type': 'string',
110
+ 'description': 'This is generated automatically from the previous two fields',
111
+ 'template': '{{city}}, {{state}}',
112
+ 'watch': {
113
+ 'city': 'location.city',
114
+ 'state': 'location.state'
115
+ }
116
+ }
117
+ }
118
+ },
119
+ 'pets': {
120
+ 'type': 'array',
121
+ 'format': 'table',
122
+ 'title': 'Pets',
123
+ 'uniqueItems': true,
124
+ 'items': {
125
+ 'type': 'object',
126
+ 'title': 'Pet',
127
+ 'properties': {
128
+ 'type': {
129
+ 'type': 'string',
130
+ 'enum': [
131
+ 'cat',
132
+ 'dog',
133
+ 'bird',
134
+ 'reptile',
135
+ 'other'
136
+ ],
137
+ 'default': 'dog'
138
+ },
139
+ 'name': {
140
+ 'type': 'string'
141
+ }
142
+ }
143
+ },
144
+ 'default': [
145
+ {
146
+ 'type': 'dog',
147
+ 'name': 'Walter'
148
+ }
149
+ ]
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ var editor = new JSONEditor(document.querySelector('#editor-container'), config)
156
+
157
+ editor.on('change', function () {
158
+ document.querySelector('#input').value = JSON.stringify(editor.getValue())
159
+ })
160
+ </script>
161
+ </body>
162
+ </html>
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.6.1",
5
+ "version": "2.9.0-beta.0",
6
6
  "main": "dist/jsoneditor.js",
7
7
  "author": {
8
8
  "name": "Jeremy Dorn",
@@ -71,9 +71,9 @@
71
71
  "http-server": "^0.12.3",
72
72
  "jasmine": "^3.7.0",
73
73
  "jasmine-core": "^3.7.1",
74
- "jodit": "~3.3.24",
74
+ "jodit": "^3.12.4",
75
75
  "jquery": "^3.6.0",
76
- "karma": "^5.2.3",
76
+ "karma": "^6.3.14",
77
77
  "karma-chrome-launcher": "^3.1.0",
78
78
  "karma-jasmine": "^2.0.1",
79
79
  "karma-jasmine-html-reporter": "^1.6.0",
@@ -87,6 +87,7 @@
87
87
  "puppeteer": "^1.20.0",
88
88
  "remove-strict-webpack-plugin": "^0.1.2",
89
89
  "sceditor": "^2.1.3",
90
+ "simplemde": "^1.11.2",
90
91
  "sinon": "^8.1.1",
91
92
  "standard": "^14.3.4",
92
93
  "style-loader": "^1.3.0",
package/release-notes.md CHANGED
@@ -79,6 +79,8 @@ For now I have passed in `JSONEditor.defaults` as `defaults`.
79
79
 
80
80
  ### NPM Release
81
81
 
82
+ Set/export `CHROME_BIN=/path/to/chrome_or_chromium`, start stack with `docker-compose up`
83
+
82
84
  1. Update `CHANGELOG.md`
83
85
  2. Switch to `release` branch and `merge master into release`
84
86
  3. NPM login: `npm login`
package/src/core.js CHANGED
@@ -31,6 +31,8 @@ export class JSONEditor {
31
31
  /* Load editors and selected theme style rules */
32
32
  if (!themeClass) throw new Error(`Unknown theme ${themeName}`)
33
33
  this.element.setAttribute('data-theme', themeName)
34
+ this.element.classList.add('je-not-loaded')
35
+ this.element.classList.remove('je-ready')
34
36
  // eslint-disable-next-line new-cap
35
37
  this.theme = new themeClass(this)
36
38
  const rules = extend(styleRules, this.getEditorsRules())
@@ -56,67 +58,64 @@ export class JSONEditor {
56
58
 
57
59
  this.root_container = this.theme.getContainer()
58
60
  this.element.appendChild(this.root_container)
61
+ this.promise = this.load()
62
+ }
59
63
 
60
- /* Fetch all external refs via ajax */
64
+ async load () {
61
65
  const fetchUrl = document.location.origin + document.location.pathname.toString()
62
66
  const loader = new SchemaLoader(this.options)
63
- const location = document.location.toString()
64
-
65
- this.expandSchema = (schema, fileBase) => loader.expandSchema(schema, fileBase)
67
+ this.expandSchema = (schema) => loader.expandSchema(schema)
66
68
  this.expandRefs = (schema, fileBase) => loader.expandRefs(schema, fileBase)
67
- this.refs = loader.refs
68
-
69
- loader.load(this.schema, schema => {
70
- const validatorOptions = this.options.custom_validators ? { custom_validators: this.options.custom_validators } : {}
71
-
72
- this.validator = new Validator(this, null, validatorOptions, JSONEditor.defaults)
73
-
74
- const editorClass = this.getEditorClass(schema)
69
+ const location = document.location.toString()
70
+ const schema = await loader.load(this.schema, fetchUrl, location)
71
+ const validatorOptions = this.options.custom_validators ? { custom_validators: this.options.custom_validators } : {}
72
+ this.validator = new Validator(this, null, validatorOptions, JSONEditor.defaults)
73
+ const editorClass = this.getEditorClass(schema)
74
+ this.root = this.createEditor(editorClass, {
75
+ jsoneditor: this,
76
+ schema,
77
+ required: true,
78
+ container: this.root_container
79
+ })
75
80
 
76
- this.root = this.createEditor(editorClass, {
77
- jsoneditor: this,
78
- schema,
79
- required: true,
80
- container: this.root_container
81
- })
81
+ this.root.preBuild()
82
+ this.root.build()
83
+ this.root.postBuild()
82
84
 
83
- this.root.preBuild()
84
- this.root.build()
85
- this.root.postBuild()
85
+ /* Starting data */
86
+ if (hasOwnProperty(this.options, 'startval')) this.root.setValue(this.options.startval)
86
87
 
87
- /* Starting data */
88
- if (hasOwnProperty(this.options, 'startval')) this.root.setValue(this.options.startval)
88
+ this.validation_results = this.validator.validate(this.root.getValue())
89
+ this.root.showValidationErrors(this.validation_results)
90
+ this.ready = true
91
+ this.element.classList.remove('je-not-loaded')
92
+ this.element.classList.add('je-ready')
89
93
 
94
+ /* Fire ready event asynchronously */
95
+ window.requestAnimationFrame(() => {
96
+ if (!this.ready) return
90
97
  this.validation_results = this.validator.validate(this.root.getValue())
91
98
  this.root.showValidationErrors(this.validation_results)
92
- this.ready = true
93
-
94
- /* Fire ready event asynchronously */
95
- window.requestAnimationFrame(() => {
96
- if (!this.ready) return
97
- this.validation_results = this.validator.validate(this.root.getValue())
98
- this.root.showValidationErrors(this.validation_results)
99
- this.trigger('ready')
100
- this.trigger('change')
101
- })
102
- }, fetchUrl, location)
99
+ this.trigger('ready')
100
+ this.trigger('change')
101
+ })
103
102
  }
104
103
 
105
104
  getValue () {
106
- if (!this.ready) throw new Error("JSON Editor not ready yet. Listen for 'ready' event before getting the value")
105
+ if (!this.ready) throw new Error('JSON Editor not ready yet. Make sure the load method is complete')
107
106
 
108
107
  return this.root.getValue()
109
108
  }
110
109
 
111
110
  setValue (value) {
112
- if (!this.ready) throw new Error("JSON Editor not ready yet. Listen for 'ready' event before setting the value")
111
+ if (!this.ready) throw new Error('JSON Editor not ready yet. Make sure the load method is complete')
113
112
 
114
113
  this.root.setValue(value)
115
114
  return this
116
115
  }
117
116
 
118
117
  validate (value) {
119
- if (!this.ready) throw new Error("JSON Editor not ready yet. Listen for 'ready' event before validating")
118
+ if (!this.ready) throw new Error('JSON Editor not ready yet. Make sure the load method is complete')
120
119
 
121
120
  /* Custom value */
122
121
  if (arguments.length === 1) {
package/src/defaults.js CHANGED
@@ -350,11 +350,18 @@ function upload (type, file, cbs) {
350
350
  }
351
351
 
352
352
  /* String translate function */
353
- function translate (key, variables) {
353
+ function translate (key, variables, schema) {
354
+ let schemaMessages = {}
355
+
356
+ if (schema && schema.options && schema.options.error_messages && schema.options.error_messages[defaults.language]) {
357
+ schemaMessages = schema.options.error_messages[defaults.language]
358
+ }
359
+
354
360
  const lang = defaults.languages[defaults.language]
361
+
355
362
  if (!lang) throw new Error(`Unknown language ${defaults.language}`)
356
363
 
357
- let string = lang[key] || defaults.languages[default_language][key] || key
364
+ let string = schemaMessages[key] || lang[key] || defaults.languages[default_language][key] || key
358
365
 
359
366
  if (variables) {
360
367
  for (let i = 0; i < variables.length; i++) {
@@ -374,6 +381,7 @@ function translateProperty (text, variables) {
374
381
  /* Default options when initializing JSON Editor */
375
382
  const options = {
376
383
  upload,
384
+ use_name_attributes: true,
377
385
  prompt_before_delete: true,
378
386
  use_default_values: true,
379
387
  max_depth: 0
@@ -1,5 +1,5 @@
1
1
  import { AbstractEditor } from '../editor.js'
2
- import { extend, trigger } from '../utilities.js'
2
+ import { extend, generateUUID, trigger } from '../utilities.js'
3
3
  import rules from './array.css.js'
4
4
 
5
5
  export class ArrayEditor extends AbstractEditor {
@@ -545,6 +545,7 @@ export class ArrayEditor extends AbstractEditor {
545
545
 
546
546
  _createCopyButton (i, holder) {
547
547
  const button = this.getButton(this.getItemTitle(), 'copy', 'button_copy_row_title', [this.getItemTitle()])
548
+ const schema = this.schema
548
549
  button.classList.add('copy', 'json-editor-btntype-copy')
549
550
  button.setAttribute('data-i', i)
550
551
  button.addEventListener('click', e => {
@@ -555,6 +556,16 @@ export class ArrayEditor extends AbstractEditor {
555
556
 
556
557
  value.forEach((row, j) => {
557
558
  if (j === i) {
559
+ /* Force generation of new UUID if the item has been cloned. */
560
+ if (schema.items.type === 'string' && schema.items.format === 'uuid') {
561
+ row = generateUUID()
562
+ } else if (schema.items.type === 'object' && schema.items.properties) {
563
+ for (const key of Object.keys(row)) {
564
+ if (schema.items.properties && schema.items.properties[key] && schema.items.properties[key].format === 'uuid') {
565
+ row[key] = generateUUID()
566
+ }
567
+ }
568
+ }
558
569
  value.push(row)
559
570
  }
560
571
  })
@@ -29,18 +29,19 @@ export class AutocompleteEditor extends StringEditor {
29
29
  /* Get options, either global options from "this.defaults.options.autocomplete" or */
30
30
  /* single property options from schema "options.autocomplete" */
31
31
  options = this.expandCallbacks('autocomplete', extend({}, {
32
- search: (jseditor, input) => {
32
+ search: (jseditor) => {
33
33
  // eslint-disable-next-line no-console
34
34
  console.log(`No "search" callback defined for autocomplete in property "${jseditor.key}"`)
35
35
  return []
36
36
  },
37
+ onSubmit: () => {
38
+ this.input.blur()
39
+ },
37
40
  baseClass: 'autocomplete'
38
41
  }, this.defaults.options.autocomplete || {}, this.options.autocomplete || {}))
39
42
 
40
43
  this.autocomplete_wrapper.classList.add(options.baseClass)
41
44
  this.autocomplete_dropdown.classList.add(`${options.baseClass}-result-list`)
42
- /* this.input.classList.add(options.baseClass + '-input'); */
43
-
44
45
  this.autocomplete_instance = new window.Autocomplete(this.autocomplete_wrapper, options)
45
46
  }
46
47
  super.afterInputReady()
@@ -34,7 +34,7 @@ export class ButtonEditor extends AbstractEditor {
34
34
  }
35
35
  }, this.defaults.options.button || {}, this.options.button || {}))
36
36
 
37
- this.input = this.theme.getFormButton(title, options.icon, title)
37
+ this.input = this.getButton(title, options.icon, title)
38
38
  this.input.addEventListener('click', options.action, false)
39
39
 
40
40
  if (this.schema.readOnly || this.schema.readonly || this.schema.template) {
@@ -12,7 +12,9 @@ export class CheckboxEditor extends AbstractEditor {
12
12
  register () {
13
13
  super.register()
14
14
  if (!this.input) return
15
- this.input.setAttribute('name', this.formname)
15
+ if (this.jsoneditor.options.use_name_attributes) {
16
+ this.input.setAttribute('name', this.formname)
17
+ }
16
18
  }
17
19
 
18
20
  unregister () {
@@ -7,7 +7,9 @@ export class HiddenEditor extends AbstractEditor {
7
7
  register () {
8
8
  super.register()
9
9
  if (!this.input) return
10
- this.input.setAttribute('name', this.formname)
10
+ if (this.jsoneditor.options.use_name_attributes) {
11
+ this.input.setAttribute('name', this.formname)
12
+ }
11
13
  }
12
14
 
13
15
  unregister () {
@@ -9,7 +9,9 @@ export class MultiSelectEditor extends AbstractEditor {
9
9
  register () {
10
10
  super.register()
11
11
  if (!this.input) return
12
- this.input.setAttribute('name', this.formname)
12
+ if (this.jsoneditor.options.use_name_attributes) {
13
+ this.input.setAttribute('name', this.formname)
14
+ }
13
15
  }
14
16
 
15
17
  unregister () {
@@ -31,19 +33,24 @@ export class MultiSelectEditor extends AbstractEditor {
31
33
  this.select_options = {}
32
34
  this.select_values = {}
33
35
  this.option_keys = []
34
- this.option_titles = []
36
+ this.option_enum = []
35
37
 
36
38
  let i
37
39
  const itemsSchema = this.jsoneditor.expandRefs(this.schema.items || {})
38
40
  const e = itemsSchema.enum || []
41
+ const oe = itemsSchema.options ? itemsSchema.options.enum || [] : []
42
+ /* fallback to enum_titles, when options.enum is not present */
39
43
  const t = itemsSchema.options ? itemsSchema.options.enum_titles || [] : []
40
44
 
41
45
  for (i = 0; i < e.length; i++) {
42
46
  /* If the sanitized value is different from the enum value, don't include it */
43
47
  if (this.sanitize(e[i]) !== e[i]) continue
44
48
 
49
+ const d = oe[i] || {}
50
+ if (!('title' in d)) d.title = `${t[i] || e[i]}`
51
+
45
52
  this.option_keys.push(`${e[i]}`)
46
- this.option_titles.push(`${t[i] || e[i]}`)
53
+ this.option_enum.push(d)
47
54
  this.select_values[`${e[i]}`] = e[i]
48
55
  }
49
56
  }
@@ -65,9 +72,13 @@ export class MultiSelectEditor extends AbstractEditor {
65
72
  this.inputs[this.option_keys[i]] = this.theme.getCheckbox()
66
73
  this.inputs[this.option_keys[i]].id = id
67
74
  this.select_options[this.option_keys[i]] = this.inputs[this.option_keys[i]]
68
- const label = this.theme.getCheckboxLabel(this.option_titles[i])
75
+ const label = this.theme.getCheckboxLabel(this.option_enum[i].title)
69
76
  label.htmlFor = id
70
- this.controls[this.option_keys[i]] = this.theme.getFormControl(label, this.inputs[this.option_keys[i]])
77
+ if (this.option_enum[i].infoText) {
78
+ const infoButton = this.theme.getInfoButton(this.translateProperty(this.option_enum[i].infoText))
79
+ label.appendChild(infoButton)
80
+ }
81
+ this.controls['_' + this.option_keys[i]] = this.theme.getFormControl(label, this.inputs[this.option_keys[i]])
71
82
  }
72
83
 
73
84
  this.control = this.theme.getMultiCheckboxHolder(this.controls, this.label, this.description, this.infoButton)
@@ -75,7 +86,7 @@ export class MultiSelectEditor extends AbstractEditor {
75
86
  } else {
76
87
  this.input_type = 'select'
77
88
  this.input = this.theme.getSelectInput(this.option_keys, true)
78
- this.theme.setSelectOptions(this.input, this.option_keys, this.option_titles)
89
+ this.theme.setSelectOptions(this.input, this.option_keys, this.option_enum.map(e => e.title))
79
90
  /* this.input.multiple = true; */
80
91
  this.input.setAttribute('multiple', 'multiple')
81
92
  this.input.size = Math.min(10, this.option_keys.length)
@@ -29,11 +29,16 @@ export class RadioEditor extends SelectEditor {
29
29
 
30
30
  for (let i = 0; i < this.enum_values.length; i++) {
31
31
  /* form radio elements */
32
- this.input = this.theme.getFormRadio({
33
- name: this.formname,
32
+ const attributes = {
34
33
  id: `${this.formname}[${i}]`,
35
34
  value: this.enum_values[i]
36
- })
35
+ }
36
+
37
+ if (this.jsoneditor.options.use_name_attributes) {
38
+ attributes.name = this.formname
39
+ }
40
+
41
+ this.input = this.theme.getFormRadio(attributes)
37
42
 
38
43
  /* Set custom attributes on input element. Parameter is array of protected keys. Empty array if none. */
39
44
  this.setInputAttributes(['id', 'value', 'name'])
@@ -30,7 +30,9 @@ export class SelectEditor extends AbstractEditor {
30
30
  register () {
31
31
  super.register()
32
32
  if (!this.input) return
33
- this.input.setAttribute('name', this.formname)
33
+ if (this.jsoneditor.options.use_name_attributes) {
34
+ this.input.setAttribute('name', this.formname)
35
+ }
34
36
  }
35
37
 
36
38
  unregister () {
@@ -20,7 +20,9 @@ export class SignatureEditor extends StringEditor {
20
20
 
21
21
  /* Create canvas for signature pad */
22
22
  const canvas = document.createElement('canvas')
23
- canvas.setAttribute('name', formname)
23
+ if (this.jsoneditor.options.use_name_attributes) {
24
+ canvas.setAttribute('name', formname)
25
+ }
24
26
  canvas.classList.add('signature')
25
27
  signatureContainer.appendChild(canvas)
26
28
 
@@ -5,7 +5,9 @@ export class StringEditor extends AbstractEditor {
5
5
  register () {
6
6
  super.register()
7
7
  if (!this.input) return
8
- this.input.setAttribute('name', this.formname)
8
+ if (this.jsoneditor.options.use_name_attributes) {
9
+ this.input.setAttribute('name', this.formname)
10
+ }
9
11
  this.input.setAttribute('aria-label', this.formname)
10
12
  }
11
13
 
@@ -213,6 +215,10 @@ export class StringEditor extends AbstractEditor {
213
215
  /* it will generate an error trying to append it to the missing parentNode */
214
216
  if (this.input.parentNode) this.afterInputReady()
215
217
  if (this.adjust_height) this.adjust_height(this.input)
218
+ if (this.format === 'range') {
219
+ const output = this.control.querySelector('output')
220
+ output.value = this.input.value
221
+ }
216
222
  })
217
223
 
218
224
  /* Compile and store the template */
@@ -1,5 +1,5 @@
1
1
  import { ArrayEditor } from './array.js'
2
- import { extend, trigger } from '../utilities.js'
2
+ import { extend, generateUUID, trigger } from '../utilities.js'
3
3
 
4
4
  export class TableEditor extends ArrayEditor {
5
5
  register () {
@@ -347,6 +347,7 @@ export class TableEditor extends ArrayEditor {
347
347
 
348
348
  _createCopyButton (i, holder) {
349
349
  const button = this.getButton('', 'copy', 'button_copy_row_title_short')
350
+ const schema = this.schema
350
351
  button.classList.add('copy', 'json-editor-btntype-copy')
351
352
  button.setAttribute('data-i', i)
352
353
  button.addEventListener('click', e => {
@@ -355,8 +356,25 @@ export class TableEditor extends ArrayEditor {
355
356
  const j = e.currentTarget.getAttribute('data-i') * 1
356
357
  const value = this.getValue()
357
358
 
358
- value.splice(j + 1, 0, value[j])
359
+ let newValue = value[j]
360
+
361
+ /* On copy, recreate uuid if needed. */
362
+ if (schema.items.type === 'string' && schema.items.format === 'uuid') {
363
+ newValue = generateUUID()
364
+ } else if (schema.items.type === 'object' && schema.items.properties) {
365
+ value.forEach((row, i) => {
366
+ if (j === i) {
367
+ for (const key of Object.keys(row)) {
368
+ if (schema.items.properties && schema.items.properties[key] && schema.items.properties[key].format === 'uuid') {
369
+ newValue = Object.assign({}, value[j])
370
+ newValue[key] = generateUUID()
371
+ }
372
+ }
373
+ }
374
+ })
375
+ }
359
376
 
377
+ value.splice(j + 1, 0, newValue)
360
378
  this.setValue(value)
361
379
  this.onChange(true)
362
380
  this.jsoneditor.trigger('copyRow', this.rows[j + 1])
@@ -257,7 +257,7 @@ export class UploadEditor extends AbstractEditor {
257
257
 
258
258
  if (this.options.auto_upload) {
259
259
  uploadButton.dispatchEvent(new window.MouseEvent('click'))
260
- this.preview.removeChild(uploadButton)
260
+ uploadButton.parentNode.removeChild(uploadButton)
261
261
  }
262
262
  }
263
263
 
@@ -1,3 +1,4 @@
1
+ import { generateUUID } from '../utilities.js'
1
2
  import { StringEditor } from './string.js'
2
3
 
3
4
  export class UuidEditor extends StringEditor {
@@ -36,18 +37,7 @@ export class UuidEditor extends StringEditor {
36
37
  }
37
38
 
38
39
  getUuid () {
39
- /* https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript */
40
- let d = new Date().getTime()
41
-
42
- if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
43
- d += performance.now() /* use high-precision timer if available */
44
- }
45
-
46
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
47
- const r = (d + Math.random() * 16) % 16 | 0
48
- d = Math.floor(d / 16)
49
- return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
50
- })
40
+ return generateUUID()
51
41
  }
52
42
 
53
43
  testUuid (value) {
package/src/iconlib.js CHANGED
@@ -8,7 +8,7 @@ export class AbstractIconLib {
8
8
  }
9
9
 
10
10
  getIconClass (key) {
11
- return this.mapping[key] ? this.icon_prefix + this.mapping[key] : null
11
+ return this.mapping[key] ? this.icon_prefix + this.mapping[key] : this.icon_prefix + key
12
12
  }
13
13
 
14
14
  getIcon (key) {