@wirenboard/json-editor 2.5.3-wb13

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 (283) hide show
  1. package/.env-dist +2 -0
  2. package/.eslintrc +7 -0
  3. package/.gitattributes +1 -0
  4. package/.github/PULL_REQUEST_TEMPLATE.md +9 -0
  5. package/.github/issue_template +25 -0
  6. package/.github/workflows/build.yml +58 -0
  7. package/.travis.yml +70 -0
  8. package/CHANGELOG.md +915 -0
  9. package/CONTRIBUTING.md +92 -0
  10. package/LICENSE +20 -0
  11. package/Makefile +26 -0
  12. package/README.md +1646 -0
  13. package/README_ADDON.md +573 -0
  14. package/UPGRADING.md +46 -0
  15. package/build/CssToJson.js +55 -0
  16. package/codecept.conf.js +35 -0
  17. package/config/.eslintrc +7 -0
  18. package/config/codeceptjs_helpers.js +139 -0
  19. package/config/helpers.js +10 -0
  20. package/config/karma.conf.js +93 -0
  21. package/config/readme.md +31 -0
  22. package/config/webpack.common.js +75 -0
  23. package/config/webpack.dev.js +15 -0
  24. package/config/webpack.nonmin.js +19 -0
  25. package/config/webpack.prod.js +25 -0
  26. package/dist/jsoneditor.js +14 -0
  27. package/dist/nonmin/jsoneditor.js +29097 -0
  28. package/dist/nonmin/jsoneditor.js.map +1 -0
  29. package/docs/ace_editor.html +56 -0
  30. package/docs/advanced.html +136 -0
  31. package/docs/basic.html +63 -0
  32. package/docs/basic_person.json +26 -0
  33. package/docs/choices.html +86 -0
  34. package/docs/cleave.html +132 -0
  35. package/docs/colorpicker.html +194 -0
  36. package/docs/css_integration.html +135 -0
  37. package/docs/datetime.html +305 -0
  38. package/docs/describedby.html +161 -0
  39. package/docs/enumsource.html +67 -0
  40. package/docs/images/categoriesDemo.png +0 -0
  41. package/docs/images/inheritance_tree.png +0 -0
  42. package/docs/images/jsoneditor.png +0 -0
  43. package/docs/imask.html +192 -0
  44. package/docs/index.html +579 -0
  45. package/docs/materialize_css.html +164 -0
  46. package/docs/meta_schema.json +705 -0
  47. package/docs/multiple_upload_base64.html +65 -0
  48. package/docs/person.json +73 -0
  49. package/docs/polyfills/assign.js +29 -0
  50. package/docs/radio.html +156 -0
  51. package/docs/recursive.html +170 -0
  52. package/docs/select2.html +99 -0
  53. package/docs/selectize.html +100 -0
  54. package/docs/signature.html +42 -0
  55. package/docs/starrating.html +137 -0
  56. package/docs/upload.html +131 -0
  57. package/docs/uuid.html +70 -0
  58. package/docs/wysiwyg.html +56 -0
  59. package/jasmine.json +11 -0
  60. package/json-editor-json-editor-2.5.3-wb13.tgz +0 -0
  61. package/package.json +100 -0
  62. package/release-notes.md +88 -0
  63. package/src/core.js +412 -0
  64. package/src/defaults.js +402 -0
  65. package/src/editor.js +707 -0
  66. package/src/editors/ace.js +89 -0
  67. package/src/editors/array/choices.js +103 -0
  68. package/src/editors/array/select2.js +110 -0
  69. package/src/editors/array/selectize.js +103 -0
  70. package/src/editors/array.css +9 -0
  71. package/src/editors/array.css.js +3 -0
  72. package/src/editors/array.js +818 -0
  73. package/src/editors/autocomplete.js +58 -0
  74. package/src/editors/base64.js +157 -0
  75. package/src/editors/button.js +97 -0
  76. package/src/editors/checkbox.js +95 -0
  77. package/src/editors/choices.css +3 -0
  78. package/src/editors/choices.css.js +3 -0
  79. package/src/editors/choices.js +69 -0
  80. package/src/editors/colorpicker.js +103 -0
  81. package/src/editors/datetime.js +141 -0
  82. package/src/editors/describedby.js +188 -0
  83. package/src/editors/enum.js +136 -0
  84. package/src/editors/hidden.js +127 -0
  85. package/src/editors/index.js +81 -0
  86. package/src/editors/info.js +20 -0
  87. package/src/editors/integer.js +19 -0
  88. package/src/editors/ip.js +36 -0
  89. package/src/editors/jodit.js +64 -0
  90. package/src/editors/multiple.js +409 -0
  91. package/src/editors/multiselect.js +218 -0
  92. package/src/editors/null.js +18 -0
  93. package/src/editors/number.js +51 -0
  94. package/src/editors/object.css +41 -0
  95. package/src/editors/object.css.js +3 -0
  96. package/src/editors/object.js +1290 -0
  97. package/src/editors/radio.js +111 -0
  98. package/src/editors/sceditor.js +72 -0
  99. package/src/editors/select.js +370 -0
  100. package/src/editors/select2.js +110 -0
  101. package/src/editors/selectize.js +112 -0
  102. package/src/editors/signature.js +113 -0
  103. package/src/editors/simplemde.js +100 -0
  104. package/src/editors/starrating.css +52 -0
  105. package/src/editors/starrating.css.js +3 -0
  106. package/src/editors/starrating.js +135 -0
  107. package/src/editors/stepper.js +27 -0
  108. package/src/editors/string.js +372 -0
  109. package/src/editors/table.js +516 -0
  110. package/src/editors/upload.js +321 -0
  111. package/src/editors/uuid.js +56 -0
  112. package/src/iconlib.js +24 -0
  113. package/src/iconlibs/bootstrap2.js +28 -0
  114. package/src/iconlibs/bootstrap3.js +28 -0
  115. package/src/iconlibs/fontawesome3.js +28 -0
  116. package/src/iconlibs/fontawesome4.js +28 -0
  117. package/src/iconlibs/fontawesome5.js +28 -0
  118. package/src/iconlibs/foundation2.js +24 -0
  119. package/src/iconlibs/foundation3.js +28 -0
  120. package/src/iconlibs/index.js +25 -0
  121. package/src/iconlibs/jqueryui.js +28 -0
  122. package/src/iconlibs/materialicons.js +49 -0
  123. package/src/iconlibs/openiconic.js +28 -0
  124. package/src/iconlibs/spectre.js +28 -0
  125. package/src/resolvers.js +128 -0
  126. package/src/schemaloader.js +408 -0
  127. package/src/style.css +150 -0
  128. package/src/style.css.js +3 -0
  129. package/src/templates/default.js +52 -0
  130. package/src/templates/ejs.js +13 -0
  131. package/src/templates/handlebars.js +1 -0
  132. package/src/templates/hogan.js +10 -0
  133. package/src/templates/index.js +21 -0
  134. package/src/templates/lodash.js +9 -0
  135. package/src/templates/markup.js +9 -0
  136. package/src/templates/mustache.js +9 -0
  137. package/src/templates/swig.js +1 -0
  138. package/src/templates/underscore.js +9 -0
  139. package/src/theme.js +659 -0
  140. package/src/themes/barebones.css +35 -0
  141. package/src/themes/barebones.css.js +3 -0
  142. package/src/themes/barebones.js +28 -0
  143. package/src/themes/bootstrap2.js +319 -0
  144. package/src/themes/bootstrap3.css +0 -0
  145. package/src/themes/bootstrap3.css.js +3 -0
  146. package/src/themes/bootstrap3.js +315 -0
  147. package/src/themes/bootstrap4.css +89 -0
  148. package/src/themes/bootstrap4.css.js +3 -0
  149. package/src/themes/bootstrap4.js +690 -0
  150. package/src/themes/bootstrap5.css.js +3 -0
  151. package/src/themes/foundation.js +569 -0
  152. package/src/themes/html.css +60 -0
  153. package/src/themes/html.css.js +3 -0
  154. package/src/themes/html.js +71 -0
  155. package/src/themes/index.js +28 -0
  156. package/src/themes/jqueryui.js +198 -0
  157. package/src/themes/materialize.js +426 -0
  158. package/src/themes/spectre.css +208 -0
  159. package/src/themes/spectre.css.js +3 -0
  160. package/src/themes/spectre.js +406 -0
  161. package/src/themes/tailwind.css +249 -0
  162. package/src/themes/tailwind.css.js +3 -0
  163. package/src/themes/tailwind.js +443 -0
  164. package/src/utilities.js +138 -0
  165. package/src/validator.js +877 -0
  166. package/src/validators/ip-validator.js +51 -0
  167. package/tests/Dockerfile +3 -0
  168. package/tests/README.md +48 -0
  169. package/tests/codeceptjs/codecept.json +42 -0
  170. package/tests/codeceptjs/constrains/if-then-else_test.js +143 -0
  171. package/tests/codeceptjs/core_test.js +217 -0
  172. package/tests/codeceptjs/editors/advanced_test.js +13 -0
  173. package/tests/codeceptjs/editors/array_any_of_test.js +50 -0
  174. package/tests/codeceptjs/editors/array_test.js +900 -0
  175. package/tests/codeceptjs/editors/button_test.js +35 -0
  176. package/tests/codeceptjs/editors/checkbox_test.js +21 -0
  177. package/tests/codeceptjs/editors/colorpicker_test.js +27 -0
  178. package/tests/codeceptjs/editors/datetime_test.js +33 -0
  179. package/tests/codeceptjs/editors/inheritance_test.js +11 -0
  180. package/tests/codeceptjs/editors/integer_test.js +84 -0
  181. package/tests/codeceptjs/editors/issues/issue-gh-812_test.js +32 -0
  182. package/tests/codeceptjs/editors/jodit_test.js +24 -0
  183. package/tests/codeceptjs/editors/multiselect_test.js +8 -0
  184. package/tests/codeceptjs/editors/number_test.js +82 -0
  185. package/tests/codeceptjs/editors/object_test.js +204 -0
  186. package/tests/codeceptjs/editors/option-no_default_values_test.js +42 -0
  187. package/tests/codeceptjs/editors/programmatic-changes_test.js +20 -0
  188. package/tests/codeceptjs/editors/radio_test.js +10 -0
  189. package/tests/codeceptjs/editors/rating_test.js +13 -0
  190. package/tests/codeceptjs/editors/select_test.js +22 -0
  191. package/tests/codeceptjs/editors/stepper_test.js +27 -0
  192. package/tests/codeceptjs/editors/string_test.js +118 -0
  193. package/tests/codeceptjs/editors/table-confirm-delete_test.js +67 -0
  194. package/tests/codeceptjs/editors/tabs_test.js +14 -0
  195. package/tests/codeceptjs/editors/uuid_test.js +21 -0
  196. package/tests/codeceptjs/editors/validation_test.js +14 -0
  197. package/tests/codeceptjs/meta-schema_test.js +17 -0
  198. package/tests/codeceptjs/schemaloader_test.js +13 -0
  199. package/tests/codeceptjs/steps.d.ts +13 -0
  200. package/tests/codeceptjs/steps_file.js +12 -0
  201. package/tests/codeceptjs/themes_test.js +519 -0
  202. package/tests/docker-compose.yml +34 -0
  203. package/tests/fixtures/basic_person.json +26 -0
  204. package/tests/fixtures/nested_object.json +26 -0
  205. package/tests/fixtures/person.json +55 -0
  206. package/tests/fixtures/recursive.json +8 -0
  207. package/tests/fixtures/some_types.json +32 -0
  208. package/tests/fixtures/string.json +3 -0
  209. package/tests/fixtures/validation.json +1140 -0
  210. package/tests/pages/_demo.html +475 -0
  211. package/tests/pages/advanced.html +137 -0
  212. package/tests/pages/anyof.html +80 -0
  213. package/tests/pages/array-anyof.html +142 -0
  214. package/tests/pages/array-checkboxes.html +41 -0
  215. package/tests/pages/array-choices.html +45 -0
  216. package/tests/pages/array-integers.html +37 -0
  217. package/tests/pages/array-move-events.html +61 -0
  218. package/tests/pages/array-multiselects.html +42 -0
  219. package/tests/pages/array-nested-arrays.html +40 -0
  220. package/tests/pages/array-numbers.html +37 -0
  221. package/tests/pages/array-objects.html +42 -0
  222. package/tests/pages/array-ratings.html +40 -0
  223. package/tests/pages/array-selectize.html +51 -0
  224. package/tests/pages/array-selects.html +36 -0
  225. package/tests/pages/array-strings.html +36 -0
  226. package/tests/pages/array.html +42 -0
  227. package/tests/pages/assets/pages.css +130 -0
  228. package/tests/pages/button-callbacks.html +77 -0
  229. package/tests/pages/checkbox-labels.html +114 -0
  230. package/tests/pages/colorpicker-no-3rd-party.html +43 -0
  231. package/tests/pages/colorpicker-use-vanilla-picker.html +50 -0
  232. package/tests/pages/core.html +118 -0
  233. package/tests/pages/datetime.html +76 -0
  234. package/tests/pages/form-name.html +108 -0
  235. package/tests/pages/grid-strict.html +311 -0
  236. package/tests/pages/grid.html +284 -0
  237. package/tests/pages/if-then-else-allOf.html +117 -0
  238. package/tests/pages/inheritance.html +76 -0
  239. package/tests/pages/integer.html +68 -0
  240. package/tests/pages/issues/_template.html +50 -0
  241. package/tests/pages/issues/issue-gh-812.html +110 -0
  242. package/tests/pages/issues/issue-gh-823-meta-schema.html +35 -0
  243. package/tests/pages/issues/issue-gh-848.html +81 -0
  244. package/tests/pages/meta_schema.json +705 -0
  245. package/tests/pages/number.html +89 -0
  246. package/tests/pages/object-no-additional-properties.html +65 -0
  247. package/tests/pages/object-no-duplicated-id.html +68 -0
  248. package/tests/pages/object-required-properties.html +236 -0
  249. package/tests/pages/object-with-dependencies-array.html +46 -0
  250. package/tests/pages/object-with-dependencies.html +60 -0
  251. package/tests/pages/object.html +79 -0
  252. package/tests/pages/oneof.html +103 -0
  253. package/tests/pages/option-no_default_values.html +58 -0
  254. package/tests/pages/programmatic-changes.html +120 -0
  255. package/tests/pages/read-only.html +105 -0
  256. package/tests/pages/select.html +41 -0
  257. package/tests/pages/stepper.html +59 -0
  258. package/tests/pages/string-ace-editor.html +52 -0
  259. package/tests/pages/string-cleave.html +46 -0
  260. package/tests/pages/string-custom-attributes.html +62 -0
  261. package/tests/pages/string-formats.html +52 -0
  262. package/tests/pages/string-formats2.html +57 -0
  263. package/tests/pages/string-jodit-editor.html +49 -0
  264. package/tests/pages/string-sceditor.html +62 -0
  265. package/tests/pages/table-move-events.html +56 -0
  266. package/tests/pages/table.html +46 -0
  267. package/tests/pages/tabs.html +131 -0
  268. package/tests/pages/themes.html +527 -0
  269. package/tests/pages/translate-property.html +247 -0
  270. package/tests/pages/urn.html +93 -0
  271. package/tests/pages/uuid.html +72 -0
  272. package/tests/pages/validation.html +99 -0
  273. package/tests/unit/.eslintrc +8 -0
  274. package/tests/unit/core.spec.js +309 -0
  275. package/tests/unit/defaults.spec.js +40 -0
  276. package/tests/unit/editor.spec.js +160 -0
  277. package/tests/unit/editors/array.spec.js +86 -0
  278. package/tests/unit/editors/object.spec.js +79 -0
  279. package/tests/unit/editors/table.spec.js +91 -0
  280. package/tests/unit/readme.md +35 -0
  281. package/tests/unit/schemaloader.spec.js +498 -0
  282. package/tests/unit/validator.spec.js +94 -0
  283. package/tests/unit/validators/ip-validator.spec.js +62 -0
