@json-editor/json-editor 1.3.5 → 1.17.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 (436) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.env +2 -0
  3. package/.env-dist +2 -0
  4. package/.eslintrc +10 -0
  5. package/.github/PULL_REQUEST_TEMPLATE.md +9 -0
  6. package/.github/workflows/build.yml +70 -0
  7. package/.travis.yml +63 -9
  8. package/CHANGELOG.md +1152 -0
  9. package/CONTRIBUTING.md +44 -5
  10. package/Makefile +26 -0
  11. package/README.md +765 -138
  12. package/README_ADDON.md +577 -0
  13. package/UPGRADING.md +46 -0
  14. package/build/CssToJson.js +55 -0
  15. package/codecept.conf.js +35 -0
  16. package/config/.eslintrc +7 -0
  17. package/config/codeceptjs_helpers.js +146 -0
  18. package/config/helpers.js +10 -0
  19. package/config/karma.conf.js +89 -0
  20. package/config/readme.md +31 -0
  21. package/config/webpack.common.js +71 -0
  22. package/config/webpack.dev.js +15 -0
  23. package/config/webpack.nonmin.js +19 -0
  24. package/config/webpack.prod.js +24 -0
  25. package/dist/jsoneditor.js +2 -11013
  26. package/dist/jsoneditor.js.LICENSE.txt +15 -0
  27. package/dist/nonmin/jsoneditor.js +31392 -0
  28. package/dist/nonmin/jsoneditor.js.map +1 -0
  29. package/docs/basic.html +2 -2
  30. package/docs/basic_person.json +2 -1
  31. package/docs/choices.html +86 -0
  32. package/docs/cleave.html +11 -19
  33. package/docs/colorpicker.html +194 -0
  34. package/docs/css_integration.html +56 -54
  35. package/docs/custom-editor.html +92 -0
  36. package/docs/datetime.html +48 -21
  37. package/docs/describedby.html +161 -0
  38. package/docs/enumsource.html +67 -0
  39. package/docs/form-submission.html +162 -0
  40. package/docs/imask.html +192 -0
  41. package/docs/index.html +606 -423
  42. package/docs/materialize_css.html +1 -1
  43. package/docs/meta-schema.html +793 -0
  44. package/docs/meta_schema.json +446 -400
  45. package/docs/polyfills/assign.js +29 -0
  46. package/docs/radio.html +156 -0
  47. package/docs/recursive.html +15 -17
  48. package/docs/scripts/ajv-validator.js +8695 -0
  49. package/docs/select2.html +15 -9
  50. package/docs/selectize.html +11 -9
  51. package/docs/signature.html +12 -11
  52. package/docs/starrating.html +5 -17
  53. package/docs/upload.html +33 -23
  54. package/docs/uuid.html +70 -0
  55. package/docs/wysiwyg.html +14 -7
  56. package/jasmine.json +11 -0
  57. package/package.json +74 -25
  58. package/release-notes.md +90 -0
  59. package/src/core.js +411 -611
  60. package/src/defaults.js +385 -306
  61. package/src/editor.js +761 -520
  62. package/src/editors/ace.js +90 -0
  63. package/src/editors/array/choices.js +104 -0
  64. package/src/editors/array/select2.js +112 -0
  65. package/src/editors/array/selectize.js +108 -86
  66. package/src/editors/array.css +9 -0
  67. package/src/editors/array.css.js +3 -0
  68. package/src/editors/array.js +812 -695
  69. package/src/editors/autocomplete.js +59 -0
  70. package/src/editors/base64.js +148 -129
  71. package/src/editors/button.js +104 -0
  72. package/src/editors/checkbox.js +111 -76
  73. package/src/editors/choices.css +3 -0
  74. package/src/editors/choices.css.js +3 -0
  75. package/src/editors/choices.js +71 -0
  76. package/src/editors/colorpicker.js +105 -0
  77. package/src/editors/datetime.js +93 -79
  78. package/src/editors/describedby.js +190 -0
  79. package/src/editors/enum.js +131 -116
  80. package/src/editors/hidden.js +100 -86
  81. package/src/editors/index.js +81 -0
  82. package/src/editors/info.js +28 -0
  83. package/src/editors/integer.js +21 -8
  84. package/src/editors/ip.js +36 -0
  85. package/src/editors/jodit.js +66 -0
  86. package/src/editors/multiple.js +380 -241
  87. package/src/editors/multiselect.js +207 -207
  88. package/src/editors/null.js +15 -11
  89. package/src/editors/number.js +39 -30
  90. package/src/editors/object.css +41 -0
  91. package/src/editors/object.css.js +3 -0
  92. package/src/editors/object.js +1209 -1007
  93. package/src/editors/radio.js +128 -0
  94. package/src/editors/sceditor.js +74 -0
  95. package/src/editors/select.js +318 -342
  96. package/src/editors/select2.js +112 -0
  97. package/src/editors/selectize.js +89 -338
  98. package/src/editors/signature.js +100 -104
  99. package/src/editors/simplemde.js +103 -0
  100. package/src/{styles → editors}/starrating.css +11 -2
  101. package/src/editors/starrating.css.js +3 -0
  102. package/src/editors/starrating.js +126 -95
  103. package/src/editors/stepper.js +27 -0
  104. package/src/editors/string.js +352 -426
  105. package/src/editors/table.js +486 -410
  106. package/src/editors/upload.js +297 -131
  107. package/src/editors/uuid.js +49 -0
  108. package/src/iconlib.js +22 -27
  109. package/src/iconlibs/bootstrap.js +28 -0
  110. package/src/iconlibs/bootstrap2.js +28 -17
  111. package/src/iconlibs/bootstrap3.js +28 -17
  112. package/src/iconlibs/fontawesome3.js +28 -17
  113. package/src/iconlibs/fontawesome4.js +28 -18
  114. package/src/iconlibs/fontawesome5.js +28 -18
  115. package/src/iconlibs/foundation2.js +24 -17
  116. package/src/iconlibs/foundation3.js +28 -17
  117. package/src/iconlibs/index.js +27 -0
  118. package/src/iconlibs/jqueryui.js +28 -17
  119. package/src/iconlibs/materialicons.js +49 -44
  120. package/src/iconlibs/openiconic.js +28 -0
  121. package/src/iconlibs/spectre.js +28 -0
  122. package/src/resolvers.js +135 -0
  123. package/src/schemaloader.js +639 -0
  124. package/src/style.css +157 -0
  125. package/src/style.css.js +3 -0
  126. package/src/templates/default.js +46 -51
  127. package/src/templates/ejs.js +8 -10
  128. package/src/templates/handlebars.js +1 -3
  129. package/src/templates/hogan.js +7 -9
  130. package/src/templates/index.js +21 -0
  131. package/src/templates/lodash.js +6 -8
  132. package/src/templates/markup.js +6 -8
  133. package/src/templates/mustache.js +6 -8
  134. package/src/templates/swig.js +1 -3
  135. package/src/templates/underscore.js +6 -8
  136. package/src/theme.js +717 -426
  137. package/src/themes/barebones.css +35 -0
  138. package/src/themes/barebones.css.js +3 -0
  139. package/src/themes/barebones.js +31 -0
  140. package/src/themes/bootstrap2.js +302 -264
  141. package/src/themes/bootstrap3.css +53 -0
  142. package/src/themes/bootstrap3.css.js +3 -0
  143. package/src/themes/bootstrap3.js +356 -259
  144. package/src/themes/bootstrap4.css +89 -0
  145. package/src/themes/bootstrap4.css.js +3 -0
  146. package/src/themes/bootstrap4.js +744 -234
  147. package/src/themes/bootstrap5.css +97 -0
  148. package/src/themes/bootstrap5.css.js +3 -0
  149. package/src/themes/bootstrap5.js +687 -0
  150. package/src/themes/foundation.js +539 -465
  151. package/src/themes/html.css +60 -0
  152. package/src/themes/html.css.js +3 -0
  153. package/src/themes/html.js +69 -82
  154. package/src/themes/index.js +29 -0
  155. package/src/themes/jqueryui.js +181 -166
  156. package/src/themes/materialize.js +263 -291
  157. package/src/themes/spectre.css +208 -0
  158. package/src/themes/spectre.css.js +3 -0
  159. package/src/themes/spectre.js +443 -0
  160. package/src/themes/tailwind.css +303 -0
  161. package/src/themes/tailwind.css.js +3 -0
  162. package/src/themes/tailwind.js +469 -0
  163. package/src/utilities.js +127 -68
  164. package/src/validator.js +914 -551
  165. package/src/validators/ip-validator.js +51 -0
  166. package/tests/Dockerfile +3 -0
  167. package/tests/README.md +27 -6
  168. package/tests/codeceptjs/codecept.json +29 -5
  169. package/tests/codeceptjs/constrains/contains_test.js +37 -0
  170. package/tests/codeceptjs/constrains/dependentRequired_test.js +33 -0
  171. package/tests/codeceptjs/constrains/dependentSchemas_test.js +16 -0
  172. package/tests/codeceptjs/constrains/if-then-else_test.js +186 -0
  173. package/tests/codeceptjs/core_test.js +534 -68
  174. package/tests/codeceptjs/editors/advanced_test.js +12 -10
  175. package/tests/codeceptjs/editors/array_any_of_test.js +50 -0
  176. package/tests/codeceptjs/editors/array_test.js +935 -677
  177. package/tests/codeceptjs/editors/autocomplete_test.js +15 -0
  178. package/tests/codeceptjs/editors/button_test.js +50 -0
  179. package/tests/codeceptjs/editors/checkbox_test.js +21 -8
  180. package/tests/codeceptjs/editors/colorpicker_test.js +29 -0
  181. package/tests/codeceptjs/editors/datetime_test.js +33 -0
  182. package/tests/codeceptjs/editors/inheritance_test.js +10 -0
  183. package/tests/codeceptjs/editors/integer_test.js +82 -71
  184. package/tests/codeceptjs/editors/jodit_test.js +23 -0
  185. package/tests/codeceptjs/editors/multiple_test.js +27 -0
  186. package/tests/codeceptjs/editors/multiselect_test.js +6 -9
  187. package/tests/codeceptjs/editors/number_test.js +75 -67
  188. package/tests/codeceptjs/editors/object_test.js +337 -28
  189. package/tests/codeceptjs/editors/option-no_default_values_test.js +42 -0
  190. package/tests/codeceptjs/editors/programmatic-changes_test.js +20 -0
  191. package/tests/codeceptjs/editors/purify_test.js +26 -0
  192. package/tests/codeceptjs/editors/radio_test.js +9 -0
  193. package/tests/codeceptjs/editors/range_test.js +10 -0
  194. package/tests/codeceptjs/editors/rating_test.js +10 -16
  195. package/tests/codeceptjs/editors/select_test.js +46 -23
  196. package/tests/codeceptjs/editors/starrating_test.js +15 -0
  197. package/tests/codeceptjs/editors/stepper_test.js +37 -0
  198. package/tests/codeceptjs/editors/string_test.js +108 -101
  199. package/tests/codeceptjs/editors/table-confirm-delete_test.js +60 -55
  200. package/tests/codeceptjs/editors/tabs_test.js +14 -11
  201. package/tests/codeceptjs/editors/uuid_test.js +48 -0
  202. package/tests/codeceptjs/editors/validation_test.js +13 -9
  203. package/tests/codeceptjs/issues/issue-gh-1133_test.js +9 -0
  204. package/tests/codeceptjs/issues/issue-gh-1158-2_test.js +10 -0
  205. package/tests/codeceptjs/issues/issue-gh-1158_test.js +8 -0
  206. package/tests/codeceptjs/issues/issue-gh-1164_test.js +9 -0
  207. package/tests/codeceptjs/issues/issue-gh-1171_test.js +11 -0
  208. package/tests/codeceptjs/issues/issue-gh-1211_test.js +17 -0
  209. package/tests/codeceptjs/issues/issue-gh-1257_test.js +11 -0
  210. package/tests/codeceptjs/issues/issue-gh-1272_test.js +21 -0
  211. package/tests/codeceptjs/issues/issue-gh-1330_test.js +8 -0
  212. package/tests/codeceptjs/issues/issue-gh-1338_test.js +18 -0
  213. package/tests/codeceptjs/issues/issue-gh-1347_test.js +8 -0
  214. package/tests/codeceptjs/issues/issue-gh-1364_test.js +13 -0
  215. package/tests/codeceptjs/issues/issue-gh-1367_test.js +11 -0
  216. package/tests/codeceptjs/issues/issue-gh-1383_test.js +9 -0
  217. package/tests/codeceptjs/issues/issue-gh-1384_test.js +9 -0
  218. package/tests/codeceptjs/issues/issue-gh-1410_test.js +13 -0
  219. package/tests/codeceptjs/issues/issue-gh-1422_test.js +9 -0
  220. package/tests/codeceptjs/issues/issue-gh-1431_test.js +12 -0
  221. package/tests/codeceptjs/issues/issue-gh-1439_test.js +12 -0
  222. package/tests/codeceptjs/issues/issue-gh-1452_test.js +10 -0
  223. package/tests/codeceptjs/issues/issue-gh-1453_test.js +18 -0
  224. package/tests/codeceptjs/issues/issue-gh-1461_test.js +14 -0
  225. package/tests/codeceptjs/issues/issue-gh-1463_test.js +9 -0
  226. package/tests/codeceptjs/issues/issue-gh-1471_test.js +17 -0
  227. package/tests/codeceptjs/issues/issue-gh-1485_test.js +13 -0
  228. package/tests/codeceptjs/issues/issue-gh-1491_test.js +9 -0
  229. package/tests/codeceptjs/issues/issue-gh-1525_test.js +9 -0
  230. package/tests/codeceptjs/issues/issue-gh-1536_test.js +12 -0
  231. package/tests/codeceptjs/issues/issue-gh-1538_test.js +10 -0
  232. package/tests/codeceptjs/issues/issue-gh-1541_test.js +8 -0
  233. package/tests/codeceptjs/issues/issue-gh-1559_test.js +15 -0
  234. package/tests/codeceptjs/issues/issue-gh-1562_test.js +12 -0
  235. package/tests/codeceptjs/issues/issue-gh-1586_test.js +15 -0
  236. package/tests/codeceptjs/issues/issue-gh-1636_test.js +9 -0
  237. package/tests/codeceptjs/issues/issue-gh-795_test.js +13 -0
  238. package/tests/codeceptjs/issues/issue-gh-810_test.js +52 -0
  239. package/tests/codeceptjs/issues/issue-gh-812_test.js +25 -0
  240. package/tests/codeceptjs/meta-schema_test.js +85 -0
  241. package/tests/codeceptjs/schemaloader_test.js +14 -0
  242. package/tests/codeceptjs/steps.d.ts +13 -0
  243. package/tests/codeceptjs/steps_file.js +4 -4
  244. package/tests/codeceptjs/test-config.js +3 -0
  245. package/tests/codeceptjs/themes_test.js +564 -0
  246. package/tests/docker-compose-local.yml +5 -0
  247. package/tests/docker-compose.yml +23 -17
  248. package/tests/fixtures/basic_person.json +2 -1
  249. package/tests/fixtures/definitions.json +22 -0
  250. package/tests/fixtures/nested_object.json +26 -0
  251. package/tests/fixtures/properties.json +20 -0
  252. package/tests/fixtures/some_types.json +32 -0
  253. package/tests/fixtures/validation.json +1347 -0
  254. package/tests/pages/_demo.html +475 -0
  255. package/tests/pages/advanced.html +1 -1
  256. package/tests/pages/anyof-2.html +91 -0
  257. package/tests/pages/anyof.html +82 -0
  258. package/tests/pages/array-anyof.html +145 -0
  259. package/tests/pages/array-checkboxes-infotext.html +55 -0
  260. package/tests/pages/array-checkboxes.html +5 -2
  261. package/tests/pages/array-choices.html +48 -0
  262. package/tests/pages/array-events-table.html +70 -0
  263. package/tests/pages/array-events.html +75 -0
  264. package/tests/pages/array-header-template.html +60 -0
  265. package/tests/pages/array-integers.html +5 -2
  266. package/tests/pages/array-multiselects.html +5 -2
  267. package/tests/pages/array-nested-arrays.html +5 -2
  268. package/tests/pages/array-numbers.html +5 -2
  269. package/tests/pages/array-objects.html +5 -2
  270. package/tests/pages/array-ratings.html +5 -2
  271. package/tests/pages/array-selectize-create.html +63 -0
  272. package/tests/pages/array-selectize.html +54 -0
  273. package/tests/pages/array-selects.html +5 -2
  274. package/tests/pages/array-strings.html +5 -2
  275. package/tests/pages/array-table-responsive.html +66 -0
  276. package/tests/pages/array-unique-items-sort.html +81 -0
  277. package/tests/pages/array.html +5 -2
  278. package/tests/pages/assets/autocomplete.css +1 -0
  279. package/tests/pages/assets/autocomplete.min.js +1 -0
  280. package/tests/pages/assets/pages.css +130 -0
  281. package/tests/pages/autocomplete.html +72 -0
  282. package/tests/pages/button-callbacks.html +79 -0
  283. package/tests/pages/button-icons.html +39 -0
  284. package/tests/pages/button_state_mode_1.html +35 -0
  285. package/tests/pages/button_state_mode_2.html +36 -0
  286. package/tests/pages/checkbox-labels.html +116 -0
  287. package/tests/pages/colorpicker-no-3rd-party.html +45 -0
  288. package/tests/pages/colorpicker-use-vanilla-picker.html +52 -0
  289. package/tests/pages/container-attributes.html +51 -0
  290. package/tests/pages/contains.html +39 -0
  291. package/tests/pages/core.html +14 -7
  292. package/tests/pages/datetime.html +78 -0
  293. package/tests/pages/dependentRequired.html +72 -0
  294. package/tests/pages/dependentSchemas.html +53 -0
  295. package/tests/pages/disable-button-in-object-editors.html +57 -0
  296. package/tests/pages/editor-show-validation-errors.html +54 -0
  297. package/tests/pages/enforce-const.html +168 -0
  298. package/tests/pages/error-messages.html +48 -0
  299. package/tests/pages/form-name.html +111 -0
  300. package/tests/pages/grid-strict.html +308 -0
  301. package/tests/pages/grid.html +281 -0
  302. package/tests/pages/if-else.html +58 -0
  303. package/tests/pages/if-then-else-allOf.html +118 -0
  304. package/tests/pages/if-then-else-disable-fields.html +70 -0
  305. package/tests/pages/if-then-else.html +65 -0
  306. package/tests/pages/if-then.html +58 -0
  307. package/tests/pages/inheritance.html +80 -0
  308. package/tests/pages/integer.html +17 -10
  309. package/tests/pages/issues/_template.html +50 -0
  310. package/tests/pages/issues/issue-gh-1133.html +64 -0
  311. package/tests/pages/issues/issue-gh-1158-2.html +189 -0
  312. package/tests/pages/issues/issue-gh-1158.html +50 -0
  313. package/tests/pages/issues/issue-gh-1164.html +71 -0
  314. package/tests/pages/issues/issue-gh-1165.html +63 -0
  315. package/tests/pages/issues/issue-gh-1165.json +8 -0
  316. package/tests/pages/issues/issue-gh-1171.html +39 -0
  317. package/tests/pages/issues/issue-gh-1211.html +1022 -0
  318. package/tests/pages/issues/issue-gh-1233-failing.html +46 -0
  319. package/tests/pages/issues/issue-gh-1233-passing.html +49 -0
  320. package/tests/pages/issues/issue-gh-1257.html +77 -0
  321. package/tests/pages/issues/issue-gh-1272.html +167 -0
  322. package/tests/pages/issues/issue-gh-1330.html +52 -0
  323. package/tests/pages/issues/issue-gh-1338.html +74 -0
  324. package/tests/pages/issues/issue-gh-1347.html +142 -0
  325. package/tests/pages/issues/issue-gh-1364.html +64 -0
  326. package/tests/pages/issues/issue-gh-1367.html +49 -0
  327. package/tests/pages/issues/issue-gh-1383.html +31 -0
  328. package/tests/pages/issues/issue-gh-1383.json +14 -0
  329. package/tests/pages/issues/issue-gh-1384.html +31 -0
  330. package/tests/pages/issues/issue-gh-1384.json +36 -0
  331. package/tests/pages/issues/issue-gh-1410.html +57 -0
  332. package/tests/pages/issues/issue-gh-1422.html +68 -0
  333. package/tests/pages/issues/issue-gh-1431.html +49 -0
  334. package/tests/pages/issues/issue-gh-1439.html +69 -0
  335. package/tests/pages/issues/issue-gh-1452.html +98 -0
  336. package/tests/pages/issues/issue-gh-1453.html +45 -0
  337. package/tests/pages/issues/issue-gh-1461.html +55 -0
  338. package/tests/pages/issues/issue-gh-1463.html +41 -0
  339. package/tests/pages/issues/issue-gh-1466.html +63 -0
  340. package/tests/pages/issues/issue-gh-1471.html +49 -0
  341. package/tests/pages/issues/issue-gh-1485.html +59 -0
  342. package/tests/pages/issues/issue-gh-1491.html +59 -0
  343. package/tests/pages/issues/issue-gh-1525.html +62 -0
  344. package/tests/pages/issues/issue-gh-1536.html +55 -0
  345. package/tests/pages/issues/issue-gh-1538.html +56 -0
  346. package/tests/pages/issues/issue-gh-1541.html +51 -0
  347. package/tests/pages/issues/issue-gh-1541.json +9 -0
  348. package/tests/pages/issues/issue-gh-1559.html +68 -0
  349. package/tests/pages/issues/issue-gh-1562.html +170 -0
  350. package/tests/pages/issues/issue-gh-1586.html +48 -0
  351. package/tests/pages/issues/issue-gh-1636.html +52 -0
  352. package/tests/pages/issues/issue-gh-795.html +58 -0
  353. package/tests/pages/issues/issue-gh-810.html +149 -0
  354. package/tests/pages/issues/issue-gh-812.html +113 -0
  355. package/tests/pages/issues/issue-gh-823-meta-schema.html +35 -0
  356. package/tests/pages/issues/issue-gh-848.html +81 -0
  357. package/tests/pages/keep_only_existing_values.html +81 -0
  358. package/tests/pages/load-events.html +61 -0
  359. package/tests/pages/maxContains.html +40 -0
  360. package/tests/pages/meta-schema.html +793 -0
  361. package/tests/pages/meta_schema.json +740 -0
  362. package/tests/pages/minContains.html +40 -0
  363. package/tests/pages/number.html +53 -6
  364. package/tests/pages/object-case-sensitive-property-search-false.html +42 -0
  365. package/tests/pages/object-case-sensitive-property-search-true.html +42 -0
  366. package/tests/pages/object-no-additional-properties.html +68 -0
  367. package/tests/pages/object-no-duplicated-id.html +70 -0
  368. package/tests/pages/object-required-properties.html +261 -0
  369. package/tests/pages/object-show-opt-in.html +111 -0
  370. package/tests/pages/object-with-dependencies-array.html +58 -0
  371. package/tests/pages/object-with-dependencies.html +62 -0
  372. package/tests/pages/object.html +5 -3
  373. package/tests/pages/oneof-2.html +91 -0
  374. package/tests/pages/oneof.html +105 -0
  375. package/tests/pages/opt-in-widget.html +134 -0
  376. package/tests/pages/option-dependencies.html +107 -0
  377. package/tests/pages/option-no_default_values.html +60 -0
  378. package/tests/pages/per-editor-options.html +44 -0
  379. package/tests/pages/placeholder-options.html +57 -0
  380. package/tests/pages/programmatic-changes.html +121 -0
  381. package/tests/pages/prompt-paste-max-length-reached.html +51 -0
  382. package/tests/pages/purify.html +66 -0
  383. package/tests/pages/range.html +62 -0
  384. package/tests/pages/read-only.html +60 -8
  385. package/tests/pages/ready.html +44 -0
  386. package/tests/pages/references.html +168 -0
  387. package/tests/pages/remove-false-properties.html +85 -0
  388. package/tests/pages/select-values.html +91 -0
  389. package/tests/pages/select.html +4 -3
  390. package/tests/pages/show-validation-errors.html +73 -0
  391. package/tests/pages/starrating.html +86 -0
  392. package/tests/pages/stepper-manual.html +59 -0
  393. package/tests/pages/stepper.html +61 -0
  394. package/tests/pages/string-ace-editor.html +7 -3
  395. package/tests/pages/string-cleave.html +48 -0
  396. package/tests/pages/string-custom-attributes.html +9 -6
  397. package/tests/pages/string-formats.html +54 -0
  398. package/tests/pages/string-formats2.html +59 -0
  399. package/tests/pages/{array-move-events.html → string-jodit-editor.html} +21 -29
  400. package/tests/pages/string-sceditor.html +9 -7
  401. package/tests/pages/string-simplemde-editor.html +83 -0
  402. package/tests/pages/switcher-option.html +69 -0
  403. package/tests/pages/table.html +4 -2
  404. package/tests/pages/tabs.html +1 -1
  405. package/tests/pages/themes.html +517 -0
  406. package/tests/pages/title-hidden.html +75 -0
  407. package/tests/pages/translate-property.html +248 -0
  408. package/tests/pages/urn.html +98 -0
  409. package/tests/pages/use-name-attributes.html +207 -0
  410. package/tests/pages/uuid.html +113 -0
  411. package/tests/pages/validation-messages.json +705 -0
  412. package/tests/pages/validation.html +20 -730
  413. package/tests/unit/.eslintrc +8 -0
  414. package/tests/unit/core.spec.js +320 -0
  415. package/tests/unit/defaults.spec.js +40 -0
  416. package/tests/unit/editor.spec.js +172 -0
  417. package/tests/unit/editors/array.spec.js +87 -0
  418. package/tests/unit/editors/object.spec.js +81 -0
  419. package/tests/unit/editors/table.spec.js +93 -0
  420. package/tests/unit/readme.md +35 -0
  421. package/tests/unit/schemaloader.spec.js +576 -0
  422. package/tests/unit/validator.spec.js +104 -0
  423. package/tests/unit/validators/ip-validator.spec.js +62 -0
  424. package/Gruntfile.js +0 -226
  425. package/dist/css/jsoneditor.min.css +0 -1
  426. package/dist/jsoneditor.js.map +0 -1
  427. package/dist/jsoneditor.min.js +0 -19
  428. package/dist/jsoneditor.min.js.map +0 -1
  429. package/docs/demo.html +0 -646
  430. package/src/class.js +0 -68
  431. package/src/editors/rating.js +0 -152
  432. package/src/ie9.js +0 -51
  433. package/src/intro.js +0 -23
  434. package/src/jquery.js +0 -64
  435. package/src/outro.js +0 -2
  436. package/src/themes/jsoneditor.barebones-theme.js +0 -60
@@ -1,1181 +1,1383 @@
1
- JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({
2
- getDefault: function() {
3
- return $extend({},this.schema["default"] || {});
4
- },
5
- getChildEditors: function() {
6
- return this.editors;
7
- },
8
- register: function() {
9
- this._super();
10
- if(this.editors) {
11
- for(var i in this.editors) {
12
- if(!this.editors.hasOwnProperty(i)) continue;
13
- this.editors[i].register();
14
- }
1
+ import { AbstractEditor } from '../editor.js'
2
+ import { extend, hasOwnProperty, trigger } from '../utilities.js'
3
+ import rules from './object.css.js'
4
+
5
+ export class ObjectEditor extends AbstractEditor {
6
+ constructor (options, defaults, depth) {
7
+ super(options, defaults)
8
+ this.currentDepth = depth
9
+ }
10
+
11
+ getChildEditors () {
12
+ return this.editors
13
+ }
14
+
15
+ register () {
16
+ super.register()
17
+ if (this.editors) {
18
+ Object.values(this.editors).forEach(e => e.register())
19
+ }
20
+ }
21
+
22
+ unregister () {
23
+ super.unregister()
24
+ if (this.editors) {
25
+ Object.values(this.editors).forEach(e => e.unregister())
15
26
  }
16
- },
17
- unregister: function() {
18
- this._super();
19
- if(this.editors) {
20
- for(var i in this.editors) {
21
- if(!this.editors.hasOwnProperty(i)) continue;
22
- this.editors[i].unregister();
27
+ }
28
+
29
+ getNumColumns () {
30
+ return Math.max(Math.min(12, this.maxwidth), 3)
31
+ }
32
+
33
+ enable () {
34
+ if (!this.always_disabled) {
35
+ if (this.editjson_control) this.editjson_control.disabled = false
36
+ if (this.addproperty_button) this.addproperty_button.disabled = false
37
+
38
+ super.enable()
39
+ if (this.editors) {
40
+ Object.values(this.editors).forEach(e => {
41
+ if (e.isActive() || e.isUiOnly) {
42
+ e.enable()
43
+ }
44
+ if (e.optInCheckbox) {
45
+ e.optInCheckbox.disabled = false
46
+ }
47
+ })
23
48
  }
24
49
  }
25
- },
26
- getNumColumns: function() {
27
- return Math.max(Math.min(12,this.maxwidth),3);
28
- },
29
- enable: function() {
30
- if(!this.always_disabled) {
31
- if(this.editjson_button) this.editjson_button.disabled = false;
32
- if(this.addproperty_button) this.addproperty_button.disabled = false;
33
-
34
- this._super();
35
- if(this.editors) {
36
- for(var i in this.editors) {
37
- if(!this.editors.hasOwnProperty(i)) continue;
38
- this.editors[i].enable();
50
+ }
51
+
52
+ disable (alwaysDisabled) {
53
+ if (alwaysDisabled) this.always_disabled = true
54
+ if (this.editjson_control) this.editjson_control.disabled = true
55
+ if (this.addproperty_button) this.addproperty_button.disabled = true
56
+ this.hideEditJSON()
57
+
58
+ super.disable()
59
+ if (this.editors) {
60
+ Object.values(this.editors).forEach(e => {
61
+ if (e.isActive() || e.isUiOnly) {
62
+ e.disable(alwaysDisabled)
39
63
  }
40
- }
64
+ e.optInCheckbox.disabled = true
65
+ })
41
66
  }
42
- },
43
- disable: function(always_disabled) {
44
- if(always_disabled) this.always_disabled = true;
45
- if(this.editjson_button) this.editjson_button.disabled = true;
46
- if(this.addproperty_button) this.addproperty_button.disabled = true;
47
- this.hideEditJSON();
48
-
49
- this._super();
50
- if(this.editors) {
51
- for(var i in this.editors) {
52
- if(!this.editors.hasOwnProperty(i)) continue;
53
- this.editors[i].disable(always_disabled);
67
+ }
68
+
69
+ layoutEditors () {
70
+ let i; let j
71
+
72
+ if (!this.row_container) return
73
+
74
+ /* Sort editors by propertyOrder */
75
+ this.property_order = Object.keys(this.editors)
76
+ this.property_order = this.property_order.sort((a, b) => {
77
+ let ordera = this.editors[a].schema.propertyOrder
78
+ let orderb = this.editors[b].schema.propertyOrder
79
+ if (typeof ordera !== 'number') ordera = 1000
80
+ if (typeof orderb !== 'number') orderb = 1000
81
+
82
+ return ordera - orderb
83
+ })
84
+
85
+ let container
86
+ const isCategoriesFormat = (this.format === 'categories')
87
+ const rows = []
88
+ let key = null
89
+ let editor = null
90
+ let row
91
+
92
+ if (this.format === 'grid-strict') {
93
+ let rowIndex = 0
94
+ row = []
95
+
96
+ this.property_order.forEach(key => {
97
+ const editor = this.editors[key]
98
+ if (editor.property_removed) {
99
+ return
100
+ }
101
+ const width = editor.options.hidden ? 0 : (editor.options.grid_columns || editor.getNumColumns())
102
+ const offset = editor.options.hidden ? 0 : (editor.options.grid_offset || 0)
103
+ const gridBreak = editor.options.hidden ? false : (editor.options.grid_break || false)
104
+ const height = editor.options.hidden ? 0 : editor.container.offsetHeight
105
+
106
+ const column = {
107
+ key,
108
+ width,
109
+ offset,
110
+ height
111
+ }
112
+
113
+ row.push(column)
114
+
115
+ rows[rowIndex] = row
116
+
117
+ if (gridBreak) {
118
+ rowIndex++
119
+ row = []
120
+ }
121
+ })
122
+
123
+ /* layout hasn't changed */
124
+ if (this.layout === JSON.stringify(rows)) return false
125
+ this.layout = JSON.stringify(rows)
126
+
127
+ /* Layout the form */
128
+ container = document.createElement('div')
129
+ for (i = 0; i < rows.length; i++) {
130
+ row = this.theme.getGridRow()
131
+ container.appendChild(row)
132
+ for (j = 0; j < rows[i].length; j++) {
133
+ key = rows[i][j].key
134
+ editor = this.editors[key]
135
+ if (editor.options.hidden) {
136
+ editor.container.style.display = 'none'
137
+ } else {
138
+ this.theme.setGridColumnSize(editor.container, rows[i][j].width, rows[i][j].offset)
139
+ }
140
+ row.appendChild(editor.container)
141
+ }
54
142
  }
55
- }
56
- },
57
- layoutEditors: function() {
58
- var self = this, i, j;
59
-
60
- if(!this.row_container) return;
61
-
62
- // Sort editors by propertyOrder
63
- this.property_order = Object.keys(this.editors);
64
- this.property_order = this.property_order.sort(function(a,b) {
65
- var ordera = self.editors[a].schema.propertyOrder;
66
- var orderb = self.editors[b].schema.propertyOrder;
67
- if(typeof ordera !== "number") ordera = 1000;
68
- if(typeof orderb !== "number") orderb = 1000;
69
-
70
- return ordera - orderb;
71
- });
72
-
73
- var container;
74
- var isCategoriesFormat = (this.format === 'categories');
75
-
76
- if(this.format === 'grid') {
77
- var rows = [];
78
- $each(this.property_order, function(j,key) {
79
- var editor = self.editors[key];
80
- if(editor.property_removed) return;
81
- var found = false;
82
- var width = editor.options.hidden? 0 : (editor.options.grid_columns || editor.getNumColumns());
83
- var height = editor.options.hidden? 0 : editor.container.offsetHeight;
84
- // See if the editor will fit in any of the existing rows first
85
- for(var i=0; i<rows.length; i++) {
86
- // If the editor will fit in the row horizontally
87
- if(rows[i].width + width <= 12) {
88
- // If the editor is close to the other elements in height
89
- // i.e. Don't put a really tall editor in an otherwise short row or vice versa
90
- if(!height || (rows[i].minh*0.5 < height && rows[i].maxh*2 > height)) {
91
- found = i;
143
+ } else if (this.format === 'grid') {
144
+ this.property_order.forEach(key => {
145
+ const editor = this.editors[key]
146
+ if (editor.property_removed) return
147
+ let found = false
148
+ const width = editor.options.hidden ? 0 : (editor.options.grid_columns || editor.getNumColumns())
149
+ const height = editor.options.hidden ? 0 : editor.container.offsetHeight
150
+ /* See if the editor will fit in any of the existing rows first */
151
+ for (let i = 0; i < rows.length; i++) {
152
+ /* If the editor will fit in the row horizontally */
153
+ if (rows[i].width + width <= 12) {
154
+ /* If the editor is close to the other elements in height */
155
+ /* i.e. Don't put a really tall editor in an otherwise short row or vice versa */
156
+ if (!height || (rows[i].minh * 0.5 < height && rows[i].maxh * 2 > height)) {
157
+ found = i
92
158
  }
93
159
  }
94
160
  }
95
161
 
96
- // If there isn't a spot in any of the existing rows, start a new row
97
- if(found === false) {
162
+ /* If there isn't a spot in any of the existing rows, start a new row */
163
+ if (found === false) {
98
164
  rows.push({
99
165
  width: 0,
100
166
  minh: 999999,
101
167
  maxh: 0,
102
168
  editors: []
103
- });
104
- found = rows.length-1;
169
+ })
170
+ found = rows.length - 1
105
171
  }
106
172
 
107
173
  rows[found].editors.push({
108
- key: key,
109
- //editor: editor,
110
- width: width,
111
- height: height
112
- });
113
- rows[found].width += width;
114
- rows[found].minh = Math.min(rows[found].minh,height);
115
- rows[found].maxh = Math.max(rows[found].maxh,height);
116
- });
117
-
118
- // Make almost full rows width 12
119
- // Do this by increasing all editors' sizes proprotionately
120
- // Any left over space goes to the biggest editor
121
- // Don't touch rows with a width of 6 or less
122
- for(i=0; i<rows.length; i++) {
123
- if(rows[i].width < 12) {
124
- var biggest = false;
125
- var new_width = 0;
126
- for(j=0; j<rows[i].editors.length; j++) {
127
- if(biggest === false) biggest = j;
128
- else if(rows[i].editors[j].width > rows[i].editors[biggest].width) biggest = j;
129
- rows[i].editors[j].width *= 12/rows[i].width;
130
- rows[i].editors[j].width = Math.floor(rows[i].editors[j].width);
131
- new_width += rows[i].editors[j].width;
174
+ key,
175
+ /* editor: editor, */
176
+ width,
177
+ height
178
+ })
179
+ rows[found].width += width
180
+ rows[found].minh = Math.min(rows[found].minh, height)
181
+ rows[found].maxh = Math.max(rows[found].maxh, height)
182
+ })
183
+
184
+ /* Make almost full rows width 12 */
185
+ /* Do this by increasing all editors' sizes proprotionately */
186
+ /* Any left over space goes to the biggest editor */
187
+ /* Don't touch rows with a width of 6 or less */
188
+ for (i = 0; i < rows.length; i++) {
189
+ if (rows[i].width < 12) {
190
+ let biggest = false
191
+ let newWidth = 0
192
+ for (j = 0; j < rows[i].editors.length; j++) {
193
+ if (biggest === false) biggest = j
194
+ else if (rows[i].editors[j].width > rows[i].editors[biggest].width) biggest = j
195
+ rows[i].editors[j].width *= 12 / rows[i].width
196
+ rows[i].editors[j].width = Math.floor(rows[i].editors[j].width)
197
+ newWidth += rows[i].editors[j].width
132
198
  }
133
- if(new_width < 12) rows[i].editors[biggest].width += 12-new_width;
134
- rows[i].width = 12;
199
+ if (newWidth < 12) rows[i].editors[biggest].width += 12 - newWidth
200
+ rows[i].width = 12
135
201
  }
136
202
  }
137
203
 
138
- // layout hasn't changed
139
- if(this.layout === JSON.stringify(rows)) return false;
140
- this.layout = JSON.stringify(rows);
141
-
142
- // Layout the form
143
- container = document.createElement('div');
144
- for(i=0; i<rows.length; i++) {
145
- var row = this.theme.getGridRow();
146
- container.appendChild(row);
147
- for(j=0; j<rows[i].editors.length; j++) {
148
- var key = rows[i].editors[j].key;
149
- var editor = this.editors[key];
150
-
151
- if(editor.options.hidden) editor.container.style.display = 'none';
152
- else this.theme.setGridColumnSize(editor.container,rows[i].editors[j].width);
153
- row.appendChild(editor.container);
204
+ /* layout hasn't changed */
205
+ if (this.layout === JSON.stringify(rows)) return false
206
+ this.layout = JSON.stringify(rows)
207
+
208
+ /* Layout the form */
209
+ container = document.createElement('div')
210
+ for (i = 0; i < rows.length; i++) {
211
+ row = this.theme.getGridRow()
212
+ container.appendChild(row)
213
+ for (j = 0; j < rows[i].editors.length; j++) {
214
+ key = rows[i].editors[j].key
215
+ editor = this.editors[key]
216
+
217
+ if (editor.options.hidden) editor.container.style.display = 'none'
218
+ else this.theme.setGridColumnSize(editor.container, rows[i].editors[j].width)
219
+ row.appendChild(editor.container)
154
220
  }
155
221
  }
156
- }
157
- // Normal layout
158
- else {
159
- container = document.createElement('div');
160
-
161
- if(isCategoriesFormat) {
162
- //A container for properties not object nor arrays
163
- var containerSimple = document.createElement('div');
164
- //This will be the place to (re)build tabs and panes
165
- //tabs_holder has 2 childs, [0]: ul.nav.nav-tabs and [1]: div.tab-content
166
- var newTabs_holder = this.theme.getTopTabHolder(this.schema.title);
167
- //child [1] of previous, stores panes
168
- var newTabPanesContainer = this.theme.getTopTabContentHolder(newTabs_holder);
169
-
170
- $each(this.property_order, function(i,key){
171
- var editor = self.editors[key];
172
- if(editor.property_removed) return;
173
- var aPane = self.theme.getTabContent();
174
- var isObjOrArray = editor.schema && (editor.schema.type === "object" || editor.schema.type === "array");
175
- //mark the pane
176
- aPane.isObjOrArray = isObjOrArray;
177
- var gridRow = self.theme.getGridRow();
178
-
179
- //this happens with added properties, they don't have a tab
180
- if(!editor.tab){
181
- //Pass the pane which holds the editor
182
- if(typeof self.basicPane === 'undefined'){
183
- //There is no basicPane yet, so aPane will be it
184
- self.addRow(editor,newTabs_holder, aPane);
185
- }
186
- else {
187
- self.addRow(editor,newTabs_holder, self.basicPane);
222
+ /* Normal layout */
223
+ } else {
224
+ container = document.createElement('div')
225
+
226
+ if (isCategoriesFormat) {
227
+ /* A container for properties not object nor arrays */
228
+ const containerSimple = document.createElement('div')
229
+ /* This will be the place to (re)build tabs and panes */
230
+ /* tabs_holder has 2 childs, [0]: ul.nav.nav-tabs and [1]: div.tab-content */
231
+ const newTabsHolder = this.theme.getTopTabHolder(this.translateProperty(this.schema.title))
232
+ /* child [1] of previous, stores panes */
233
+ const newTabPanesContainer = this.theme.getTopTabContentHolder(newTabsHolder)
234
+
235
+ this.property_order.forEach(key => {
236
+ const editor = this.editors[key]
237
+ if (editor.property_removed) return
238
+ const aPane = this.theme.getTabContent()
239
+ const isObjOrArray = editor.schema && (editor.schema.type === 'object' || editor.schema.type === 'array')
240
+ /* mark the pane */
241
+ aPane.isObjOrArray = isObjOrArray
242
+ const gridRow = this.theme.getGridRow()
243
+
244
+ /* this happens with added properties, they don't have a tab */
245
+ if (!editor.tab) {
246
+ /* Pass the pane which holds the editor */
247
+ if (typeof this.basicPane === 'undefined') {
248
+ /* There is no basicPane yet, so aPane will be it */
249
+ this.addRow(editor, newTabsHolder, aPane)
250
+ } else {
251
+ this.addRow(editor, newTabsHolder, this.basicPane)
188
252
  }
189
253
  }
190
254
 
191
- aPane.id = self.getValidId(editor.tab_text.textContent);
192
-
193
- //For simple properties, add them on the same panel (Basic)
194
- if(!isObjOrArray){
195
- containerSimple.appendChild(gridRow);
196
- //There are already some panes
197
- if(newTabPanesContainer.childElementCount > 0){
198
- //If first pane is object or array, insert before a simple pane
199
- if(newTabPanesContainer.firstChild.isObjOrArray){
200
- //Append pane for simple properties
201
- aPane.appendChild(containerSimple);
202
- newTabPanesContainer.insertBefore(aPane,newTabPanesContainer.firstChild);
203
- //Add "Basic" tab
204
- self.theme.insertBasicTopTab(editor.tab,newTabs_holder);
205
- //newTabs_holder.firstChild.insertBefore(editor.tab,newTabs_holder.firstChild.firstChild);
206
- //Update the basicPane
207
- editor.basicPane = aPane;
255
+ aPane.id = this.getValidId(editor.tab_text.textContent)
256
+
257
+ /* For simple properties, add them on the same panel (Basic) */
258
+ if (!isObjOrArray) {
259
+ containerSimple.appendChild(gridRow)
260
+ /* There are already some panes */
261
+ if (newTabPanesContainer.childElementCount > 0) {
262
+ /* If first pane is object or array, insert before a simple pane */
263
+ if (newTabPanesContainer.firstChild.isObjOrArray) {
264
+ /* Append pane for simple properties */
265
+ aPane.appendChild(containerSimple)
266
+ newTabPanesContainer.insertBefore(aPane, newTabPanesContainer.firstChild)
267
+ /* Add "Basic" tab */
268
+ this.theme.insertBasicTopTab(editor.tab, newTabsHolder)
269
+ /* newTabs_holder.firstChild.insertBefore(editor.tab,newTabs_holder.firstChild.firstChild); */
270
+ /* Update the basicPane */
271
+ editor.basicPane = aPane
272
+ } else {
273
+ /* We already have a first "Basic" pane, just add the new property to it, so */
274
+ /* do nothing; */
208
275
  }
209
- else {
210
- //We already have a first "Basic" pane, just add the new property to it, so
211
- //do nothing;
212
- }
213
- }
214
- //There is no pane, so add the first (simple) pane
215
- else {
216
- //Append pane for simple properties
217
- aPane.appendChild(containerSimple);
218
- newTabPanesContainer.appendChild(aPane);
219
- //Add "Basic" tab
220
- //newTabs_holder.firstChild.appendChild(editor.tab);
221
- self.theme.addTopTab(newTabs_holder,editor.tab);
222
- //Update the basicPane
223
- editor.basicPane = aPane;
276
+ /* There is no pane, so add the first (simple) pane */
277
+ } else {
278
+ /* Append pane for simple properties */
279
+ aPane.appendChild(containerSimple)
280
+ newTabPanesContainer.appendChild(aPane)
281
+ /* Add "Basic" tab */
282
+ /* newTabs_holder.firstChild.appendChild(editor.tab); */
283
+ this.theme.addTopTab(newTabsHolder, editor.tab)
284
+ /* Update the basicPane */
285
+ editor.basicPane = aPane
224
286
  }
225
- }
226
- //Objects and arrays earn it's own panes
227
- else {
228
- aPane.appendChild(gridRow);
229
- newTabPanesContainer.appendChild(aPane);
230
- //newTabs_holder.firstChild.appendChild(editor.tab);
231
- self.theme.addTopTab(newTabs_holder,editor.tab);
287
+ /* Objects and arrays earn their own panes */
288
+ } else {
289
+ aPane.appendChild(gridRow)
290
+ newTabPanesContainer.appendChild(aPane)
291
+ /* newTabs_holder.firstChild.appendChild(editor.tab); */
292
+ this.theme.addTopTab(newTabsHolder, editor.tab)
232
293
  }
233
294
 
234
- if(editor.options.hidden) editor.container.style.display = 'none';
235
- else self.theme.setGridColumnSize(editor.container,12);
236
- //Now, add the property editor to the row
237
- gridRow.appendChild(editor.container);
238
- //Update the rowPane (same as self.rows[x].rowPane)
239
- editor.rowPane = aPane;
295
+ if (editor.options.hidden) editor.container.style.display = 'none'
296
+ else this.theme.setGridColumnSize(editor.container, 12)
297
+ /* Now, add the property editor to the row */
298
+ gridRow.appendChild(editor.container)
299
+ /* Update the rowPane (same as this.rows[x].rowPane) */
300
+ editor.rowPane = aPane
301
+ })
240
302
 
241
- });
242
-
243
- //Erase old panes
303
+ /* Erase old panes */
244
304
  while (this.tabPanesContainer.firstChild) {
245
- this.tabPanesContainer.removeChild(this.tabPanesContainer.firstChild);
305
+ this.tabPanesContainer.removeChild(this.tabPanesContainer.firstChild)
246
306
  }
247
307
 
248
- //Erase old tabs and set the new ones
249
- var parentTabs_holder = this.tabs_holder.parentNode;
250
- parentTabs_holder.removeChild(parentTabs_holder.firstChild);
251
- parentTabs_holder.appendChild(newTabs_holder);
308
+ /* Erase old tabs and set the new ones */
309
+ const parentTabsHolder = this.tabs_holder.parentNode
310
+ parentTabsHolder.removeChild(parentTabsHolder.firstChild)
311
+ parentTabsHolder.appendChild(newTabsHolder)
252
312
 
253
- this.tabPanesContainer = newTabPanesContainer;
254
- this.tabs_holder = newTabs_holder;
313
+ this.tabPanesContainer = newTabPanesContainer
314
+ this.tabs_holder = newTabsHolder
255
315
 
256
- //Activate the first tab
257
- var firstTab = this.theme.getFirstTab(this.tabs_holder);
258
- if(firstTab){
259
- $trigger(firstTab,'click');
316
+ /* Activate the first tab */
317
+ const firstTab = this.theme.getFirstTab(this.tabs_holder)
318
+ if (firstTab) {
319
+ trigger(firstTab, 'click')
260
320
  }
261
- return;
262
- }
263
- //Normal layout
264
- else {
265
- $each(this.property_order, function(i,key) {
266
- var editor = self.editors[key];
267
- if(editor.property_removed) return;
268
- var row = self.theme.getGridRow();
269
- container.appendChild(row);
270
-
271
- if(editor.options.hidden) editor.container.style.display = 'none';
272
- else self.theme.setGridColumnSize(editor.container,12);
273
- row.appendChild(editor.container);
274
- });
321
+ return
322
+ /* Normal layout */
275
323
  }
324
+ this.property_order.forEach(key => {
325
+ const editor = this.editors[key]
326
+ if (editor.property_removed) return
327
+ row = this.theme.getGridRow()
328
+ container.appendChild(row)
329
+
330
+ if (editor.options.hidden) editor.container.style.display = 'none'
331
+ else this.theme.setGridColumnSize(editor.container, 12)
332
+ row.appendChild(editor.container)
333
+ })
276
334
  }
277
- //for grid and normal layout
335
+ /* for grid and normal layout */
278
336
  while (this.row_container.firstChild) {
279
- this.row_container.removeChild(this.row_container.firstChild);
337
+ this.row_container.removeChild(this.row_container.firstChild)
280
338
  }
281
- this.row_container.appendChild(container);
282
- },
283
- getPropertySchema: function(key) {
284
- // Schema declared directly in properties
285
- var schema = this.schema.properties[key] || {};
286
- schema = $extend({},schema);
287
- var matched = this.schema.properties[key]? true : false;
288
-
289
- // Any matching patternProperties should be merged in
290
- if(this.schema.patternProperties) {
291
- for(var i in this.schema.patternProperties) {
292
- if(!this.schema.patternProperties.hasOwnProperty(i)) continue;
293
- var regex = new RegExp(i);
294
- if(regex.test(key)) {
295
- schema.allOf = schema.allOf || [];
296
- schema.allOf.push(this.schema.patternProperties[i]);
297
- matched = true;
339
+ this.row_container.appendChild(container)
340
+ }
341
+
342
+ getPropertySchema (key) {
343
+ /* Schema declared directly in properties */
344
+ let schema = this.schema.properties[key] || {}
345
+ schema = extend({}, schema)
346
+ let matched = !!this.schema.properties[key]
347
+
348
+ /* Any matching patternProperties should be merged in */
349
+ if (this.schema.patternProperties) {
350
+ Object.keys(this.schema.patternProperties).forEach(i => {
351
+ const regex = new RegExp(i)
352
+ if (regex.test(key)) {
353
+ schema.allOf = schema.allOf || []
354
+ schema.allOf.push(this.schema.patternProperties[i])
355
+ matched = true
298
356
  }
299
- }
357
+ })
300
358
  }
301
359
 
302
- // Hasn't matched other rules, use additionalProperties schema
303
- if(!matched && this.schema.additionalProperties && typeof this.schema.additionalProperties === "object") {
304
- schema = $extend({},this.schema.additionalProperties);
360
+ /* Hasn't matched other rules, use additionalProperties schema */
361
+ if (!matched && this.schema.additionalProperties && typeof this.schema.additionalProperties === 'object') {
362
+ schema = extend({}, this.schema.additionalProperties)
305
363
  }
306
364
 
307
- return schema;
308
- },
309
- preBuild: function() {
310
- this._super();
365
+ return schema
366
+ }
367
+
368
+ preBuild () {
369
+ super.preBuild()
311
370
 
312
- this.editors = {};
313
- this.cached_editors = {};
314
- var self = this;
371
+ this.editors = {}
372
+ this.cached_editors = {}
315
373
 
316
- this.format = this.options.layout || this.options.object_layout || this.schema.format || this.jsoneditor.options.object_layout || 'normal';
374
+ this.format = this.options.layout || this.options.object_layout || this.schema.format || this.jsoneditor.options.object_layout || 'normal'
317
375
 
318
- this.schema.properties = this.schema.properties || {};
376
+ this.schema.properties = this.schema.properties || {}
319
377
 
320
- this.minwidth = 0;
321
- this.maxwidth = 0;
378
+ this.minwidth = 0
379
+ this.maxwidth = 0
322
380
 
323
- // If the object should be rendered as a table row
324
- if(this.options.table_row) {
325
- $each(this.schema.properties, function(key,schema) {
326
- var editor = self.jsoneditor.getEditorClass(schema);
327
- self.editors[key] = self.jsoneditor.createEditor(editor,{
328
- jsoneditor: self.jsoneditor,
329
- schema: schema,
330
- path: self.path+'.'+key,
331
- parent: self,
381
+ /* If the object should be rendered as a table row */
382
+ if (this.options.table_row) {
383
+ Object.entries(this.schema.properties).forEach(([key, schema]) => {
384
+ const editor = this.jsoneditor.getEditorClass(schema)
385
+ this.editors[key] = this.jsoneditor.createEditor(editor, {
386
+ jsoneditor: this.jsoneditor,
387
+ schema,
388
+ path: `${this.path}.${key}`,
389
+ parent: this,
332
390
  compact: true,
333
391
  required: true
334
- });
335
- self.editors[key].preBuild();
336
-
337
- var width = self.editors[key].options.hidden? 0 : (self.editors[key].options.grid_columns || self.editors[key].getNumColumns());
338
-
339
- self.minwidth += width;
340
- self.maxwidth += width;
341
- });
342
- this.no_link_holder = true;
343
- }
344
- // If the object should be rendered as a table
345
- else if(this.options.table) {
346
- // TODO: table display format
347
- throw "Not supported yet";
348
- }
349
- // If the object should be rendered as a div
350
- else {
351
- if(!this.schema.defaultProperties) {
352
- if(this.jsoneditor.options.display_required_only || this.options.display_required_only) {
353
- this.schema.defaultProperties = [];
354
- $each(this.schema.properties, function(k,s) {
355
- if(self.isRequired({key: k, schema: s})) {
356
- self.schema.defaultProperties.push(k);
357
- }
358
- });
359
- }
360
- else {
361
- self.schema.defaultProperties = Object.keys(self.schema.properties);
392
+ }, this.currentDepth + 1)
393
+ this.editors[key].preBuild()
394
+
395
+ const width = this.editors[key].options.hidden ? 0 : (this.editors[key].options.grid_columns || this.editors[key].getNumColumns())
396
+
397
+ this.minwidth += width
398
+ this.maxwidth += width
399
+ })
400
+ this.no_link_holder = true
401
+ /* If the object should be rendered as a table */
402
+ } else if (this.options.table) {
403
+ /* TODO: table display format */
404
+ throw new Error('Not supported yet')
405
+ /* If the object should be rendered as a div */
406
+ } else {
407
+ if (!this.schema.defaultProperties) {
408
+ if (this.jsoneditor.options.display_required_only || this.options.display_required_only) {
409
+ this.schema.defaultProperties = Object.keys(this.schema.properties).filter(k => this.isRequiredObject({ key: k, schema: this.schema.properties[k] }))
410
+ } else {
411
+ this.schema.defaultProperties = Object.keys(this.schema.properties)
362
412
  }
363
413
  }
364
414
 
365
- // Increase the grid width to account for padding
366
- self.maxwidth += 1;
415
+ /* Increase the grid width to account for padding */
416
+ this.maxwidth += 1
367
417
 
368
- $each(this.schema.defaultProperties, function(i,key) {
369
- self.addObjectProperty(key, true);
418
+ /* Check for array (eg. meta-schema options is an object) */
419
+ if (Array.isArray(this.schema.defaultProperties)) {
420
+ this.schema.defaultProperties.forEach(key => {
421
+ this.addObjectProperty(key, true)
370
422
 
371
- if(self.editors[key]) {
372
- self.minwidth = Math.max(self.minwidth,(self.editors[key].options.grid_columns || self.editors[key].getNumColumns()));
373
- self.maxwidth += (self.editors[key].options.grid_columns || self.editors[key].getNumColumns());
374
- }
375
- });
423
+ if (this.editors[key]) {
424
+ this.minwidth = Math.max(this.minwidth, (this.editors[key].options.grid_columns || this.editors[key].getNumColumns()))
425
+ this.maxwidth += (this.editors[key].options.grid_columns || this.editors[key].getNumColumns())
426
+ }
427
+ })
428
+ }
376
429
  }
377
430
 
378
- // Sort editors by propertyOrder
379
- this.property_order = Object.keys(this.editors);
380
- this.property_order = this.property_order.sort(function(a,b) {
381
- var ordera = self.editors[a].schema.propertyOrder;
382
- var orderb = self.editors[b].schema.propertyOrder;
383
- if(typeof ordera !== "number") ordera = 1000;
384
- if(typeof orderb !== "number") orderb = 1000;
385
-
386
- return ordera - orderb;
387
- });
388
- },
389
- //"Borrow" from arrays code
390
- addTab: function(idx){
391
- var self = this;
392
- var isObjOrArray = self.rows[idx].schema && (self.rows[idx].schema.type === "object" || self.rows[idx].schema.type === "array");
393
- if(self.tabs_holder) {
394
- self.rows[idx].tab_text = document.createElement('span');
395
-
396
- if(!isObjOrArray){
397
- self.rows[idx].tab_text.textContent = (typeof self.schema.basicCategoryTitle === 'undefined') ? "Basic" : self.schema.basicCategoryTitle;
398
- } else {
399
- self.rows[idx].tab_text.textContent = self.rows[idx].getHeaderText();
400
- }
401
- self.rows[idx].tab = self.theme.getTopTab(self.rows[idx].tab_text,this.getValidId(self.rows[idx].tab_text.textContent));
402
- self.rows[idx].tab.addEventListener('click', function(e) {
403
- self.active_tab = self.rows[idx].tab;
404
- self.refreshTabs();
405
- e.preventDefault();
406
- e.stopPropagation();
407
- });
431
+ /* Sort editors by propertyOrder */
432
+ this.property_order = Object.keys(this.editors)
433
+ this.property_order = this.property_order.sort((a, b) => {
434
+ let ordera = this.editors[a].schema.propertyOrder
435
+ let orderb = this.editors[b].schema.propertyOrder
436
+ if (typeof ordera !== 'number') ordera = 1000
437
+ if (typeof orderb !== 'number') orderb = 1000
408
438
 
409
- }
439
+ return ordera - orderb
440
+ })
441
+ }
410
442
 
411
- },
412
- addRow: function(editor, tabHolder, aPane) {
413
- var self = this;
414
- var rowsLen = this.rows.length;
415
- var isObjOrArray = editor.schema.type === "object" || editor.schema.type === "array";
416
-
417
- //Add a row
418
- self.rows[rowsLen] = editor;
419
- //rowPane stores the editor corresponding pane to set the display style when refreshing Tabs
420
- self.rows[rowsLen].rowPane = aPane;
421
-
422
- if(!isObjOrArray){
423
-
424
- //This is the first simple property to be added,
425
- //add a ("Basic") tab for it and save it's row number
426
- if(typeof self.basicTab === "undefined"){
427
- self.addTab(rowsLen);
428
- //Store the index row of the first simple property added
429
- self.basicTab = rowsLen;
430
- self.basicPane = aPane;
431
- self.theme.addTopTab(tabHolder, self.rows[rowsLen].tab);
432
- }
443
+ /* "Borrow" from arrays code */
444
+ addTab (idx) {
445
+ const isObjOrArray = this.rows[idx].schema && (this.rows[idx].schema.type === 'object' || this.rows[idx].schema.type === 'array')
446
+ if (this.tabs_holder) {
447
+ this.rows[idx].tab_text = document.createElement('span')
433
448
 
434
- else {
435
- //Any other simple property gets the same tab (and the same pane) as the first one,
436
- //so, when 'click' event is fired from a row, it gets the correct ("Basic") tab
437
- self.rows[rowsLen].tab = self.rows[self.basicTab].tab;
438
- self.rows[rowsLen].tab_text = self.rows[self.basicTab].tab_text;
439
- self.rows[rowsLen].rowPane = self.rows[self.basicTab].rowPane;
449
+ if (!isObjOrArray) {
450
+ this.rows[idx].tab_text.textContent = (typeof this.schema.basicCategoryTitle === 'undefined') ? 'Basic' : this.schema.basicCategoryTitle
451
+ } else {
452
+ this.rows[idx].tab_text.textContent = this.rows[idx].getHeaderText()
440
453
  }
454
+ this.rows[idx].tab = this.theme.getTopTab(this.rows[idx].tab_text, this.getValidId(this.rows[idx].tab_text.textContent))
455
+ this.rows[idx].tab.addEventListener('click', (e) => {
456
+ this.active_tab = this.rows[idx].tab
457
+ this.refreshTabs()
458
+ e.preventDefault()
459
+ e.stopPropagation()
460
+ })
441
461
  }
442
- else {
443
- self.addTab(rowsLen);
444
- self.theme.addTopTab(tabHolder, self.rows[rowsLen].tab);
462
+ }
463
+
464
+ addRow (editor, tabHolder, aPane) {
465
+ const rowsLen = this.rows.length
466
+ const isObjOrArray = editor.schema.type === 'object' || editor.schema.type === 'array'
467
+
468
+ /* Add a row */
469
+ this.rows[rowsLen] = editor
470
+ /* rowPane stores the editor corresponding pane to set the display style when refreshing Tabs */
471
+ this.rows[rowsLen].rowPane = aPane
472
+
473
+ if (!isObjOrArray) {
474
+ /* This is the first simple property to be added, */
475
+ /* add a ("Basic") tab for it and save it's row number */
476
+ if (typeof this.basicTab === 'undefined') {
477
+ this.addTab(rowsLen)
478
+ /* Store the index row of the first simple property added */
479
+ this.basicTab = rowsLen
480
+ this.basicPane = aPane
481
+ this.theme.addTopTab(tabHolder, this.rows[rowsLen].tab)
482
+ } else {
483
+ /* Any other simple property gets the same tab (and the same pane) as the first one, */
484
+ /* so, when 'click' event is fired from a row, it gets the correct ("Basic") tab */
485
+ this.rows[rowsLen].tab = this.rows[this.basicTab].tab
486
+ this.rows[rowsLen].tab_text = this.rows[this.basicTab].tab_text
487
+ this.rows[rowsLen].rowPane = this.rows[this.basicTab].rowPane
488
+ }
489
+ } else {
490
+ this.addTab(rowsLen)
491
+ this.theme.addTopTab(tabHolder, this.rows[rowsLen].tab)
445
492
  }
446
- },
447
- //Mark the active tab and make visible the corresponding pane, hide others
448
- refreshTabs: function(refresh_headers) {
449
- var self = this;
450
- var basicTabPresent = typeof self.basicTab !== 'undefined';
451
- var basicTabRefreshed = false;
493
+ }
452
494
 
453
- $each(this.rows, function(i,row) {
454
- //If it's an orphan row (some property which has been deleted), return
455
- if(!row.tab || !row.rowPane || !row.rowPane.parentNode) return;
495
+ /* Mark the active tab and make visible the corresponding pane, hide others */
496
+ refreshTabs (refreshHeaders) {
497
+ const basicTabPresent = typeof this.basicTab !== 'undefined'
498
+ let basicTabRefreshed = false
456
499
 
457
- if(basicTabPresent && row.tab == self.rows[self.basicTab].tab && basicTabRefreshed) return;
500
+ this.rows.forEach(row => {
501
+ /* If it's an orphan row (some property which has been deleted), return */
502
+ if (!row.tab || !row.rowPane || !row.rowPane.parentNode) return
458
503
 
459
- if(refresh_headers) {
460
- row.tab_text.textContent = row.getHeaderText();
461
- }
462
- else {
463
- //All rows of simple properties point to the same tab, so refresh just once
464
- if(basicTabPresent && row.tab == self.rows[self.basicTab].tab) basicTabRefreshed = true;
504
+ if (basicTabPresent && row.tab === this.rows[this.basicTab].tab && basicTabRefreshed) return
465
505
 
466
- if(row.tab === self.active_tab) {
467
- self.theme.markTabActive(row);
468
- }
469
- else {
470
- self.theme.markTabInactive(row);
506
+ if (refreshHeaders) {
507
+ row.tab_text.textContent = row.getHeaderText()
508
+ } else {
509
+ /* All rows of simple properties point to the same tab, so refresh just once */
510
+ if (basicTabPresent && row.tab === this.rows[this.basicTab].tab) basicTabRefreshed = true
511
+
512
+ if (row.tab === this.active_tab) {
513
+ this.theme.markTabActive(row)
514
+ } else {
515
+ this.theme.markTabInactive(row)
471
516
  }
472
517
  }
473
- });
474
- },
475
- build: function() {
476
- var self = this;
477
-
478
- var isCategoriesFormat = (this.format === 'categories');
479
- this.rows=[];
480
- this.active_tab = null;
481
-
482
- // If the object should be rendered as a table row
483
- if(this.options.table_row) {
484
- this.editor_holder = this.container;
485
- $each(this.editors, function(key,editor) {
486
- var holder = self.theme.getTableCell();
487
- self.editor_holder.appendChild(holder);
488
-
489
- editor.setContainer(holder);
490
- editor.build();
491
- editor.postBuild();
492
-
493
- if(self.editors[key].options.hidden) {
494
- holder.style.display = 'none';
518
+ })
519
+ }
520
+
521
+ build () {
522
+ const isCategoriesFormat = (this.format === 'categories')
523
+ this.rows = []
524
+ this.active_tab = null
525
+
526
+ /* If the object should be rendered as a table row */
527
+ if (this.options.table_row) {
528
+ this.editor_holder = this.container
529
+ Object.entries(this.editors).forEach(([key, editor]) => {
530
+ const holder = this.theme.getTableCell()
531
+ this.editor_holder.appendChild(holder)
532
+
533
+ editor.setContainer(holder)
534
+ editor.build()
535
+ editor.postBuild()
536
+ editor.setOptInCheckbox(editor.header)
537
+
538
+ editor.setValue(editor.getDefault(), true)
539
+
540
+ if (this.editors[key].options.hidden) {
541
+ holder.style.display = 'none'
495
542
  }
496
- if(self.editors[key].options.input_width) {
497
- holder.style.width = self.editors[key].options.input_width;
543
+ if (this.editors[key].options.input_width) {
544
+ holder.style.width = this.editors[key].options.input_width
498
545
  }
499
- });
500
- }
501
- // If the object should be rendered as a table
502
- else if(this.options.table) {
503
- // TODO: table display format
504
- throw "Not supported yet";
505
- }
506
- // If the object should be rendered as a div
507
- else {
508
- this.header = document.createElement('span');
509
- this.header.textContent = this.getTitle();
510
- this.title = this.theme.getHeader(this.header);
511
- this.container.appendChild(this.title);
512
- this.container.style.position = 'relative';
513
-
514
- // Edit JSON modal
515
- this.editjson_holder = this.theme.getModal();
516
- this.editjson_textarea = this.theme.getTextareaInput();
517
- this.editjson_textarea.style.height = '170px';
518
- this.editjson_textarea.style.width = '300px';
519
- this.editjson_textarea.style.display = 'block';
520
- this.editjson_save = this.getButton('Save','save','Save');
521
- this.editjson_save.classList.add('json-editor-btntype-save');
522
- this.editjson_save.addEventListener('click',function(e) {
523
- e.preventDefault();
524
- e.stopPropagation();
525
- self.saveJSON();
526
- });
527
- this.editjson_copy = this.getButton('Copy','copy','Copy');
528
- this.editjson_copy.classList.add('json-editor-btntype-copy');
529
- this.editjson_copy.addEventListener('click',function(e) {
530
- e.preventDefault();
531
- e.stopPropagation();
532
- self.copyJSON();
533
- });
534
- this.editjson_cancel = this.getButton('Cancel','cancel','Cancel');
535
- this.editjson_cancel.classList.add('json-editor-btntype-cancel');
536
- this.editjson_cancel.addEventListener('click',function(e) {
537
- e.preventDefault();
538
- e.stopPropagation();
539
- self.hideEditJSON();
540
- });
541
- this.editjson_holder.appendChild(this.editjson_textarea);
542
- this.editjson_holder.appendChild(this.editjson_save);
543
- this.editjson_holder.appendChild(this.editjson_copy);
544
- this.editjson_holder.appendChild(this.editjson_cancel);
545
-
546
- // Manage Properties modal
547
- this.addproperty_holder = this.theme.getModal();
548
- this.addproperty_list = document.createElement('div');
549
- this.addproperty_list.style.width = '295px';
550
- this.addproperty_list.style.maxHeight = '160px';
551
- this.addproperty_list.style.padding = '5px 0';
552
- this.addproperty_list.style.overflowY = 'auto';
553
- this.addproperty_list.style.overflowX = 'hidden';
554
- this.addproperty_list.style.paddingLeft = '5px';
555
- this.addproperty_list.setAttribute('class', 'property-selector');
556
- this.addproperty_add = this.getButton('add','add','add');
557
- this.addproperty_add.classList.add('json-editor-btntype-add');
558
- this.addproperty_input = this.theme.getFormInputField('text');
559
- this.addproperty_input.setAttribute('placeholder','Property name...');
560
- this.addproperty_input.style.width = '220px';
561
- this.addproperty_input.style.marginBottom = '0';
562
- this.addproperty_input.style.display = 'inline-block';
563
- this.addproperty_add.addEventListener('click',function(e) {
564
- e.preventDefault();
565
- e.stopPropagation();
566
- if(self.addproperty_input.value) {
567
- if(self.editors[self.addproperty_input.value]) {
568
- window.alert('there is already a property with that name');
569
- return;
546
+ })
547
+ /* If the object should be rendered as a table */
548
+ } else if (this.options.table) {
549
+ /* TODO: table display format */
550
+ throw new Error('Not supported yet')
551
+ /* If the object should be rendered as a div */
552
+ } else {
553
+ this.header = ''
554
+ if (!this.options.compact) {
555
+ this.header = document.createElement('span')
556
+ this.header.textContent = this.getTitle()
557
+ }
558
+ this.title = this.theme.getHeader(this.header, this.getPathDepth())
559
+ this.title.classList.add('je-object__title')
560
+ this.controls = this.theme.getButtonHolder()
561
+ this.controls.classList.add('je-object__controls')
562
+
563
+ this.container.appendChild(this.title)
564
+ this.container.appendChild(this.controls)
565
+ this.container.classList.add('je-object__container')
566
+
567
+ /* Edit JSON modal */
568
+ this.editjson_holder = this.theme.getModal()
569
+ this.editjson_textarea_label = this.theme.getHiddenLabel(this.translate('button_edit_json'))
570
+ this.editjson_textarea_label.setAttribute('for', this.path + '-' + 'edit-json-textarea')
571
+ this.editjson_textarea = this.theme.getTextareaInput()
572
+ this.editjson_textarea.setAttribute('id', this.path + '-' + 'edit-json-textarea')
573
+ this.editjson_textarea.setAttribute('aria-labelledby', this.path + '-' + 'edit-json-textarea')
574
+ this.editjson_textarea.classList.add('je-edit-json--textarea')
575
+ this.editjson_save = this.getButton('button_save', 'save', 'button_save')
576
+ this.editjson_save.classList.add('json-editor-btntype-save')
577
+ this.editjson_save.addEventListener('click', (e) => {
578
+ e.preventDefault()
579
+ e.stopPropagation()
580
+ this.saveJSON()
581
+ })
582
+ this.editjson_copy = this.getButton('button_copy', 'copy', 'button_copy')
583
+ this.editjson_copy.classList.add('json-editor-btntype-copy')
584
+ this.editjson_copy.addEventListener('click', (e) => {
585
+ e.preventDefault()
586
+ e.stopPropagation()
587
+ this.copyJSON()
588
+ })
589
+ this.editjson_cancel = this.getButton('button_cancel', 'cancel', 'button_cancel')
590
+ this.editjson_cancel.classList.add('json-editor-btntype-cancel')
591
+ this.editjson_cancel.addEventListener('click', (e) => {
592
+ e.preventDefault()
593
+ e.stopPropagation()
594
+ this.hideEditJSON()
595
+ })
596
+ this.editjson_holder.appendChild(this.editjson_textarea_label)
597
+ this.editjson_holder.appendChild(this.editjson_textarea)
598
+ this.editjson_holder.appendChild(this.editjson_save)
599
+ this.editjson_holder.appendChild(this.editjson_copy)
600
+ this.editjson_holder.appendChild(this.editjson_cancel)
601
+
602
+ /* Manage Properties modal */
603
+ this.addproperty_holder = this.theme.getModal()
604
+ this.addproperty_list = document.createElement('div')
605
+ this.addproperty_list.classList.add('property-selector')
606
+ this.addproperty_add = this.getButton('button_add', 'add', 'button_add')
607
+ this.addproperty_add.classList.add('json-editor-btntype-add')
608
+
609
+ this.addproperty_input = this.theme.getFormInputField('text')
610
+ this.addproperty_input.setAttribute('placeholder', 'Property name...')
611
+
612
+ this.addproperty_input_label = this.theme.getHiddenLabel(this.translate('button_properties'))
613
+ this.addproperty_input_label.setAttribute('for', this.path + '-' + 'property-selector')
614
+
615
+ this.addproperty_input.classList.add('property-selector-input')
616
+ this.addproperty_input.setAttribute('id', this.path + '-' + 'property-selector')
617
+ this.addproperty_input.setAttribute('aria-labelledby', this.path + '-' + 'property-selector')
618
+
619
+ this.addproperty_add.addEventListener('click', (e) => {
620
+ e.preventDefault()
621
+ e.stopPropagation()
622
+ if (this.addproperty_input.value) {
623
+ if (this.editors[this.addproperty_input.value]) {
624
+ window.alert('there is already a property with that name')
625
+ return
570
626
  }
571
627
 
572
- self.addObjectProperty(self.addproperty_input.value);
573
- if(self.editors[self.addproperty_input.value]) {
574
- self.editors[self.addproperty_input.value].disable();
628
+ this.addObjectProperty(this.addproperty_input.value)
629
+ if (this.editors[this.addproperty_input.value]) {
630
+ this.editors[this.addproperty_input.value].disable()
575
631
  }
576
- self.onChange(true);
632
+ const key = this.editors[this.addproperty_input.value].key
633
+ const type = this.editors[this.addproperty_input.value].type
634
+ const path = this.editors[this.addproperty_input.value].path
635
+
636
+ this.onChange(true, false, {
637
+ event: 'add',
638
+ data: { key, type, path }
639
+ })
577
640
  }
578
- });
579
- this.addproperty_holder.appendChild(this.addproperty_list);
580
- this.addproperty_holder.appendChild(this.addproperty_input);
581
- this.addproperty_holder.appendChild(this.addproperty_add);
582
- var spacer = document.createElement('div');
583
- spacer.style.clear = 'both';
584
- this.addproperty_holder.appendChild(spacer);
585
-
586
- // Close properties modal if clicked outside modal
587
- document.addEventListener('click', function(e) {
588
- if (!this.addproperty_holder.contains(e.target) && this.adding_property) {
589
- e.preventDefault();
590
- e.stopPropagation();
591
- this.toggleAddProperty();
592
- }
593
- }.bind(this));
641
+ })
642
+ this.addproperty_input.addEventListener('input', (e) => {
643
+ e.target.previousSibling.previousSibling.childNodes.forEach((value) => {
644
+ let searchTerm = value.innerText
645
+ let propertyTitle = e.target.value
594
646
 
595
- // Description
596
- if(this.schema.description) {
597
- this.description = this.theme.getDescription(this.schema.description);
598
- this.container.appendChild(this.description);
599
- }
600
-
601
- // Validation error placeholder area
602
- this.error_holder = document.createElement('div');
603
- this.container.appendChild(this.error_holder);
604
-
605
- // Container for child editor area
606
- this.editor_holder = this.theme.getIndentedPanel();
607
- this.container.appendChild(this.editor_holder);
647
+ const caseSensitivePropertySearch = this.options.case_sensitive_property_search || this.jsoneditor.options.case_sensitive_property_search
608
648
 
609
- // Container for rows of child editors
610
- this.row_container = this.theme.getGridContainer();
649
+ if (!caseSensitivePropertySearch) {
650
+ searchTerm = searchTerm.toLowerCase()
651
+ propertyTitle = propertyTitle.toLowerCase()
652
+ }
611
653
 
612
- if(isCategoriesFormat) {
613
- this.tabs_holder = this.theme.getTopTabHolder(this.getValidId(this.schema.title));
614
- this.tabPanesContainer = this.theme.getTopTabContentHolder(this.tabs_holder);
615
- this.editor_holder.appendChild(this.tabs_holder);
654
+ if (searchTerm.includes(propertyTitle)) {
655
+ value.style.display = ''
656
+ } else {
657
+ value.style.display = 'none'
658
+ }
659
+ })
660
+ })
661
+ this.addproperty_holder.appendChild(this.addproperty_list)
662
+ this.addproperty_holder.appendChild(this.addproperty_input_label)
663
+ this.addproperty_holder.appendChild(this.addproperty_input)
664
+ this.addproperty_holder.appendChild(this.addproperty_add)
665
+ const spacer = document.createElement('div')
666
+ spacer.style.clear = 'both'
667
+ this.addproperty_holder.appendChild(spacer)
668
+
669
+ /* Close properties modal if clicked outside modal */
670
+ this.onOutsideModalClickListener = this.onOutsideModalClick.bind(this)
671
+ document.addEventListener('click', this.onOutsideModalClickListener, true)
672
+
673
+ /* Description */
674
+ if (this.schema.description) {
675
+ this.description = this.theme.getDescription(this.translateProperty(this.schema.description))
676
+ this.container.appendChild(this.description)
616
677
  }
617
- else {
618
- this.tabs_holder = this.theme.getTabHolder(this.getValidId(this.schema.title));
619
- this.tabPanesContainer = this.theme.getTabContentHolder(this.tabs_holder);
620
- this.editor_holder.appendChild(this.row_container);
678
+
679
+ /* Validation error placeholder area */
680
+ this.error_holder = document.createElement('div')
681
+ this.container.appendChild(this.error_holder)
682
+
683
+ /* Container for child editor area */
684
+ this.editor_holder = this.theme.getIndentedPanel()
685
+ this.container.appendChild(this.editor_holder)
686
+
687
+ /* Container for rows of child editors */
688
+ this.row_container = this.theme.getGridContainer()
689
+
690
+ if (isCategoriesFormat) {
691
+ this.tabs_holder = this.theme.getTopTabHolder(this.getValidId(this.translateProperty(this.schema.title)))
692
+ this.tabPanesContainer = this.theme.getTopTabContentHolder(this.tabs_holder)
693
+ this.editor_holder.appendChild(this.tabs_holder)
694
+ } else {
695
+ this.tabs_holder = this.theme.getTabHolder(this.getValidId(this.translateProperty(this.schema.title)))
696
+ this.tabPanesContainer = this.theme.getTabContentHolder(this.tabs_holder)
697
+ this.editor_holder.appendChild(this.row_container)
621
698
  }
622
699
 
623
- $each(this.editors, function(key,editor) {
624
- var aPane = self.theme.getTabContent();
625
- var holder = self.theme.getGridColumn();
626
- var isObjOrArray = (editor.schema && (editor.schema.type === 'object' || editor.schema.type === 'array')) ? true : false;
627
- aPane.isObjOrArray = isObjOrArray;
628
-
629
- if(isCategoriesFormat){
630
- if(isObjOrArray) {
631
- var single_row_container = self.theme.getGridContainer();
632
- single_row_container.appendChild(holder);
633
- aPane.appendChild(single_row_container);
634
- self.tabPanesContainer.appendChild(aPane);
635
- self.row_container = single_row_container;
636
- }
637
- else {
638
- if(typeof self.row_container_basic === 'undefined'){
639
- self.row_container_basic = self.theme.getGridContainer();
640
- aPane.appendChild(self.row_container_basic);
641
- if(self.tabPanesContainer.childElementCount == 0){
642
- self.tabPanesContainer.appendChild(aPane);
643
- }
644
- else {
645
- self.tabPanesContainer.insertBefore(aPane,self.tabPanesContainer.childNodes[1]);
700
+ Object.values(this.editors).forEach(editor => {
701
+ const aPane = this.theme.getTabContent()
702
+ const holder = this.theme.getGridColumn()
703
+ const isObjOrArray = !!((editor.schema && (editor.schema.type === 'object' || editor.schema.type === 'array')))
704
+ aPane.isObjOrArray = isObjOrArray
705
+
706
+ if (isCategoriesFormat) {
707
+ if (isObjOrArray) {
708
+ const singleRowContainer = this.theme.getGridContainer()
709
+ singleRowContainer.appendChild(holder)
710
+ aPane.appendChild(singleRowContainer)
711
+ this.tabPanesContainer.appendChild(aPane)
712
+ this.row_container = singleRowContainer
713
+ } else {
714
+ if (typeof this.row_container_basic === 'undefined') {
715
+ this.row_container_basic = this.theme.getGridContainer()
716
+ aPane.appendChild(this.row_container_basic)
717
+ if (this.tabPanesContainer.childElementCount === 0) {
718
+ this.tabPanesContainer.appendChild(aPane)
719
+ } else {
720
+ this.tabPanesContainer.insertBefore(aPane, this.tabPanesContainer.childNodes[1])
646
721
  }
647
722
  }
648
- self.row_container_basic.appendChild(holder);
723
+ this.row_container_basic.appendChild(holder)
649
724
  }
650
725
 
651
- self.addRow(editor,self.tabs_holder,aPane);
726
+ this.addRow(editor, this.tabs_holder, aPane)
652
727
 
653
- aPane.id = self.getValidId(editor.schema.title); //editor.schema.path//tab_text.textContent
654
-
655
- }
656
- else {
657
- self.row_container.appendChild(holder);
728
+ aPane.id = this.getValidId(editor.schema.title) /* editor.schema.path//tab_text.textContent */
729
+ } else {
730
+ this.row_container.appendChild(holder)
658
731
  }
659
732
 
660
- editor.setContainer(holder);
661
- editor.build();
662
- editor.postBuild();
663
- });
733
+ editor.setContainer(holder)
734
+ editor.build()
735
+ editor.postBuild()
736
+ editor.setOptInCheckbox(editor.header)
737
+ })
664
738
 
665
- if(this.rows[0]){
666
- $trigger(this.rows[0].tab,'click');
739
+ if (this.rows[0]) {
740
+ trigger(this.rows[0].tab, 'click')
667
741
  }
668
742
 
669
- // Control buttons
670
- this.title_controls = this.theme.getHeaderButtonHolder();
671
- this.editjson_controls = this.theme.getHeaderButtonHolder();
672
- this.addproperty_controls = this.theme.getHeaderButtonHolder();
673
- this.title.appendChild(this.title_controls);
674
- this.title.appendChild(this.editjson_controls);
675
- this.title.appendChild(this.addproperty_controls);
676
-
677
- // Show/Hide button
678
- this.collapsed = false;
679
- this.toggle_button = this.getButton('', 'collapse', this.translate('button_collapse'));
680
- this.toggle_button.classList.add('json-editor-btntype-toggle');
681
- this.title_controls.appendChild(this.toggle_button);
682
- this.toggle_button.addEventListener('click',function(e) {
683
- e.preventDefault();
684
- e.stopPropagation();
685
- if(self.collapsed) {
686
- self.editor_holder.style.display = '';
687
- self.collapsed = false;
688
- self.setButtonText(self.toggle_button,'','collapse',self.translate('button_collapse'));
689
- }
690
- else {
691
- self.editor_holder.style.display = 'none';
692
- self.collapsed = true;
693
- self.setButtonText(self.toggle_button,'','expand',self.translate('button_expand'));
743
+ /* Show/Hide button */
744
+ this.collapsed = false
745
+ this.collapse_control = this.getButton('', 'collapse', 'button_collapse')
746
+ this.collapse_control.classList.add('json-editor-btntype-toggle')
747
+ this.title.insertBefore(this.collapse_control, this.title.childNodes[0])
748
+
749
+ this.collapse_control.addEventListener('click', (e) => {
750
+ e.preventDefault()
751
+ e.stopPropagation()
752
+ if (this.collapsed) {
753
+ this.editor_holder.style.display = ''
754
+ this.collapsed = false
755
+ this.setButtonText(this.collapse_control, '', 'collapse', 'button_collapse')
756
+ } else {
757
+ this.editor_holder.style.display = 'none'
758
+ this.collapsed = true
759
+ this.setButtonText(this.collapse_control, '', 'expand', 'button_expand')
694
760
  }
695
- });
761
+ })
696
762
 
697
- // If it should start collapsed
698
- if(this.options.collapsed) {
699
- $trigger(this.toggle_button,'click');
763
+ /* If it should start collapsed */
764
+ if (this.options.collapsed) {
765
+ trigger(this.collapse_control, 'click')
700
766
  }
701
767
 
702
- // Collapse button disabled
703
- if(this.schema.options && typeof this.schema.options.disable_collapse !== "undefined") {
704
- if(this.schema.options.disable_collapse) this.toggle_button.style.display = 'none';
705
- }
706
- else if(this.jsoneditor.options.disable_collapse) {
707
- this.toggle_button.style.display = 'none';
768
+ /* Collapse button disabled */
769
+ if (this.schema.options && typeof this.schema.options.disable_collapse !== 'undefined') {
770
+ if (this.schema.options.disable_collapse) this.collapse_control.style.display = 'none'
771
+ } else if (this.jsoneditor.options.disable_collapse) {
772
+ this.collapse_control.style.display = 'none'
708
773
  }
709
774
 
710
- // Edit JSON Button
711
- this.editjson_button = this.getButton('JSON','edit','Edit JSON');
712
- this.editjson_button.classList.add('json-editor-btntype-editjson');
713
- this.editjson_button.addEventListener('click',function(e) {
714
- e.preventDefault();
715
- e.stopPropagation();
716
- self.toggleEditJSON();
717
- });
718
- this.editjson_controls.appendChild(this.editjson_button);
719
- this.editjson_controls.appendChild(this.editjson_holder);
720
-
721
- // Edit JSON Buttton disabled
722
- if(this.schema.options && typeof this.schema.options.disable_edit_json !== "undefined") {
723
- if(this.schema.options.disable_edit_json) this.editjson_button.style.display = 'none';
724
- }
725
- else if(this.jsoneditor.options.disable_edit_json) {
726
- this.editjson_button.style.display = 'none';
775
+ /* Edit JSON Button */
776
+ this.editjson_control = this.getButton('JSON', 'edit', 'button_edit_json')
777
+ this.editjson_control.classList.add('json-editor-btntype-editjson')
778
+ this.editjson_control.addEventListener('click', (e) => {
779
+ e.preventDefault()
780
+ e.stopPropagation()
781
+ this.toggleEditJSON()
782
+ })
783
+ this.controls.appendChild(this.editjson_control)
784
+ this.controls.insertBefore(this.editjson_holder, this.controls.childNodes[0])
785
+
786
+ /* Edit JSON Buttton disabled */
787
+ if (this.schema.options && typeof this.schema.options.disable_edit_json !== 'undefined') {
788
+ if (this.schema.options.disable_edit_json) this.editjson_control.style.display = 'none'
789
+ } else if (this.jsoneditor.options.disable_edit_json) {
790
+ this.editjson_control.style.display = 'none'
727
791
  }
728
792
 
729
- // Object Properties Button
730
- this.addproperty_button = this.getButton('Properties','edit','Object Properties');
731
- this.addproperty_button.classList.add('json-editor-btntype-properties');
732
- this.addproperty_button.addEventListener('click',function(e) {
733
- e.preventDefault();
734
- e.stopPropagation();
735
- self.toggleAddProperty();
736
- });
737
- this.addproperty_controls.appendChild(this.addproperty_button);
738
- this.addproperty_controls.appendChild(this.addproperty_holder);
739
- this.refreshAddProperties();
793
+ /* Object Properties Button */
794
+ this.addproperty_button = this.getButton('properties', 'edit_properties', 'button_object_properties')
795
+ this.addproperty_button.classList.add('json-editor-btntype-properties')
796
+ this.addproperty_button.addEventListener('click', (e) => {
797
+ e.preventDefault()
798
+ e.stopPropagation()
799
+ this.toggleAddProperty()
800
+ })
801
+ this.controls.appendChild(this.addproperty_button)
802
+ this.controls.insertBefore(this.addproperty_holder, this.controls.childNodes[1])
803
+
804
+ this.refreshAddProperties()
805
+
806
+ /* non required properties start deactivated */
807
+ this.deactivateNonRequiredProperties(false)
740
808
  }
741
-
742
- // Fix table cell ordering
743
- if(this.options.table_row) {
744
- this.editor_holder = this.container;
745
- $each(this.property_order,function(i,key) {
746
- self.editor_holder.appendChild(self.editors[key].container);
747
- });
809
+
810
+ /* Fix table cell ordering */
811
+ if (this.options.table_row) {
812
+ this.editor_holder = this.container
813
+ this.property_order.forEach(key => {
814
+ this.editor_holder.appendChild(this.editors[key].container)
815
+ })
816
+ /* Layout object editors in grid if needed */
817
+ } else {
818
+ /* Initial layout */
819
+ this.layoutEditors()
820
+ /* Do it again now that we know the approximate heights of elements */
821
+ this.layoutEditors()
748
822
  }
749
- // Layout object editors in grid if needed
750
- else {
751
- // Initial layout
752
- this.layoutEditors();
753
- // Do it again now that we know the approximate heights of elements
754
- this.layoutEditors();
823
+
824
+ if (this.schema.readOnly || this.schema.readonly) {
825
+ this.disable()
755
826
  }
756
- },
757
- showEditJSON: function() {
758
- if(!this.editjson_holder) return;
759
- this.hideAddProperty();
760
-
761
- // Position the form directly beneath the button
762
- // TODO: edge detection
763
- this.editjson_holder.style.left = this.editjson_button.offsetLeft+"px";
764
- this.editjson_holder.style.top = this.editjson_button.offsetTop + this.editjson_button.offsetHeight+"px";
765
-
766
- // Start the textarea with the current value
767
- this.editjson_textarea.value = JSON.stringify(this.getValue(),null,2);
768
-
769
- // Disable the rest of the form while editing JSON
770
- this.disable();
771
-
772
- this.editjson_holder.style.display = '';
773
- this.editjson_button.disabled = false;
774
- this.editing_json = true;
775
- },
776
- hideEditJSON: function() {
777
- if(!this.editjson_holder) return;
778
- if(!this.editing_json) return;
779
-
780
- this.editjson_holder.style.display = 'none';
781
- this.enable();
782
- this.editing_json = false;
783
- },
784
- copyJSON: function() {
785
- if(!this.editjson_holder) return;
786
- var ta = document.createElement('textarea');
787
- ta.value = this.editjson_textarea.value;
788
- ta.setAttribute('readonly', '');
789
- ta.style.position = 'absolute';
790
- ta.style.left = '-9999px';
791
- document.body.appendChild(ta);
792
- ta.select();
793
- document.execCommand('copy');
794
- document.body.removeChild(ta);
795
- },
796
- saveJSON: function() {
797
- if(!this.editjson_holder) return;
827
+ }
798
828
 
799
- try {
800
- var json = JSON.parse(this.editjson_textarea.value);
801
- this.setValue(json);
802
- this.hideEditJSON();
803
- this.onChange(true);
829
+ deactivateNonRequiredProperties (recursive) {
830
+ /* the show_opt_in editor option is for backward compatibility */
831
+ const globalOptIn = this.jsoneditor.options.show_opt_in
832
+ const editorOptInDefined = (typeof this.options.show_opt_in !== 'undefined')
833
+ const editorOptInEnabled = (editorOptInDefined && this.options.show_opt_in === true)
834
+ const editorOptInDisabled = (editorOptInDefined && this.options.show_opt_in === false)
835
+ if (editorOptInEnabled || (!editorOptInDisabled && globalOptIn) || (!editorOptInDefined && globalOptIn)) {
836
+ Object.entries(this.editors).forEach(([key, editor]) => {
837
+ if (!this.isRequiredObject(editor)) {
838
+ this.editors[key].deactivate()
839
+ }
840
+ if (recursive && typeof this.editors[key].deactivateNonRequiredProperties === 'function') {
841
+ this.editors[key].deactivateNonRequiredProperties(recursive)
842
+ }
843
+ })
804
844
  }
805
- catch(e) {
806
- window.alert('invalid JSON');
807
- throw e;
845
+ }
846
+
847
+ showEditJSON () {
848
+ if (!this.editjson_holder) return
849
+ this.hideAddProperty()
850
+
851
+ /* Position the form directly beneath the button */
852
+ /* TODO: edge detection */
853
+ this.editjson_holder.style.left = `${this.editjson_control.offsetLeft}px`
854
+ this.editjson_holder.style.top = `${this.editjson_control.offsetTop + this.editjson_control.offsetHeight}px`
855
+
856
+ /* Start the textarea with the current value */
857
+ this.editjson_textarea.value = JSON.stringify(this.getValue(), null, 2)
858
+
859
+ /* Disable the rest of the form while editing JSON */
860
+ this.disable()
861
+
862
+ this.editjson_holder.style.display = ''
863
+ this.editjson_control.disabled = false
864
+ this.editing_json = true
865
+ }
866
+
867
+ hideEditJSON () {
868
+ if (!this.editjson_holder) return
869
+ if (!this.editing_json) return
870
+
871
+ this.editjson_holder.style.display = 'none'
872
+ this.enable()
873
+ this.editing_json = false
874
+ }
875
+
876
+ copyJSON () {
877
+ if (!this.editjson_holder) return
878
+ navigator.clipboard.writeText(this.editjson_textarea.value)
879
+ .catch((e) => window.alert(e))
880
+ }
881
+
882
+ saveJSON () {
883
+ if (!this.editjson_holder) return
884
+
885
+ try {
886
+ const json = JSON.parse(this.editjson_textarea.value)
887
+ this.setValue(json)
888
+ this.hideEditJSON()
889
+ this.onChange(true)
890
+ } catch (e) {
891
+ window.alert('invalid JSON')
892
+ throw e
808
893
  }
809
- },
810
- toggleEditJSON: function() {
811
- if(this.editing_json) this.hideEditJSON();
812
- else this.showEditJSON();
813
- },
814
- insertPropertyControlUsingPropertyOrder: function (property, control, container) {
815
- var propertyOrder;
816
- if (this.schema.properties[property])
817
- propertyOrder = this.schema.properties[property].propertyOrder;
818
- if (typeof propertyOrder !== "number") propertyOrder = 1000;
819
- control.propertyOrder = propertyOrder;
820
-
821
- for (var i = 0; i < container.childNodes.length; i++) {
822
- var child = container.childNodes[i];
894
+ }
895
+
896
+ toggleEditJSON () {
897
+ if (this.editing_json) this.hideEditJSON()
898
+ else this.showEditJSON()
899
+ }
900
+
901
+ insertPropertyControlUsingPropertyOrder (property, control, container) {
902
+ let propertyOrder
903
+ if (this.schema.properties[property]) { propertyOrder = this.schema.properties[property].propertyOrder }
904
+ if (typeof propertyOrder !== 'number') propertyOrder = 1000
905
+ control.propertyOrder = propertyOrder
906
+
907
+ for (let i = 0; i < container.childNodes.length; i++) {
908
+ const child = container.childNodes[i]
823
909
  if (control.propertyOrder < child.propertyOrder) {
824
- this.addproperty_list.insertBefore(control, child);
825
- control = null;
826
- break;
910
+ this.addproperty_list.insertBefore(control, child)
911
+ control = null
912
+ break
827
913
  }
828
914
  }
829
915
  if (control) {
830
- this.addproperty_list.appendChild(control);
916
+ this.addproperty_list.appendChild(control)
831
917
  }
832
- },
833
- addPropertyCheckbox: function(key) {
834
- var self = this;
835
- var checkbox, label, labelText, control;
918
+ }
919
+
920
+ addPropertyCheckbox (key) {
921
+ let labelText
836
922
 
837
- checkbox = self.theme.getCheckbox();
838
- checkbox.style.width = 'auto';
923
+ const checkbox = this.theme.getCheckbox()
839
924
 
840
- if (this.schema.properties[key] && this.schema.properties[key].title)
841
- labelText = this.schema.properties[key].title;
842
- else
843
- labelText = key;
925
+ if (this.schema.properties[key] && this.schema.properties[key].title) { labelText = this.schema.properties[key].title } else { labelText = key }
844
926
 
845
- label = self.theme.getCheckboxLabel(labelText);
927
+ const label = this.theme.getCheckboxLabel(labelText)
846
928
 
847
- control = self.theme.getFormControl(label,checkbox);
848
- control.style.paddingBottom = control.style.marginBottom = control.style.paddingTop = control.style.marginTop = 0;
849
- control.style.height = 'auto';
850
- //control.style.overflowY = 'hidden';
929
+ const control = this.theme.getFormControl(label, checkbox, null, null, this.path + '-' + key)
930
+ control.style.paddingBottom = control.style.marginBottom = control.style.paddingTop = control.style.marginTop = 0
931
+ control.style.height = 'auto'
932
+ /* control.style.overflowY = 'hidden'; */
851
933
 
852
- this.insertPropertyControlUsingPropertyOrder(key, control, this.addproperty_list);
934
+ this.insertPropertyControlUsingPropertyOrder(key, control, this.addproperty_list)
853
935
 
854
- checkbox.checked = key in this.editors;
855
- checkbox.addEventListener('change',function() {
856
- if(checkbox.checked) {
857
- self.addObjectProperty(key);
936
+ checkbox.checked = key in this.editors
937
+ checkbox.addEventListener('change', () => {
938
+ if (checkbox.checked) {
939
+ this.addObjectProperty(key)
940
+ } else {
941
+ this.removeObjectProperty(key)
858
942
  }
859
- else {
860
- self.removeObjectProperty(key);
943
+ this.onChange(true)
944
+ })
945
+ this.addproperty_checkboxes[key] = checkbox
946
+
947
+ return checkbox
948
+ }
949
+
950
+ showAddProperty () {
951
+ if (!this.addproperty_holder) return
952
+ this.hideEditJSON()
953
+
954
+ /* Position the form directly beneath the button */
955
+ /* TODO: edge detection */
956
+ this.addproperty_holder.style.left = `${this.addproperty_button.offsetLeft}px`
957
+ this.addproperty_holder.style.top = `${this.addproperty_button.offsetTop + this.addproperty_button.offsetHeight}px`
958
+
959
+ /* Disable the rest of the form while editing JSON */
960
+ this.disable()
961
+
962
+ this.adding_property = true
963
+ this.addproperty_button.disabled = false
964
+ this.addproperty_holder.style.display = ''
965
+ this.refreshAddProperties()
966
+ }
967
+
968
+ hideAddProperty () {
969
+ if (!this.addproperty_holder) return
970
+ if (!this.adding_property) return
971
+
972
+ this.addproperty_holder.style.display = 'none'
973
+ this.enable()
974
+
975
+ this.adding_property = false
976
+ }
977
+
978
+ toggleAddProperty () {
979
+ if (this.adding_property) this.hideAddProperty()
980
+ else this.showAddProperty()
981
+ }
982
+
983
+ removeObjectProperty (property) {
984
+ if (this.editors[property]) {
985
+ // do not destroy dependent editors
986
+ if (this.editors[property].schema?.options?.dependencies) {
987
+ return
861
988
  }
862
- self.onChange(true);
863
- });
864
- self.addproperty_checkboxes[key] = checkbox;
865
-
866
- return checkbox;
867
- },
868
- showAddProperty: function() {
869
- if(!this.addproperty_holder) return;
870
- this.hideEditJSON();
871
-
872
- // Position the form directly beneath the button
873
- // TODO: edge detection
874
- this.addproperty_holder.style.left = this.addproperty_button.offsetLeft+"px";
875
- this.addproperty_holder.style.top = this.addproperty_button.offsetTop + this.addproperty_button.offsetHeight+"px";
876
-
877
- // Disable the rest of the form while editing JSON
878
- this.disable();
879
-
880
- this.adding_property = true;
881
- this.addproperty_button.disabled = false;
882
- this.addproperty_holder.style.display = '';
883
- this.refreshAddProperties();
884
- },
885
- hideAddProperty: function() {
886
- if(!this.addproperty_holder) return;
887
- if(!this.adding_property) return;
888
-
889
- this.addproperty_holder.style.display = 'none';
890
- this.enable();
891
-
892
- this.adding_property = false;
893
- },
894
- toggleAddProperty: function() {
895
- if(this.adding_property) this.hideAddProperty();
896
- else this.showAddProperty();
897
- },
898
- removeObjectProperty: function(property) {
899
- if(this.editors[property]) {
900
- this.editors[property].unregister();
901
- delete this.editors[property];
902
-
903
- this.refreshValue();
904
- this.layoutEditors();
905
- }
906
- },
907
- addObjectProperty: function(name, prebuild_only) {
908
- var self = this;
909
-
910
- // Property is already added
911
- if(this.editors[name]) return;
912
-
913
- // Property was added before and is cached
914
- if(this.cached_editors[name]) {
915
- this.editors[name] = this.cached_editors[name];
916
- if(prebuild_only) return;
917
- this.editors[name].register();
989
+
990
+ this.editors[property].unregister()
991
+ delete this.editors[property]
992
+
993
+ this.refreshValue()
994
+ this.layoutEditors()
918
995
  }
919
- // New property
920
- else {
921
- if(!this.canHaveAdditionalProperties() && (!this.schema.properties || !this.schema.properties[name])) {
922
- return;
923
- }
996
+ }
924
997
 
925
- var schema = self.getPropertySchema(name);
926
- if(typeof schema.propertyOrder !== 'number'){
927
- // if the propertyOrder undefined, then set a smart default value.
928
- schema.propertyOrder = Object.keys(self.editors).length + 1000;
998
+ getSchemaOnMaxDepth (schema) {
999
+ return Object.keys(schema).reduce((acc, key) => {
1000
+ switch (key) {
1001
+ case '$ref':
1002
+ return acc
1003
+ case 'properties':
1004
+ case 'items':
1005
+ return {
1006
+ ...acc,
1007
+ [key]: {}
1008
+ }
1009
+ case 'additionalProperties':
1010
+ case 'propertyNames':
1011
+ return {
1012
+ ...acc,
1013
+ [key]: true
1014
+ }
1015
+ default:
1016
+ return {
1017
+ ...acc,
1018
+ [key]: schema[key]
1019
+ }
929
1020
  }
1021
+ }, {})
1022
+ }
930
1023
 
1024
+ addObjectProperty (name, prebuildOnly) {
1025
+ /* Property is already added */
1026
+ if (this.editors[name]) return
1027
+
1028
+ /* Property was added before and is cached */
1029
+ if (this.cached_editors[name]) {
1030
+ this.editors[name] = this.cached_editors[name]
1031
+ if (prebuildOnly) return
1032
+ this.editors[name].register()
1033
+ /* New property */
1034
+ } else {
1035
+ if (!this.canHaveAdditionalProperties() && (!this.schema.properties || !this.schema.properties[name]) &&
1036
+ (!this.schema.patternProperties || !(Object.keys(this.schema.patternProperties).find(i => new RegExp(i).test(name))))) {
1037
+ return
1038
+ }
931
1039
 
932
- // Add the property
933
- var editor = self.jsoneditor.getEditorClass(schema);
934
-
935
- self.editors[name] = self.jsoneditor.createEditor(editor,{
936
- jsoneditor: self.jsoneditor,
937
- schema: schema,
938
- path: self.path+'.'+name,
939
- parent: self
940
- });
941
- self.editors[name].preBuild();
1040
+ const schema = this.getPropertySchema(name)
1041
+ if (typeof schema.propertyOrder !== 'number') {
1042
+ /* if the propertyOrder undefined, then set a smart default value. */
1043
+ schema.propertyOrder = Object.keys(this.editors).length + 1000
1044
+ }
942
1045
 
943
- if(!prebuild_only) {
944
- var holder = self.theme.getChildEditorHolder();
945
- self.editor_holder.appendChild(holder);
946
- self.editors[name].setContainer(holder);
947
- self.editors[name].build();
948
- self.editors[name].postBuild();
1046
+ /* Add the property */
1047
+ const editor = this.jsoneditor.getEditorClass(schema)
1048
+
1049
+ const { max_depth: maxDepth } = this.jsoneditor.options
1050
+
1051
+ this.editors[name] = this.jsoneditor.createEditor(editor, {
1052
+ jsoneditor: this.jsoneditor,
1053
+ schema: !!maxDepth && this.currentDepth >= maxDepth ? this.getSchemaOnMaxDepth(schema) : schema,
1054
+ path: `${this.path}.${name}`,
1055
+ parent: this
1056
+ }, this.currentDepth + 1)
1057
+ this.editors[name].preBuild()
1058
+
1059
+ if (!prebuildOnly) {
1060
+ const holder = this.theme.getChildEditorHolder()
1061
+ this.editor_holder.appendChild(holder)
1062
+ this.editors[name].setContainer(holder)
1063
+ this.editors[name].build()
1064
+ this.editors[name].postBuild()
1065
+ this.editors[name].setOptInCheckbox(editor.header)
1066
+ this.editors[name].activate()
949
1067
  }
950
1068
 
951
- self.cached_editors[name] = self.editors[name];
1069
+ this.cached_editors[name] = this.editors[name]
952
1070
  }
953
1071
 
954
- // If we're only prebuilding the editors, don't refresh values
955
- if(!prebuild_only) {
956
- self.refreshValue();
957
- self.layoutEditors();
958
- }
959
- },
960
- onChildEditorChange: function(editor) {
961
- this.refreshValue();
962
- this._super(editor);
963
- },
964
- canHaveAdditionalProperties: function() {
965
- if (typeof this.schema.additionalProperties === "boolean") {
966
- return this.schema.additionalProperties;
967
- }
968
- return !this.jsoneditor.options.no_additional_properties;
969
- },
970
- destroy: function() {
971
- $each(this.cached_editors, function(i,el) {
972
- el.destroy();
973
- });
974
- if(this.editor_holder) this.editor_holder.innerHTML = '';
975
- if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title);
976
- if(this.error_holder && this.error_holder.parentNode) this.error_holder.parentNode.removeChild(this.error_holder);
977
-
978
- this.editors = null;
979
- this.cached_editors = null;
980
- if(this.editor_holder && this.editor_holder.parentNode) this.editor_holder.parentNode.removeChild(this.editor_holder);
981
- this.editor_holder = null;
982
-
983
- this._super();
984
- },
985
- getValue: function() {
986
- if (!this.dependenciesFulfilled) {
987
- return undefined;
1072
+ /* If we're only prebuilding the editors, don't refresh values */
1073
+ if (!prebuildOnly) {
1074
+ this.refreshValue()
1075
+ this.layoutEditors()
988
1076
  }
989
- var result = this._super();
990
- if(this.jsoneditor.options.remove_empty_properties || this.options.remove_empty_properties) {
991
- for (var i in result) {
992
- if (result.hasOwnProperty(i)) {
993
- if (typeof result[i] === 'undefined' || result[i] === '' || result[i] === Object(result[i]) && Object.keys(result[i]).length == 0 && result[i].constructor == Object) {
994
- delete result[i];
995
- }
996
- }
997
- }
1077
+ }
1078
+
1079
+ onOutsideModalClick (e) {
1080
+ const path = e.path || (e.composedPath && e.composedPath())
1081
+ if (this.addproperty_holder && !this.addproperty_holder.contains(path[0]) && this.adding_property) {
1082
+ e.preventDefault()
1083
+ e.stopPropagation()
1084
+ this.toggleAddProperty()
998
1085
  }
1086
+ }
999
1087
 
1000
- return result;
1001
- },
1002
- refreshValue: function() {
1003
- this.value = {};
1004
- var self = this;
1088
+ onChildEditorChange (editor, eventData) {
1089
+ this.refreshValue()
1090
+ super.onChildEditorChange(editor, eventData)
1091
+ }
1005
1092
 
1006
- for(var i in this.editors) {
1007
- if(!this.editors.hasOwnProperty(i)) continue;
1008
- this.value[i] = this.editors[i].getValue();
1093
+ canHaveAdditionalProperties () {
1094
+ // schemas have priority over options
1095
+ // local options have priority over global options
1096
+ // lastly global options are evaluated
1097
+
1098
+ // If the schema additionalProperties keyword is a boolean let the keyword decide
1099
+ if (typeof this.schema.additionalProperties === 'boolean') {
1100
+ return this.schema.additionalProperties
1009
1101
  }
1010
1102
 
1011
- if(this.adding_property) this.refreshAddProperties();
1012
- },
1013
- refreshAddProperties: function() {
1014
- if(this.options.disable_properties || (this.options.disable_properties !== false && this.jsoneditor.options.disable_properties)) {
1015
- this.addproperty_controls.style.display = 'none';
1016
- return;
1103
+ // If the schema additionalProperties keyword is a schema then additional properties are allowed and limited by such schema
1104
+ if (typeof this.schema.additionalProperties === 'object' && this.schema.additionalProperties !== null) {
1105
+ return true
1017
1106
  }
1018
1107
 
1019
- var can_add = false, can_remove = false, num_props = 0, i, show_modal = false;
1108
+ // If the schema options no_additional_properties is a boolean let the option decide
1109
+ if (typeof this.options.no_additional_properties === 'boolean') {
1110
+ return !this.options.no_additional_properties
1111
+ }
1020
1112
 
1021
- // Get number of editors
1022
- for(i in this.editors) {
1023
- if(!this.editors.hasOwnProperty(i)) continue;
1024
- num_props++;
1113
+ // If the global options no_additional_properties is a boolean let the option decide
1114
+ if (typeof this.jsoneditor.options.no_additional_properties === 'boolean') {
1115
+ return !this.jsoneditor.options.no_additional_properties
1025
1116
  }
1026
1117
 
1027
- // Determine if we can add back removed properties
1028
- can_add = this.canHaveAdditionalProperties() && !(typeof this.schema.maxProperties !== "undefined" && num_props >= this.schema.maxProperties);
1118
+ return true
1119
+ }
1120
+
1121
+ destroy () {
1122
+ Object.values(this.cached_editors).forEach(el => el.destroy())
1123
+ if (this.editor_holder) this.editor_holder.innerHTML = ''
1124
+ if (this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title)
1125
+ if (this.error_holder && this.error_holder.parentNode) this.error_holder.parentNode.removeChild(this.error_holder)
1126
+
1127
+ this.editors = null
1128
+ this.cached_editors = null
1129
+ if (this.editor_holder && this.editor_holder.parentNode) this.editor_holder.parentNode.removeChild(this.editor_holder)
1130
+ this.editor_holder = null
1131
+ document.removeEventListener('click', this.onOutsideModalClickListener, true)
1029
1132
 
1030
- if(this.addproperty_checkboxes) {
1031
- this.addproperty_list.innerHTML = '';
1133
+ super.destroy()
1134
+ }
1135
+
1136
+ getValue () {
1137
+ if (!this.dependenciesFulfilled) {
1138
+ return undefined
1139
+ }
1140
+ const result = super.getValue()
1141
+ const isEmpty = obj => typeof obj === 'undefined' || obj === '' ||
1142
+ (
1143
+ obj === Object(obj) &&
1144
+ Object.keys(obj).length === 0 &&
1145
+ obj.constructor === Object
1146
+ )
1147
+ if (result && (this.jsoneditor.options.remove_empty_properties || this.options.remove_empty_properties)) {
1148
+ Object.keys(result).forEach(key => {
1149
+ if (isEmpty(result[key])) {
1150
+ delete result[key]
1151
+ }
1152
+ })
1032
1153
  }
1033
- this.addproperty_checkboxes = {};
1034
1154
 
1035
- // Check for which editors can't be removed or added back
1036
- for(i in this.cached_editors) {
1037
- if(!this.cached_editors.hasOwnProperty(i)) continue;
1155
+ if (result && (this.jsoneditor.options.remove_false_properties || this.options.remove_false_properties)) {
1156
+ Object.keys(result).forEach(key => {
1157
+ if (result[key] === false) {
1158
+ delete result[key]
1159
+ }
1160
+ })
1161
+ }
1162
+ return result
1163
+ }
1038
1164
 
1039
- this.addPropertyCheckbox(i);
1165
+ refreshValue () {
1166
+ this.value = {}
1040
1167
 
1041
- if(this.isRequired(this.cached_editors[i]) && i in this.editors) {
1042
- this.addproperty_checkboxes[i].disabled = true;
1168
+ if (!this.editors) {
1169
+ return
1170
+ }
1171
+
1172
+ Object.keys(this.editors).forEach(i => {
1173
+ if (this.editors[i].isActive()) {
1174
+ this.editors[i].refreshValue()
1175
+ this.value[i] = this.editors[i].getValue()
1043
1176
  }
1177
+ })
1044
1178
 
1045
- if(typeof this.schema.minProperties !== "undefined" && num_props <= this.schema.minProperties) {
1046
- this.addproperty_checkboxes[i].disabled = this.addproperty_checkboxes[i].checked;
1047
- if(!this.addproperty_checkboxes[i].checked) show_modal = true;
1179
+ Object.keys(this.editors).forEach(i => {
1180
+ if (this.editors[i].isActive()) {
1181
+ this.activateDependentRequired(this.editors[i].key)
1048
1182
  }
1049
- else if(!(i in this.editors)) {
1050
- if(!can_add && !this.schema.properties.hasOwnProperty(i)) {
1051
- this.addproperty_checkboxes[i].disabled = true;
1052
- }
1053
- else {
1054
- this.addproperty_checkboxes[i].disabled = false;
1055
- show_modal = true;
1183
+ })
1184
+
1185
+ if (this.adding_property) {
1186
+ this.refreshAddProperties()
1187
+ }
1188
+ }
1189
+
1190
+ activateDependentRequired (key) {
1191
+ const dependentRequired = this.getDependentRequired(key)
1192
+ dependentRequired.forEach((requiredProperty) => {
1193
+ let dependentRequiredEditor
1194
+
1195
+ Object.entries(this.cached_editors).forEach(([i, cachedEditor]) => {
1196
+ if (cachedEditor.key === requiredProperty) {
1197
+ dependentRequiredEditor = cachedEditor
1056
1198
  }
1199
+ })
1200
+
1201
+ if (dependentRequiredEditor && !dependentRequiredEditor.isActive()) {
1202
+ dependentRequiredEditor.activate()
1057
1203
  }
1058
- else {
1059
- show_modal = true;
1060
- can_remove = true;
1204
+ })
1205
+ }
1206
+
1207
+ getDependentRequired (property) {
1208
+ if (this.schema.dependentRequired) {
1209
+ if (hasOwnProperty(this.schema.dependentRequired, property)) {
1210
+ return this.schema.dependentRequired[property]
1061
1211
  }
1062
1212
  }
1063
1213
 
1064
- if(this.canHaveAdditionalProperties()) {
1065
- show_modal = true;
1066
- }
1214
+ return []
1215
+ }
1067
1216
 
1068
- // Additional addproperty checkboxes not tied to a current editor
1069
- for(i in this.schema.properties) {
1070
- if(!this.schema.properties.hasOwnProperty(i)) continue;
1071
- if(this.cached_editors[i]) continue;
1072
- show_modal = true;
1073
- this.addPropertyCheckbox(i);
1217
+ refreshAddProperties () {
1218
+ if (this.options.disable_properties || (this.options.disable_properties !== false && this.jsoneditor.options.disable_properties)) {
1219
+ this.addproperty_button.style.display = 'none'
1220
+ return
1074
1221
  }
1075
1222
 
1076
- // If no editors can be added or removed, hide the modal button
1077
- if(!show_modal) {
1078
- this.hideAddProperty();
1079
- this.addproperty_controls.style.display = 'none';
1223
+ let canAdd = false; let numProps = 0; let showModal = false
1224
+
1225
+ /* Get number of editors */
1226
+ Object.keys(this.editors).forEach(i => numProps++)
1227
+
1228
+ /* Determine if we can add back removed properties */
1229
+ canAdd = this.canHaveAdditionalProperties() && !(typeof this.schema.maxProperties !== 'undefined' && numProps >= this.schema.maxProperties)
1230
+
1231
+ if (this.addproperty_checkboxes) {
1232
+ this.addproperty_list.innerHTML = ''
1080
1233
  }
1081
- // If additional properties are disabled
1082
- else if(!this.canHaveAdditionalProperties()) {
1083
- this.addproperty_add.style.display = 'none';
1084
- this.addproperty_input.style.display = 'none';
1234
+ this.addproperty_checkboxes = {}
1235
+
1236
+ /* Check for which editors can't be removed or added back */
1237
+ Object.keys(this.cached_editors).forEach(i => {
1238
+ this.addPropertyCheckbox(i)
1239
+
1240
+ if (this.isRequiredObject(this.cached_editors[i]) && i in this.editors) {
1241
+ this.addproperty_checkboxes[i].disabled = true
1242
+ }
1243
+
1244
+ if (typeof this.schema.minProperties !== 'undefined' && numProps <= this.schema.minProperties) {
1245
+ this.addproperty_checkboxes[i].disabled = this.addproperty_checkboxes[i].checked
1246
+ if (!this.addproperty_checkboxes[i].checked) showModal = true
1247
+ } else if (!(i in this.editors)) {
1248
+ if (!canAdd && !hasOwnProperty(this.schema.properties, i)) {
1249
+ this.addproperty_checkboxes[i].disabled = true
1250
+ } else {
1251
+ this.addproperty_checkboxes[i].disabled = false
1252
+ showModal = true
1253
+ }
1254
+ } else {
1255
+ showModal = true
1256
+ }
1257
+ })
1258
+
1259
+ if (this.canHaveAdditionalProperties()) {
1260
+ showModal = true
1085
1261
  }
1086
- // If no new properties can be added
1087
- else if(!can_add) {
1088
- this.addproperty_add.disabled = true;
1262
+
1263
+ /* Additional addproperty checkboxes not tied to a current editor */
1264
+ Object.keys(this.schema.properties).forEach(i => {
1265
+ if (this.cached_editors[i]) return
1266
+ showModal = true
1267
+ this.addPropertyCheckbox(i)
1268
+ })
1269
+
1270
+ /* If no editors can be added or removed, hide the modal button */
1271
+ if (!showModal) {
1272
+ this.hideAddProperty()
1273
+ this.addproperty_button.style.display = 'none'
1274
+ /* If additional properties are disabled */
1275
+ } else if (!this.canHaveAdditionalProperties()) {
1276
+ this.addproperty_add.style.display = 'none'
1277
+ this.addproperty_input.style.display = 'none'
1278
+ /* If no new properties can be added */
1279
+ } else if (!canAdd) {
1280
+ this.addproperty_add.disabled = true
1281
+ /* If new properties can be added */
1282
+ } else {
1283
+ this.addproperty_add.disabled = false
1089
1284
  }
1090
- // If new properties can be added
1091
- else {
1092
- this.addproperty_add.disabled = false;
1285
+ }
1286
+
1287
+ isRequiredObject (editor) {
1288
+ if (!editor) {
1289
+ return
1093
1290
  }
1094
- },
1095
- isRequired: function(editor) {
1096
- if(typeof editor.schema.required === "boolean") return editor.schema.required;
1097
- else if(Array.isArray(this.schema.required)) return this.schema.required.indexOf(editor.key) > -1;
1098
- else if(this.jsoneditor.options.required_by_default) return true;
1099
- else return false;
1100
- },
1101
- setValue: function(value, initial) {
1102
- var self = this;
1103
- value = value || {};
1104
-
1105
- if(typeof value !== "object" || Array.isArray(value)) value = {};
1106
-
1107
- // First, set the values for all of the defined properties
1108
- $each(this.cached_editors, function(i,editor) {
1109
- // Value explicitly set
1110
- if(typeof value[i] !== "undefined") {
1111
- self.addObjectProperty(i);
1112
- editor.setValue(value[i],initial);
1113
- }
1114
- // Otherwise, remove value unless this is the initial set or it's required
1115
- else if(!initial && !self.isRequired(editor)) {
1116
- self.removeObjectProperty(i);
1117
- }
1118
- // Otherwise, set the value to the default
1119
- else {
1120
- editor.setValue(editor.getDefault(),initial);
1121
- }
1122
- });
1123
1291
 
1124
- $each(value, function(i,val) {
1125
- if(!self.cached_editors[i]) {
1126
- self.addObjectProperty(i);
1127
- if(self.editors[i]) self.editors[i].setValue(val,initial);
1128
- }
1129
- });
1130
-
1131
- this.refreshValue();
1132
- this.layoutEditors();
1133
- this.onChange();
1134
- },
1135
- showValidationErrors: function(errors) {
1136
- var self = this;
1137
-
1138
- // Get all the errors that pertain to this editor
1139
- var my_errors = [];
1140
- var other_errors = [];
1141
- $each(errors, function(i,error) {
1142
- if(error.path === self.path) {
1143
- my_errors.push(error);
1292
+ if (typeof editor.schema.required === 'boolean') return editor.schema.required
1293
+ else if (Array.isArray(this.schema.required)) return this.schema.required.includes(editor.key)
1294
+ else if (this.jsoneditor.options.required_by_default) return true
1295
+ return false
1296
+ }
1297
+
1298
+ setValue (value, initial) {
1299
+ value = this.applyConstFilter(value)
1300
+
1301
+ value = value || {}
1302
+
1303
+ if (typeof value !== 'object' || Array.isArray(value)) value = {}
1304
+
1305
+ /* First, set the values for all of the defined properties */
1306
+ Object.entries(this.cached_editors).forEach(([i, editor]) => {
1307
+ /* Value explicitly set */
1308
+ if (typeof value[i] !== 'undefined') {
1309
+ this.addObjectProperty(i)
1310
+ editor.setValue(value[i], initial)
1311
+ editor.activate()
1312
+ if (this.disabled) {
1313
+ editor.disable()
1314
+ }
1315
+ /* Otherwise, remove value unless this is the initial set or it's required */
1316
+ } else if (!initial && !this.isRequiredObject(editor)) {
1317
+ if (this.jsoneditor.options.show_opt_in || this.options.show_opt_in) {
1318
+ editor.deactivate()
1319
+ } else {
1320
+ this.removeObjectProperty(i)
1321
+ }
1322
+ /* Otherwise, set the value to the default */
1323
+ } else {
1324
+ editor.setValue(editor.getDefault(), initial)
1144
1325
  }
1145
- else {
1146
- other_errors.push(error);
1326
+ })
1327
+
1328
+ Object.entries(value).forEach(([i, val]) => {
1329
+ if (!this.cached_editors[i]) {
1330
+ this.addObjectProperty(i)
1331
+ if (this.editors[i]) this.editors[i].setValue(val, initial, !!this.editors[i].template)
1147
1332
  }
1148
- });
1149
-
1150
- // Show errors for this editor
1151
- if(this.error_holder) {
1152
- if(my_errors.length) {
1153
- var message = [];
1154
- this.error_holder.innerHTML = '';
1155
- this.error_holder.style.display = '';
1156
- $each(my_errors, function(i,error) {
1157
- self.error_holder.appendChild(self.theme.getErrorMessage(error.message));
1158
- });
1333
+ })
1334
+
1335
+ this.refreshValue()
1336
+ this.layoutEditors()
1337
+ this.onChange()
1338
+ }
1339
+
1340
+ showValidationErrors (errors) {
1341
+ /* Get all the errors that pertain to this editor */
1342
+ const myErrors = []
1343
+ const otherErrors = []
1344
+ errors.forEach(error => {
1345
+ if (error.path === this.path) {
1346
+ myErrors.push(error)
1347
+ } else {
1348
+ otherErrors.push(error)
1159
1349
  }
1160
- // Hide error area
1161
- else {
1162
- this.error_holder.style.display = 'none';
1350
+ })
1351
+
1352
+ /* Show errors for this editor */
1353
+ if (this.error_holder) {
1354
+ if (myErrors.length) {
1355
+ this.error_holder.innerHTML = ''
1356
+ this.error_holder.style.display = ''
1357
+ myErrors.forEach(error => {
1358
+ if (error.errorcount && error.errorcount > 1) error.message += ` (${error.errorcount} errors)`
1359
+ this.error_holder.appendChild(this.theme.getErrorMessage(error.message))
1360
+ })
1361
+ /* Hide error area */
1362
+ } else {
1363
+ this.error_holder.style.display = 'none'
1163
1364
  }
1164
1365
  }
1165
1366
 
1166
- // Show error for the table row if this is inside a table
1167
- if(this.options.table_row) {
1168
- if(my_errors.length) {
1169
- this.theme.addTableRowError(this.container);
1170
- }
1171
- else {
1172
- this.theme.removeTableRowError(this.container);
1367
+ /* Show error for the table row if this is inside a table */
1368
+ if (this.options.table_row) {
1369
+ if (myErrors.length) {
1370
+ this.theme.addTableRowError(this.container)
1371
+ } else {
1372
+ this.theme.removeTableRowError(this.container)
1173
1373
  }
1174
1374
  }
1175
1375
 
1176
- // Show errors for child editors
1177
- $each(this.editors, function(i,editor) {
1178
- editor.showValidationErrors(other_errors);
1179
- });
1376
+ /* Show errors for child editors */
1377
+ Object.values(this.editors).forEach(editor => {
1378
+ editor.showValidationErrors(otherErrors)
1379
+ })
1180
1380
  }
1181
- });
1381
+ }
1382
+
1383
+ ObjectEditor.rules = rules