package/src/editor.js ADDED
@@ -0,0 +1,707 @@
1
+ import { extend, hasOwnProperty } from './utilities.js'
2
+
3
+ /**
4
+ * All editors should extend from this class
5
+ */
6
+ export class AbstractEditor {
7
+ constructor (options, defaults) {
8
+ this.defaults = defaults
9
+ this.jsoneditor = options.jsoneditor
10
+ this.theme = this.jsoneditor.theme
11
+ this.template_engine = this.jsoneditor.template
12
+ this.iconlib = this.jsoneditor.iconlib
13
+ this.translate = this.jsoneditor.translate || this.defaults.translate
14
+ this.translateProperty = this.jsoneditor.translateProperty || this.defaults.translateProperty
15
+ this.original_schema = options.schema
16
+ this.schema = this.jsoneditor.expandSchema(this.original_schema)
17
+ this.active = true
18
+ this.options = extend({}, (this.options || {}), (this.schema.options || {}), (options.schema.options || {}), options)
19
+
20
+ this.formname = this.jsoneditor.options.form_name_root || 'root'
21
+
22
+ if (!options.path && !this.schema.id) this.schema.id = this.formname
23
+ this.path = options.path || this.formname
24
+ this.formname = options.formname || this.path.replace(/\.([^.]+)/g, '[$1]')
25
+
26
+ this.parent = options.parent
27
+ this.key = this.parent !== undefined ? this.path.split('.').slice(this.parent.path.split('.').length).join('.') : this.path
28
+
29
+ this.link_watchers = []
30
+ this.watchLoop = false
31
+
32
+ if (options.container) this.setContainer(options.container)
33
+ this.registerDependencies()
34
+ }
35
+
36
+ onChildEditorChange (editor) {
37
+ this.onChange(true)
38
+ }
39
+
40
+ notify () {
41
+ if (this.path) this.jsoneditor.notifyWatchers(this.path)
42
+ }
43
+
44
+ change () {
45
+ if (this.parent) this.parent.onChildEditorChange(this)
46
+ else if (this.jsoneditor) this.jsoneditor.onChange()
47
+ }
48
+
49
+ onChange (bubble) {
50
+ this.notify()
51
+ if (this.watch_listener) this.watch_listener()
52
+ if (bubble) this.change()
53
+ }
54
+
55
+ register () {
56
+ this.jsoneditor.registerEditor(this)
57
+ this.onChange()
58
+ }
59
+
60
+ unregister () {
61
+ if (!this.jsoneditor) return
62
+ this.jsoneditor.unregisterEditor(this)
63
+ }
64
+
65
+ getNumColumns () {
66
+ return 12
67
+ }
68
+
69
+ isActive () {
70
+ return this.active
71
+ }
72
+
73
+ activate () {
74
+ this.active = true
75
+ this.optInCheckbox.checked = true
76
+ this.enable()
77
+ this.change()
78
+ }
79
+
80
+ deactivate () {
81
+ /* only non required properties can be deactivated. */
82
+ if (!this.isRequired()) {
83
+ this.active = false
84
+ this.optInCheckbox.checked = false
85
+ this.disable()
86
+ this.change()
87
+ }
88
+ }
89
+
90
+ registerDependencies () {
91
+ this.dependenciesFulfilled = true
92
+ const deps = this.options.dependencies
93
+ if (!deps) {
94
+ return
95
+ }
96
+
97
+ Object.keys(deps).forEach(dependency => {
98
+ let path = this.path.split('.')
99
+ path[path.length - 1] = dependency
100
+ path = path.join('.')
101
+ this.jsoneditor.watch(path, () => {
102
+ this.evaluateDependencies()
103
+ })
104
+ })
105
+ }
106
+
107
+ evaluateDependencies () {
108
+ const wrapper = this.container || this.control
109
+ if (!wrapper || this.jsoneditor === null) {
110
+ return
111
+ }
112
+
113
+ const deps = this.options.dependencies
114
+ if (!deps) {
115
+ return
116
+ }
117
+ // Assume true and set to false if any unmet dependencies are found
118
+ const previousStatus = this.dependenciesFulfilled
119
+ this.dependenciesFulfilled = true
120
+
121
+ Object.keys(deps).forEach(dependency => {
122
+ let path = this.path.split('.')
123
+ path[path.length - 1] = dependency
124
+ path = path.join('.')
125
+ const choices = deps[dependency]
126
+ this.checkDependency(path, choices)
127
+ })
128
+
129
+ if (this.dependenciesFulfilled !== previousStatus) {
130
+ this.notify()
131
+ }
132
+
133
+ let displayMode = this.dependenciesFulfilled ? 'block' : 'none'
134
+
135
+ if (this.options.hidden) {
136
+ displayMode = 'none'
137
+ }
138
+
139
+ if (wrapper.tagName === 'TD') {
140
+ Object.keys(wrapper.childNodes).forEach(child => (wrapper.childNodes[child].style.display = displayMode))
141
+ } else wrapper.style.display = displayMode
142
+ }
143
+
144
+ checkDependency (path, choices) {
145
+ if (this.path === path || this.jsoneditor === null) {
146
+ return
147
+ }
148
+
149
+ const editor = this.jsoneditor.getEditor(path)
150
+ const value = editor ? editor.getValue() : undefined
151
+
152
+ if (!editor || !editor.dependenciesFulfilled) {
153
+ this.dependenciesFulfilled = false
154
+ } else if (Array.isArray(choices)) {
155
+ this.dependenciesFulfilled = choices.some(choice => {
156
+ if (value === choice) {
157
+ return true
158
+ }
159
+ })
160
+ } else if (typeof choices === 'object') {
161
+ if (typeof value !== 'object') {
162
+ this.dependenciesFulfilled = choices === value
163
+ } else {
164
+ Object.keys(choices).some(key => {
165
+ if (!hasOwnProperty(choices, key)) {
166
+ return false
167
+ }
168
+ if (!hasOwnProperty(value, key) || choices[key] !== value[key]) {
169
+ this.dependenciesFulfilled = false
170
+ return true
171
+ }
172
+ })
173
+ }
174
+ } else if (typeof choices === 'string' || typeof choices === 'number') {
175
+ this.dependenciesFulfilled = this.dependenciesFulfilled && value === choices
176
+ } else if (typeof choices === 'boolean') {
177
+ if (choices) {
178
+ this.dependenciesFulfilled = this.dependenciesFulfilled && (value || value.length > 0)
179
+ } else {
180
+ this.dependenciesFulfilled = this.dependenciesFulfilled && (!value || value.length === 0)
181
+ }
182
+ }
183
+ }
184
+
185
+ setContainer (container) {
186
+ this.container = container
187
+ if (this.schema.id) this.container.setAttribute('data-schemaid', this.schema.id)
188
+ if (this.schema.type && typeof this.schema.type === 'string') this.container.setAttribute('data-schematype', this.schema.type)
189
+ this.container.setAttribute('data-schemapath', this.path)
190
+ }
191
+
192
+ setOptInCheckbox (header) {
193
+ /* the active/deactive checbox control. */
194
+
195
+ this.optInCheckbox = document.createElement('input')
196
+ this.optInCheckbox.setAttribute('type', 'checkbox')
197
+ this.optInCheckbox.setAttribute('style', 'margin: 0 10px 0 0;')
198
+ this.optInCheckbox.classList.add('json-editor-opt-in')
199
+
200
+ this.optInCheckbox.addEventListener('click', () => {
201
+ if (this.isActive()) {
202
+ this.deactivate()
203
+ } else {
204
+ this.activate()
205
+ }
206
+ })
207
+
208
+ /* append active/deactive checkbox if show_opt_in is true */
209
+ if (this.jsoneditor.options.show_opt_in || this.options.show_opt_in) {
210
+ /* and control to type object editors if they are not required */
211
+ if (this.parent && this.parent.schema.type === 'object' && !this.isRequired() && this.header) {
212
+ this.header.appendChild(this.optInCheckbox)
213
+ this.header.insertBefore(this.optInCheckbox, this.header.firstChild)
214
+ this.active = false
215
+ this.optInCheckbox.checked = false
216
+ this.disable()
217
+ }
218
+ }
219
+ }
220
+
221
+ preBuild () {
222
+
223
+ }
224
+
225
+ build () {
226
+
227
+ }
228
+
229
+ postBuild () {
230
+ this.setupWatchListeners()
231
+ this.addLinks()
232
+ this.setValue(this.getDefault(), true)
233
+ this.updateHeaderText()
234
+ this.register()
235
+ this.onWatchedFieldChange()
236
+ }
237
+
238
+ setupWatchListeners () {
239
+ /* Watched fields */
240
+ this.watched = {}
241
+ if (this.schema.vars) this.schema.watch = this.schema.vars
242
+ this.watched_values = {}
243
+ this.watch_listener = () => {
244
+ if (this.refreshWatchedFieldValues()) {
245
+ this.onWatchedFieldChange()
246
+ }
247
+ }
248
+
249
+ if (hasOwnProperty(this.schema, 'watch')) {
250
+ let path; let pathParts; let first; let root; let adjustedPath
251
+ const myPath = this.container.getAttribute('data-schemapath')
252
+
253
+ Object.keys(this.schema.watch).forEach(name => {
254
+ path = this.schema.watch[name]
255
+ if (Array.isArray(path)) {
256
+ if (path.length < 2) return
257
+ pathParts = [path[0]].concat(path[1].split('.'))
258
+ } else {
259
+ pathParts = path.split('.')
260
+ if (!this.theme.closest(this.container, `[data-schemaid="${pathParts[0]}"]`)) pathParts.unshift('#')
261
+ }
262
+ first = pathParts.shift()
263
+
264
+ if (first === '#') first = this.jsoneditor.schema.id || this.jsoneditor.root.formname
265
+
266
+ /* Find the root node for this template variable */
267
+ root = this.theme.closest(this.container, `[data-schemaid="${first}"]`)
268
+ if (!root) throw new Error(`Could not find ancestor node with id ${first}`)
269
+
270
+ /* Keep track of the root node and path for use when rendering the template */
271
+ adjustedPath = `${root.getAttribute('data-schemapath')}.${pathParts.join('.')}`
272
+
273
+ if (myPath.startsWith(adjustedPath)) this.watchLoop = true
274
+ this.jsoneditor.watch(adjustedPath, this.watch_listener)
275
+
276
+ this.watched[name] = adjustedPath
277
+ })
278
+ }
279
+
280
+ /* Dynamic header */
281
+ if (this.schema.headerTemplate) {
282
+ this.header_template = this.jsoneditor.compileTemplate(this.translateProperty(this.schema.headerTemplate), this.template_engine)
283
+ }
284
+ }
285
+
286
+ addLinks () {
287
+ /* Add links */
288
+ if (!this.no_link_holder) {
289
+ this.link_holder = this.theme.getLinksHolder()
290
+ /* if description element exists, insert the link before */
291
+ if (typeof this.description !== 'undefined') this.description.parentNode.insertBefore(this.link_holder, this.description)
292
+ /* otherwise just insert link at bottom of container */
293
+ else this.container.appendChild(this.link_holder)
294
+ if (this.schema.links) {
295
+ for (let i = 0; i < this.schema.links.length; i++) {
296
+ this.addLink(this.getLink(this.schema.links[i]))
297
+ }
298
+ }
299
+ }
300
+ }
301
+
302
+ onMove () {}
303
+
304
+ getButton (text, icon, title, args = []) {
305
+ const btnClass = `json-editor-btn-${icon}`
306
+ if (!this.iconlib) icon = null
307
+ else icon = this.iconlib.getIcon(icon)
308
+
309
+ text = this.translate(text, args)
310
+ title = this.translate(title, args)
311
+
312
+ if (!icon && title) {
313
+ text = title
314
+ title = null
315
+ }
316
+
317
+ const btn = this.theme.getButton(text, icon, title)
318
+ btn.classList.add(btnClass)
319
+ return btn
320
+ }
321
+
322
+ setButtonText (button, text, icon, title, args = []) {
323
+ if (!this.iconlib) icon = null
324
+ else icon = this.iconlib.getIcon(icon)
325
+
326
+ text = this.translate(text, args)
327
+ title = this.translate(title, args)
328
+
329
+ if (!icon && title) {
330
+ text = title
331
+ title = null
332
+ }
333
+
334
+ return this.theme.setButtonText(button, text, icon, title)
335
+ }
336
+
337
+ addLink (link) {
338
+ if (this.link_holder) this.link_holder.appendChild(link)
339
+ }
340
+
341
+ getLink (data) {
342
+ let holder
343
+ let link
344
+
345
+ /* Get mime type of the link */
346
+ const mime = data.mediaType || 'application/javascript'
347
+ const type = mime.split('/')[0]
348
+
349
+ /* Template to generate the link href */
350
+ const href = this.jsoneditor.compileTemplate(data.href, this.template_engine)
351
+ const relTemplate = this.jsoneditor.compileTemplate(data.rel ? data.rel : data.href, this.template_engine)
352
+
353
+ /* Template to generate the link's download attribute */
354
+ let download = null
355
+ if (data.download) download = data.download
356
+
357
+ if (download && download !== true) {
358
+ download = this.jsoneditor.compileTemplate(download, this.template_engine)
359
+ }
360
+
361
+ /* Image links */
362
+ if (type === 'image') {
363
+ holder = this.theme.getBlockLinkHolder()
364
+ link = document.createElement('a')
365
+ link.setAttribute('target', '_blank')
366
+ const image = document.createElement('img')
367
+
368
+ this.theme.createImageLink(holder, link, image)
369
+
370
+ /* When a watched field changes, update the url */
371
+ this.link_watchers.push(vars => {
372
+ const url = href(vars)
373
+ const rel = relTemplate(vars)
374
+ link.setAttribute('href', url)
375
+ link.setAttribute('title', rel || url)
376
+ image.setAttribute('src', url)
377
+ })
378
+ /* Audio/Video links */
379
+ } else if (['audio', 'video'].includes(type)) {
380
+ holder = this.theme.getBlockLinkHolder()
381
+
382
+ link = this.theme.getBlockLink()
383
+ link.setAttribute('target', '_blank')
384
+
385
+ const media = document.createElement(type)
386
+ media.setAttribute('controls', 'controls')
387
+
388
+ this.theme.createMediaLink(holder, link, media)
389
+
390
+ /* When a watched field changes, update the url */
391
+ this.link_watchers.push(vars => {
392
+ const url = href(vars)
393
+ const rel = relTemplate(vars)
394
+ link.setAttribute('href', url)
395
+ link.textContent = rel || url
396
+ media.setAttribute('src', url)
397
+ })
398
+ /* Text links or blank link */
399
+ } else {
400
+ link = holder = this.theme.getBlockLink()
401
+ holder.setAttribute('target', '_blank')
402
+ holder.textContent = data.rel
403
+ holder.style.display = 'none' /* Prevent blank links from showing up when using custom view */
404
+
405
+ /* When a watched field changes, update the url */
406
+ this.link_watchers.push(vars => {
407
+ const url = href(vars)
408
+ const rel = relTemplate(vars)
409
+ if (url) holder.style.display = ''
410
+ holder.setAttribute('href', url)
411
+ holder.textContent = rel || url
412
+ })
413
+ }
414
+
415
+ if (download && link) {
416
+ if (download === true) {
417
+ link.setAttribute('download', '')
418
+ } else {
419
+ this.link_watchers.push(vars => {
420
+ link.setAttribute('download', download(vars))
421
+ })
422
+ }
423
+ }
424
+
425
+ if (data.class) link.classList.add(data.class)
426
+
427
+ return holder
428
+ }
429
+
430
+ refreshWatchedFieldValues () {
431
+ if (!this.watched_values) return
432
+ const watched = {}
433
+ let changed = false
434
+
435
+ if (this.watched) {
436
+ Object.keys(this.watched).forEach(name => {
437
+ const editor = this.jsoneditor.getEditor(this.watched[name])
438
+ const val = editor ? editor.getValue() : null
439
+ if (this.watched_values[name] !== val) changed = true
440
+ watched[name] = val
441
+ })
442
+ }
443
+
444
+ watched.self = this.getValue()
445
+ if (this.watched_values.self !== watched.self) changed = true
446
+
447
+ this.watched_values = watched
448
+
449
+ return changed
450
+ }
451
+
452
+ getWatchedFieldValues () {
453
+ return this.watched_values
454
+ }
455
+
456
+ updateHeaderText () {
457
+ if (this.header) {
458
+ const headerText = this.getHeaderText()
459
+ /* If the header has children, only update the text node's value */
460
+ if (this.header.children.length) {
461
+ for (let i = 0; i < this.header.childNodes.length; i++) {
462
+ if (this.header.childNodes[i].nodeType === 3) {
463
+ this.header.childNodes[i].nodeValue = this.cleanText(headerText)
464
+ break
465
+ }
466
+ }
467
+ /* Otherwise, just update the entire node */
468
+ } else {
469
+ if (window.DOMPurify) this.header.innerHTML = window.DOMPurify.sanitize(headerText)
470
+ else this.header.textContent = this.cleanText(headerText)
471
+ }
472
+ }
473
+ }
474
+
475
+ getHeaderText (titleOnly) {
476
+ if (this.header_text) return this.header_text
477
+ else if (titleOnly) return this.translateProperty(this.schema.title)
478
+ else return this.getTitle()
479
+ }
480
+
481
+ getPathDepth () {
482
+ return this.path.split('.').length
483
+ }
484
+
485
+ cleanText (txt) {
486
+ /* Clean out HTML tags from txt */
487
+ const tmp = document.createElement('div')
488
+ tmp.innerHTML = txt
489
+ return (tmp.textContent || tmp.innerText)
490
+ }
491
+
492
+ onWatchedFieldChange () {
493
+ let vars
494
+ if (this.header_template) {
495
+ vars = extend(this.getWatchedFieldValues(), {
496
+ key: this.key,
497
+ i: this.key,
498
+ i0: (this.key * 1),
499
+ i1: (this.key * 1 + 1),
500
+ title: this.getTitle()
501
+ })
502
+ const headerText = this.header_template(vars)
503
+
504
+ if (headerText !== this.header_text) {
505
+ this.header_text = headerText
506
+ this.updateHeaderText()
507
+ this.notify()
508
+ /* this.fireChangeHeaderEvent(); */
509
+ }
510
+ }
511
+ if (this.link_watchers.length) {
512
+ vars = this.getWatchedFieldValues()
513
+ for (let i = 0; i < this.link_watchers.length; i++) {
514
+ this.link_watchers[i](vars)
515
+ }
516
+ }
517
+ }
518
+
519
+ setValue (value) {
520
+ this.value = value
521
+ }
522
+
523
+ getValue () {
524
+ if (!this.dependenciesFulfilled) {
525
+ return undefined
526
+ }
527
+ return this.value
528
+ }
529
+
530
+ refreshValue () {
531
+
532
+ }
533
+
534
+ getChildEditors () {
535
+ return false
536
+ }
537
+
538
+ destroy () {
539
+ this.unregister(this)
540
+ if (this.watched) {
541
+ Object.values(this.watched).forEach(adjustedPath => this.jsoneditor.unwatch(adjustedPath, this.watch_listener))
542
+ }
543
+
544
+ this.watched = null
545
+ this.watched_values = null
546
+ this.watch_listener = null
547
+ this.header_text = null
548
+ this.header_template = null
549
+ this.value = null
550
+ if (this.container && this.container.parentNode) this.container.parentNode.removeChild(this.container)
551
+ this.container = null
552
+ this.jsoneditor = null
553
+ this.schema = null
554
+ this.path = null
555
+ this.key = null
556
+ this.parent = null
557
+ }
558
+
559
+ isDefaultRequired () {
560
+ return this.isRequired() || !!this.jsoneditor.options.use_default_values
561
+ }
562
+
563
+ getDefault () {
564
+ if (typeof this.schema.default !== 'undefined') {
565
+ return typeof this.schema.default === 'string' ? this.translateProperty(this.schema.default) : this.schema.default
566
+ }
567
+
568
+ if (typeof this.schema.enum !== 'undefined') {
569
+ return this.schema.enum[0]
570
+ }
571
+
572
+ let type = this.schema.type || this.schema.oneOf
573
+ if (type && Array.isArray(type)) type = type[0]
574
+ if (type && typeof type === 'object') type = type.type
575
+ if (type && Array.isArray(type)) type = type[0]
576
+
577
+ if (typeof type === 'string') {
578
+ if (type === 'number') return this.isDefaultRequired() ? 0.0 : undefined
579
+ if (type === 'boolean') return this.isDefaultRequired() ? false : undefined
580
+ if (type === 'integer') return this.isDefaultRequired() ? 0 : undefined
581
+ if (type === 'string') return ''
582
+ if (type === 'object') return {}
583
+ if (type === 'array') return []
584
+ }
585
+
586
+ return null
587
+ }
588
+
589
+ getTitle () {
590
+ return this.translateProperty(this.schema.title || this.key)
591
+ }
592
+
593
+ enable () {
594
+ this.disabled = false
595
+ }
596
+
597
+ disable () {
598
+ this.disabled = true
599
+ }
600
+
601
+ isEnabled () {
602
+ return !this.disabled
603
+ }
604
+
605
+ isRequired () {
606
+ if (typeof this.schema.required === 'boolean') return this.schema.required
607
+ else if (this.parent && this.parent.schema && Array.isArray(this.parent.schema.required)) return this.parent.schema.required.includes(this.key)
608
+ else if (this.jsoneditor.options.required_by_default) return true
609
+ else return false
610
+ }
611
+
612
+ getDisplayText (arr) {
613
+ const disp = []
614
+ const used = {}
615
+
616
+ /* Determine how many times each attribute name is used. */
617
+ /* This helps us pick the most distinct display text for the schemas. */
618
+ arr.forEach(el => {
619
+ if (el.title) {
620
+ const title = this.translateProperty(el.title)
621
+ used[title] = used[title] || 0
622
+ used[title]++
623
+ }
624
+ if (el.description) {
625
+ used[el.description] = used[el.description] || 0
626
+ used[el.description]++
627
+ }
628
+ if (el.format) {
629
+ used[el.format] = used[el.format] || 0
630
+ used[el.format]++
631
+ }
632
+ if (el.type) {
633
+ used[el.type] = used[el.type] || 0
634
+ used[el.type]++
635
+ }
636
+ })
637
+
638
+ /* Determine display text for each element of the array */
639
+ arr.forEach(el => {
640
+ let name
641
+ const title = this.translateProperty(el.title)
642
+
643
+ /* If it's a simple string */
644
+ if (typeof el === 'string') name = el
645
+ /* Object */
646
+ else if (el.title && used[title] <= 1) name = title
647
+ else if (el.format && used[el.format] <= 1) name = el.format
648
+ else if (el.type && used[el.type] <= 1) name = el.type
649
+ else if (el.description && used[el.description] <= 1) name = el.descripton
650
+ else if (el.title) name = title
651
+ else if (el.format) name = el.format
652
+ else if (el.type) name = el.type
653
+ else if (el.description) name = el.description
654
+ else if (JSON.stringify(el).length < 500) name = JSON.stringify(el)
655
+ else name = 'type'
656
+
657
+ disp.push(name)
658
+ })
659
+
660
+ /* Replace identical display text with "text 1", "text 2", etc. */
661
+ const inc = {}
662
+ disp.forEach((name, i) => {
663
+ inc[name] = inc[name] || 0
664
+ inc[name]++
665
+
666
+ if (used[name] > 1) disp[i] = `${name} ${inc[name]}`
667
+ })
668
+
669
+ return disp
670
+ }
671
+
672
+ /* Replace space(s) with "-" to create valid id value */
673
+ getValidId (id) {
674
+ id = id === undefined ? '' : id.toString()
675
+ return id.replace(/\s+/g, '-')
676
+ }
677
+
678
+ setInputAttributes (inputAttribute) {
679
+ if (this.schema.options && this.schema.options.inputAttributes) {
680
+ const inputAttributes = this.schema.options.inputAttributes
681
+ const protectedAttributes = ['name', 'type'].concat(inputAttribute)
682
+ Object.keys(inputAttributes).forEach(key => {
683
+ if (!protectedAttributes.includes(key.toLowerCase())) {
684
+ this.input.setAttribute(key, key === 'placeholder' ? this.translateProperty(inputAttributes[key]) : inputAttributes[key])
685
+ }
686
+ })
687
+ }
688
+ }
689
+
690
+ expandCallbacks (scope, options) {
691
+ const callback = this.defaults.callbacks[scope]
692
+ Object.entries(options).forEach(([key, value]) => {
693
+ if (value === Object(value)) {
694
+ options[key] = this.expandCallbacks(scope, value)
695
+ } else if (typeof value === 'string' &&
696
+ typeof callback === 'object' &&
697
+ typeof callback[value] === 'function') {
698
+ options[key] = callback[value].bind(null, this)
699
+ }
700
+ })
701
+ return options
702
+ }
703
+
704
+ showValidationErrors (errors) {
705
+
706
+ }
707
+ }