@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
package/src/editor.js CHANGED
@@ -1,573 +1,814 @@
1
+ import { extend, hasOwnProperty } from './utilities.js'
2
+
1
3
  /**
2
4
  * All editors should extend from this class
3
5
  */
4
- JSONEditor.AbstractEditor = Class.extend({
5
- onChildEditorChange: function(editor) {
6
- this.onChange(true);
7
- },
8
- notify: function() {
9
- if(this.path) this.jsoneditor.notifyWatchers(this.path);
10
- },
11
- change: function() {
12
- if(this.parent) this.parent.onChildEditorChange(this);
13
- else if(this.jsoneditor) this.jsoneditor.onChange();
14
- },
15
- onChange: function(bubble) {
16
- this.notify();
17
- if(this.watch_listener) this.watch_listener();
18
- if(bubble) this.change();
19
- },
20
- register: function() {
21
- this.jsoneditor.registerEditor(this);
22
- this.onChange();
23
- },
24
- unregister: function() {
25
- if(!this.jsoneditor) return;
26
- this.jsoneditor.unregisterEditor(this);
27
- },
28
- getNumColumns: function() {
29
- return 12;
30
- },
31
- init: function(options) {
32
- this.jsoneditor = options.jsoneditor;
33
-
34
- this.theme = this.jsoneditor.theme;
35
- this.template_engine = this.jsoneditor.template;
36
- this.iconlib = this.jsoneditor.iconlib;
37
-
38
- this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate;
39
-
40
- this.original_schema = options.schema;
41
- this.schema = this.jsoneditor.expandSchema(this.original_schema);
42
-
43
- this.options = $extend({}, (this.options || {}), (this.schema.options || {}), (options.schema.options || {}), options);
44
-
45
- if(!options.path && !this.schema.id) this.schema.id = 'root';
46
- this.path = options.path || 'root';
47
- this.formname = options.formname || this.path.replace(/\.([^.]+)/g,'[$1]');
48
- if(this.jsoneditor.options.form_name_root) this.formname = this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+'[');
49
- this.key = this.path.split('.').pop();
50
- this.parent = options.parent;
51
-
52
- this.link_watchers = [];
53
-
54
- if(options.container) this.setContainer(options.container);
55
- this.registerDependencies();
56
- },
57
- registerDependencies: function() {
58
- this.dependenciesFulfilled = true;
59
- var deps = this.options.dependencies;
6
+ export class AbstractEditor {
7
+ constructor (options, defaults) {
8
+ this.defaults = defaults
9
+ this.jsoneditor = options.jsoneditor
10
+ this.theme = this.jsoneditor.theme
11
+ this.template_engine = this.jsoneditor.template
12
+ this.iconlib = this.jsoneditor.iconlib
13
+ this.translate = this.jsoneditor.translate || this.defaults.translate
14
+ this.translateProperty = this.jsoneditor.translateProperty || this.defaults.translateProperty
15
+ this.original_schema = options.schema
16
+ this.schema = this.jsoneditor.expandSchema(this.original_schema)
17
+ this.active = true
18
+ this.isUiOnly = false
19
+ this.options = extend({}, (this.options || {}), (this.schema.options || {}), (options.schema.options || {}), options)
20
+ this.enforceConstEnabled = this.options.enforce_const ?? this.jsoneditor.options.enforce_const
21
+ this.formname = this.jsoneditor.options.form_name_root || 'root'
22
+
23
+ if (!options.path && !this.schema.id) this.schema.id = this.formname
24
+ this.path = options.path || this.formname
25
+ this.formname = options.formname || this.path.replace(/\.([^.]+)/g, '[$1]')
26
+
27
+ this.parent = options.parent
28
+ this.key = this.parent !== undefined ? this.path.split('.').slice(this.parent.path.split('.').length).join('.') : this.path
29
+
30
+ this.link_watchers = []
31
+ this.watchLoop = false
32
+ this.optInWidget = this.options.opt_in_widget ?? this.jsoneditor.options.opt_in_widget
33
+
34
+ if (options.container) this.setContainer(options.container)
35
+ this.registerDependencies()
36
+ }
37
+
38
+ onChildEditorChange (editor, eventData) {
39
+ this.onChange(true, false, eventData)
40
+ }
41
+
42
+ notify () {
43
+ if (this.path) this.jsoneditor.notifyWatchers(this.path)
44
+ }
45
+
46
+ change (eventData) {
47
+ if (this.parent) this.parent.onChildEditorChange(this, eventData)
48
+ else if (this.jsoneditor) this.jsoneditor.onChange(eventData)
49
+ }
50
+
51
+ onChange (bubble, fromTemplate, eventData) {
52
+ this.notify()
53
+
54
+ if (!fromTemplate) {
55
+ if (this.watch_listener) this.watch_listener()
56
+ }
57
+
58
+ if (bubble) this.change(eventData)
59
+ }
60
+
61
+ register () {
62
+ this.jsoneditor.registerEditor(this)
63
+ if (this.input && !this.label) {
64
+ const ariaLabel = this.getTitle() || this.formname
65
+ this.input.setAttribute('aria-label', ariaLabel)
66
+ }
67
+ this.onChange()
68
+ }
69
+
70
+ unregister () {
71
+ if (!this.jsoneditor) return
72
+ this.jsoneditor.unregisterEditor(this)
73
+ }
74
+
75
+ getNumColumns () {
76
+ return 12
77
+ }
78
+
79
+ isActive () {
80
+ return this.active
81
+ }
82
+
83
+ activate () {
84
+ this.active = true
85
+ this.optInCheckbox.checked = true
86
+ this.enable()
87
+ this.change()
88
+ }
89
+
90
+ deactivate () {
91
+ /* only non required properties can be deactivated. */
92
+ if (!this.isRequired()) {
93
+ this.active = false
94
+ this.optInCheckbox.checked = false
95
+ this.disable()
96
+ this.change()
97
+ }
98
+ }
99
+
100
+ registerDependencies () {
101
+ this.dependenciesFulfilled = true
102
+ const deps = this.options.dependencies
60
103
  if (!deps) {
61
- return;
62
- }
63
-
64
- var self = this;
65
- Object.keys(deps).forEach(function(dependency) {
66
- var path = self.path.split('.');
67
- path[path.length - 1] = dependency;
68
- path = path.join('.');
69
- var choices = deps[dependency];
70
- self.jsoneditor.watch(path, function() {
71
- self.checkDependency(path, choices);
72
- });
73
- });
74
- },
75
- checkDependency: function(path, choices) {
76
- var wrapper = this.control || this.container;
77
- if (this.path === path || !wrapper) {
78
- return;
79
- }
80
-
81
- var self = this;
82
- var editor = this.jsoneditor.getEditor(path);
83
- var value = editor ? editor.getValue() : undefined;
84
- var previousStatus = this.dependenciesFulfilled;
85
- this.dependenciesFulfilled = false;
86
-
87
- if (!editor || !editor.dependenciesFulfilled) {
88
- this.dependenciesFulfilled = false;
104
+ return
105
+ }
106
+
107
+ Object.keys(deps).forEach(dependency => {
108
+ let path
109
+ const isFullPath = dependency.startsWith(this.jsoneditor.root.path)
110
+
111
+ if (isFullPath) {
112
+ path = dependency
113
+ } else {
114
+ path = this.path.split('.')
115
+ path[path.length - 1] = dependency
116
+ path = path.join('.')
117
+ }
118
+
119
+ this.jsoneditor.watch(path, () => {
120
+ this.evaluateDependencies()
121
+ })
122
+ })
123
+ }
124
+
125
+ evaluateDependencies () {
126
+ const wrapper = this.container || this.control
127
+ if (!wrapper || this.jsoneditor === null) {
128
+ return
129
+ }
130
+
131
+ const deps = this.options.dependencies
132
+ if (!deps) {
133
+ return
134
+ }
135
+
136
+ // Assume true and set to false if any unmet dependencies are found
137
+ const previousStatus = this.dependenciesFulfilled
138
+ this.dependenciesFulfilled = true
139
+
140
+ Object.keys(deps).forEach(dependency => {
141
+ let path
142
+ const isFullPath = dependency.startsWith(this.jsoneditor.root.path)
143
+
144
+ if (isFullPath) {
145
+ path = dependency
146
+ } else {
147
+ path = this.path.split('.')
148
+ path[path.length - 1] = dependency
149
+ path = path.join('.')
150
+ }
151
+
152
+ const choices = deps[dependency]
153
+ this.checkDependency(path, choices)
154
+ })
155
+
156
+ if (this.dependenciesFulfilled !== previousStatus) {
157
+ this.notify()
158
+ }
159
+
160
+ let displayMode = this.dependenciesFulfilled ? 'block' : 'none'
161
+
162
+ if (this.options.hidden) {
163
+ displayMode = 'none'
164
+ }
165
+
166
+ if (wrapper.tagName === 'TD') {
167
+ Object.keys(wrapper.childNodes).forEach(child => (wrapper.childNodes[child].style.display = displayMode))
168
+ } else wrapper.style.display = displayMode
169
+ }
170
+
171
+ checkDependency (path, choices) {
172
+ if (this.path === path || this.jsoneditor === null) {
173
+ return
174
+ }
175
+
176
+ const editor = this.jsoneditor.getEditor(path)
177
+ const value = editor ? editor.getValue() : undefined
178
+
179
+ if (!editor || !editor.dependenciesFulfilled || value === undefined || value === null) {
180
+ this.dependenciesFulfilled = false
89
181
  } else if (Array.isArray(choices)) {
90
- choices.some(function(choice) {
91
- if (value === choice) {
92
- self.dependenciesFulfilled = true;
93
- return true;
182
+ this.dependenciesFulfilled = choices.some(choice => {
183
+ if (JSON.stringify(value) === JSON.stringify(choice)) {
184
+ return true
94
185
  }
95
- });
186
+ })
96
187
  } else if (typeof choices === 'object') {
97
188
  if (typeof value !== 'object') {
98
- this.dependenciesFulfilled = choices === value;
189
+ this.dependenciesFulfilled = choices === value
99
190
  } else {
100
- Object.keys(choices).some(function(key) {
101
- if (!choices.hasOwnProperty(key)) {
102
- return false;
191
+ Object.keys(choices).some(key => {
192
+ if (!hasOwnProperty(choices, key)) {
193
+ return false
103
194
  }
104
- if (!value.hasOwnProperty(key) || choices[key] !== value[key]) {
105
- self.dependenciesFulfilled = false;
106
- return true;
195
+ if (!hasOwnProperty(value, key) || choices[key] !== value[key]) {
196
+ this.dependenciesFulfilled = false
197
+ return true
107
198
  }
108
- self.dependenciesFulfilled = true;
109
- });
199
+ })
110
200
  }
111
201
  } else if (typeof choices === 'string' || typeof choices === 'number') {
112
- this.dependenciesFulfilled = value === choices;
202
+ this.dependenciesFulfilled = this.dependenciesFulfilled && value === choices
113
203
  } else if (typeof choices === 'boolean') {
114
204
  if (choices) {
115
- this.dependenciesFulfilled = value && value.length > 0;
205
+ this.dependenciesFulfilled = this.dependenciesFulfilled && (value || value.length > 0)
116
206
  } else {
117
- this.dependenciesFulfilled = !value || value.length === 0;
207
+ this.dependenciesFulfilled = this.dependenciesFulfilled && (!value || value.length === 0)
118
208
  }
119
209
  }
120
-
121
- if (this.dependenciesFulfilled !== previousStatus) {
122
- this.notify();
123
- }
124
-
125
- if (this.dependenciesFulfilled) {
126
- wrapper.style.display = 'block';
210
+ }
211
+
212
+ setContainer (container) {
213
+ this.container = container
214
+ this.setContainerAttributes()
215
+ if (this.schema.id) this.container.setAttribute('data-schemaid', this.schema.id)
216
+ if (this.schema.type && typeof this.schema.type === 'string') this.container.setAttribute('data-schematype', this.schema.type)
217
+ this.container.setAttribute('data-schemapath', this.path)
218
+ }
219
+
220
+ setOptInCheckbox () {
221
+ let optIn
222
+
223
+ if (this.optInWidget === 'switch') {
224
+ optIn = this.theme.getOptInSwitch(this.formname)
127
225
  } else {
128
- wrapper.style.display = 'none';
129
- }
130
- },
131
- setContainer: function(container) {
132
- this.container = container;
133
- if(this.schema.id) this.container.setAttribute('data-schemaid',this.schema.id);
134
- if(this.schema.type && typeof this.schema.type === "string") this.container.setAttribute('data-schematype',this.schema.type);
135
- this.container.setAttribute('data-schemapath',this.path);
136
- },
137
-
138
- preBuild: function() {
139
-
140
- },
141
- build: function() {
142
-
143
- },
144
- postBuild: function() {
145
- this.setupWatchListeners();
146
- this.addLinks();
147
- this.setValue(this.getDefault(), true);
148
- this.updateHeaderText();
149
- this.register();
150
- this.onWatchedFieldChange();
151
- },
152
-
153
- setupWatchListeners: function() {
154
- var self = this;
155
-
156
- // Watched fields
157
- this.watched = {};
158
- if(this.schema.vars) this.schema.watch = this.schema.vars;
159
- this.watched_values = {};
160
- this.watch_listener = function() {
161
- if(self.refreshWatchedFieldValues()) {
162
- self.onWatchedFieldChange();
226
+ optIn = this.theme.getOptInCheckbox(this.formname)
227
+ }
228
+
229
+ this.optInCheckbox = optIn.checkbox
230
+ this.optInContainer = optIn.container
231
+
232
+ this.optInCheckbox.addEventListener('click', () => {
233
+ if (this.isActive()) {
234
+ this.deactivate()
235
+ } else {
236
+ this.activate()
163
237
  }
164
- };
165
-
166
- if(this.schema.hasOwnProperty('watch')) {
167
- var path,path_parts,first,root,adjusted_path;
168
-
169
- for(var name in this.schema.watch) {
170
- if(!this.schema.watch.hasOwnProperty(name)) continue;
171
- path = this.schema.watch[name];
172
-
173
- if(Array.isArray(path)) {
174
- if(path.length<2) continue;
175
- path_parts = [path[0]].concat(path[1].split('.'));
176
- }
177
- else {
178
- path_parts = path.split('.');
179
- if(!self.theme.closest(self.container,'[data-schemaid="'+path_parts[0]+'"]')) path_parts.unshift('#');
180
- }
181
- first = path_parts.shift();
238
+ })
239
+
240
+ /* append active/deactive checkbox if show_opt_in is true */
241
+ const globalOptIn = this.jsoneditor.options.show_opt_in
242
+ const parentOptInDefined = (typeof this.parent.options.show_opt_in !== 'undefined')
243
+ const parentOptInEnabled = (parentOptInDefined && this.parent.options.show_opt_in === true)
244
+ const parentOptInDisabled = (parentOptInDefined && this.parent.options.show_opt_in === false)
182
245
 
183
- if(first === '#') first = self.jsoneditor.schema.id || 'root';
246
+ if (parentOptInEnabled || (!parentOptInDisabled && globalOptIn) || (!parentOptInDefined && globalOptIn)) {
247
+ /* and control to type object editors if they are not required */
248
+ if (this.parent && this.parent.schema.type === 'object' && !this.isRequired() && this.header) {
249
+ this.header.insertBefore(this.optInContainer, this.header.firstChild)
250
+ this.optInAppended = true
251
+ }
252
+ }
253
+ }
254
+
255
+ preBuild () {
184
256
 
185
- // Find the root node for this template variable
186
- root = self.theme.closest(self.container,'[data-schemaid="'+first+'"]');
187
- if(!root) throw "Could not find ancestor node with id "+first;
257
+ }
258
+
259
+ build () {
260
+
261
+ }
188
262
 
189
- // Keep track of the root node and path for use when rendering the template
190
- adjusted_path = root.getAttribute('data-schemapath') + '.' + path_parts.join('.');
191
-
192
- self.jsoneditor.watch(adjusted_path,self.watch_listener);
193
-
194
- self.watched[name] = adjusted_path;
263
+ postBuild () {
264
+ this.setupWatchListeners()
265
+ this.addLinks()
266
+ this.register()
267
+ this.setValue(this.getDefault(), true)
268
+ this.updateHeaderText()
269
+ this.onWatchedFieldChange()
270
+
271
+ if (this.options.titleHidden) {
272
+ this.theme.visuallyHidden(this.label)
273
+ this.theme.visuallyHidden(this.header)
274
+ }
275
+
276
+ if (this.enforceConstEnabled && this.schema.const) {
277
+ this.disable()
278
+ }
279
+ }
280
+
281
+ setupWatchListeners () {
282
+ /* Watched fields */
283
+ this.watched = {}
284
+ if (this.schema.vars) this.schema.watch = this.schema.vars
285
+ this.watched_values = {}
286
+ this.watch_listener = () => {
287
+ if (this.refreshWatchedFieldValues()) {
288
+ this.onWatchedFieldChange()
195
289
  }
196
290
  }
197
-
198
- // Dynamic header
199
- if(this.schema.headerTemplate) {
200
- this.header_template = this.jsoneditor.compileTemplate(this.schema.headerTemplate, this.template_engine);
201
- }
202
- },
203
-
204
- addLinks: function() {
205
- // Add links
206
- if(!this.no_link_holder) {
207
- this.link_holder = this.theme.getLinksHolder();
208
- this.container.appendChild(this.link_holder);
209
- if(this.schema.links) {
210
- for(var i=0; i<this.schema.links.length; i++) {
211
- this.addLink(this.getLink(this.schema.links[i]));
291
+
292
+ if (hasOwnProperty(this.schema, 'watch')) {
293
+ let path; let pathParts; let first; let root; let adjustedPath
294
+ const myPath = this.container.getAttribute('data-schemapath')
295
+
296
+ Object.keys(this.schema.watch).forEach(name => {
297
+ path = this.schema.watch[name]
298
+ if (Array.isArray(path)) {
299
+ if (path.length < 2) return
300
+ pathParts = [path[0]].concat(path[1].split('.'))
301
+ } else {
302
+ pathParts = path.split('.')
303
+ if (!this.theme.closest(this.container, `[data-schemaid="${pathParts[0]}"]`)) pathParts.unshift('#')
304
+ }
305
+ first = pathParts.shift()
306
+
307
+ if (first === '#') first = this.jsoneditor.schema.id || this.jsoneditor.root.formname
308
+
309
+ /* Find the root node for this template variable */
310
+ root = this.theme.closest(this.container, `[data-schemaid="${first}"]`)
311
+ if (!root) throw new Error(`Could not find ancestor node with id ${first}`)
312
+
313
+ /* Keep track of the root node and path for use when rendering the template */
314
+ adjustedPath = `${root.getAttribute('data-schemapath')}.${pathParts.join('.')}`
315
+
316
+ if (myPath.startsWith(adjustedPath)) this.watchLoop = true
317
+ this.jsoneditor.watch(adjustedPath, this.watch_listener)
318
+
319
+ this.watched[name] = adjustedPath
320
+ })
321
+ }
322
+
323
+ /* Dynamic header */
324
+ if (this.schema.headerTemplate) {
325
+ this.header_template = this.jsoneditor.compileTemplate(this.schema.headerTemplate, this.template_engine)
326
+ }
327
+ }
328
+
329
+ addLinks () {
330
+ /* Add links */
331
+ if (!this.no_link_holder) {
332
+ this.link_holder = this.theme.getLinksHolder()
333
+ /* if description element exists, insert the link before */
334
+ if (typeof this.description !== 'undefined') this.description.parentNode.insertBefore(this.link_holder, this.description)
335
+ /* otherwise just insert link at bottom of container */
336
+ else this.container.appendChild(this.link_holder)
337
+ if (this.schema.links) {
338
+ for (let i = 0; i < this.schema.links.length; i++) {
339
+ this.addLink(this.getLink(this.schema.links[i]))
212
340
  }
213
341
  }
214
342
  }
215
- },
216
- onMove: function() {},
217
- getButton: function(text, icon, title) {
218
- var btnClass = 'json-editor-btn-'+icon;
219
- if(!this.iconlib) icon = null;
220
- else icon = this.iconlib.getIcon(icon);
221
-
222
- if(!icon && title) {
223
- text = title;
224
- title = null;
225
- }
226
-
227
- var btn = this.theme.getButton(text, icon, title);
228
- btn.classList.add(btnClass);
229
- return btn;
230
- },
231
- setButtonText: function(button, text, icon, title) {
232
- if(!this.iconlib) icon = null;
233
- else icon = this.iconlib.getIcon(icon);
234
-
235
- if(!icon && title) {
236
- text = title;
237
- title = null;
238
- }
239
-
240
- return this.theme.setButtonText(button, text, icon, title);
241
- },
242
- addLink: function(link) {
243
- if(this.link_holder) this.link_holder.appendChild(link);
244
- },
245
- getLink: function(data) {
246
- var holder, link;
247
-
248
- // Get mime type of the link
249
- var mime = data.mediaType || 'application/javascript';
250
- var type = mime.split('/')[0];
251
-
252
- // Template to generate the link href
253
- var href = this.jsoneditor.compileTemplate(data.href,this.template_engine);
254
- var relTemplate = this.jsoneditor.compileTemplate(data.rel ? data.rel : data.href,this.template_engine);
255
-
256
- // Template to generate the link's download attribute
257
- var download = null;
258
- if(data.download) download = data.download;
259
-
260
- if(download && download !== true) {
261
- download = this.jsoneditor.compileTemplate(download, this.template_engine);
262
- }
263
-
264
- // Image links
265
- if(type === 'image') {
266
- holder = this.theme.getBlockLinkHolder();
267
- link = document.createElement('a');
268
- link.setAttribute('target','_blank');
269
- var image = document.createElement('img');
270
-
271
- this.theme.createImageLink(holder,link,image);
272
-
273
- // When a watched field changes, update the url
274
- this.link_watchers.push(function(vars) {
275
- var url = href(vars);
276
- var rel = relTemplate(vars);
277
- link.setAttribute('href',url);
278
- link.setAttribute('title',rel || url);
279
- image.setAttribute('src',url);
280
- });
281
- }
282
- // Audio/Video links
283
- else if(['audio','video'].indexOf(type) >=0) {
284
- holder = this.theme.getBlockLinkHolder();
285
-
286
- link = this.theme.getBlockLink();
287
- link.setAttribute('target','_blank');
288
-
289
- var media = document.createElement(type);
290
- media.setAttribute('controls','controls');
291
-
292
- this.theme.createMediaLink(holder,link,media);
293
-
294
- // When a watched field changes, update the url
295
- this.link_watchers.push(function(vars) {
296
- var url = href(vars);
297
- var rel = relTemplate(vars);
298
- link.setAttribute('href',url);
299
- link.textContent = rel || url;
300
- media.setAttribute('src',url);
301
- });
302
- }
303
- // Text links
304
- else {
305
- link = holder = this.theme.getBlockLink();
306
- holder.setAttribute('target','_blank');
307
- holder.textContent = data.rel;
308
-
309
- // When a watched field changes, update the url
310
- this.link_watchers.push(function(vars) {
311
- var url = href(vars);
312
- var rel = relTemplate(vars);
313
- holder.setAttribute('href',url);
314
- holder.textContent = rel || url;
315
- });
316
- }
317
-
318
- if(download && link) {
319
- if(download === true) {
320
- link.setAttribute('download','');
321
- }
322
- else {
323
- this.link_watchers.push(function(vars) {
324
- link.setAttribute('download',download(vars));
325
- });
326
- }
343
+ }
344
+
345
+ onMove () {}
346
+
347
+ getButton (text, icon, title, args = []) {
348
+ const btnClass = `json-editor-btn-${icon}`
349
+ if (!this.iconlib) icon = null
350
+ else icon = this.iconlib.getIcon(icon)
351
+
352
+ text = this.translate(text, args)
353
+ title = this.translate(title, args)
354
+
355
+ if (!icon && title) {
356
+ text = title
357
+ title = null
358
+ }
359
+
360
+ const btn = this.theme.getButton(text, icon, title)
361
+ btn.classList.add(btnClass)
362
+ return btn
363
+ }
364
+
365
+ setButtonText (button, text, icon, title, args = []) {
366
+ if (!this.iconlib) icon = null
367
+ else icon = this.iconlib.getIcon(icon)
368
+
369
+ text = this.translate(text, args)
370
+ title = this.translate(title, args)
371
+
372
+ if (!icon && title) {
373
+ text = title
374
+ title = null
375
+ }
376
+
377
+ return this.theme.setButtonText(button, text, icon, title)
378
+ }
379
+
380
+ addLink (link) {
381
+ if (this.link_holder) this.link_holder.appendChild(link)
382
+ }
383
+
384
+ getLink (data) {
385
+ let holder
386
+ let link
387
+
388
+ /* Get mime type of the link */
389
+ const mime = data.mediaType || 'application/javascript'
390
+ const type = mime.split('/')[0]
391
+
392
+ /* Template to generate the link href */
393
+ const href = this.jsoneditor.compileTemplate(data.href, this.template_engine)
394
+ const relTemplate = this.jsoneditor.compileTemplate(data.rel ? data.rel : data.href, this.template_engine)
395
+
396
+ /* Template to generate the link's download attribute */
397
+ let download = null
398
+ if (data.download) download = data.download
399
+
400
+ if (download && download !== true) {
401
+ download = this.jsoneditor.compileTemplate(download, this.template_engine)
402
+ }
403
+
404
+ /* Image links */
405
+ if (type === 'image') {
406
+ holder = this.theme.getBlockLinkHolder()
407
+ link = document.createElement('a')
408
+ link.setAttribute('target', '_blank')
409
+ const image = document.createElement('img')
410
+
411
+ this.theme.createImageLink(holder, link, image)
412
+
413
+ /* When a watched field changes, update the url */
414
+ this.link_watchers.push(vars => {
415
+ const url = href(vars)
416
+ const rel = relTemplate(vars)
417
+ link.setAttribute('href', url)
418
+ link.setAttribute('title', rel || url)
419
+ image.setAttribute('src', url)
420
+ })
421
+ /* Audio/Video links */
422
+ } else if (['audio', 'video'].includes(type)) {
423
+ holder = this.theme.getBlockLinkHolder()
424
+
425
+ link = this.theme.getBlockLink()
426
+ link.setAttribute('target', '_blank')
427
+
428
+ const media = document.createElement(type)
429
+ media.setAttribute('controls', 'controls')
430
+
431
+ this.theme.createMediaLink(holder, link, media)
432
+
433
+ /* When a watched field changes, update the url */
434
+ this.link_watchers.push(vars => {
435
+ const url = href(vars)
436
+ const rel = relTemplate(vars)
437
+ link.setAttribute('href', url)
438
+ link.textContent = rel || url
439
+ media.setAttribute('src', url)
440
+ })
441
+ /* Text links or blank link */
442
+ } else {
443
+ link = holder = this.theme.getBlockLink()
444
+ holder.setAttribute('target', '_blank')
445
+ holder.textContent = data.rel
446
+ holder.style.display = 'none' /* Prevent blank links from showing up when using custom view */
447
+
448
+ /* When a watched field changes, update the url */
449
+ this.link_watchers.push(vars => {
450
+ const url = href(vars)
451
+ const rel = relTemplate(vars)
452
+ if (url) holder.style.display = ''
453
+ holder.setAttribute('href', url)
454
+ holder.textContent = rel || url
455
+ })
327
456
  }
328
-
329
- if(data.class) link.classList.add(data.class);
330
-
331
- return holder;
332
- },
333
- refreshWatchedFieldValues: function() {
334
- if(!this.watched_values) return;
335
- var watched = {};
336
- var changed = false;
337
- var self = this;
338
-
339
- if(this.watched) {
340
- var val,editor;
341
- for(var name in this.watched) {
342
- if(!this.watched.hasOwnProperty(name)) continue;
343
- editor = self.jsoneditor.getEditor(this.watched[name]);
344
- val = editor? editor.getValue() : null;
345
- if(self.watched_values[name] !== val) changed = true;
346
- watched[name] = val;
457
+
458
+ if (download && link) {
459
+ if (download === true) {
460
+ link.setAttribute('download', '')
461
+ } else {
462
+ this.link_watchers.push(vars => {
463
+ link.setAttribute('download', download(vars))
464
+ })
347
465
  }
348
466
  }
349
-
350
- watched.self = this.getValue();
351
- if(this.watched_values.self !== watched.self) changed = true;
352
-
353
- this.watched_values = watched;
354
-
355
- return changed;
356
- },
357
- getWatchedFieldValues: function() {
358
- return this.watched_values;
359
- },
360
- updateHeaderText: function() {
361
- if(this.header) {
362
- // If the header has children, only update the text node's value
363
- if(this.header.children.length) {
364
- for(var i=0; i<this.header.childNodes.length; i++) {
365
- if(this.header.childNodes[i].nodeType===3) {
366
- this.header.childNodes[i].nodeValue = this.getHeaderText();
367
- break;
467
+
468
+ if (data.class) {
469
+ const classNames = data.class.split(' ')
470
+
471
+ classNames.forEach((className) => {
472
+ link.classList.add(className)
473
+ })
474
+ }
475
+
476
+ return holder
477
+ }
478
+
479
+ refreshWatchedFieldValues () {
480
+ if (!this.watched_values) return
481
+ const watched = {}
482
+ let changed = false
483
+
484
+ if (this.watched) {
485
+ Object.keys(this.watched).forEach(name => {
486
+ const editor = this.jsoneditor.getEditor(this.watched[name])
487
+ const val = editor ? editor.getValue() : null
488
+ if (this.watched_values[name] !== val) changed = true
489
+ watched[name] = val
490
+ })
491
+ }
492
+
493
+ watched.self = this.getValue()
494
+ if (this.watched_values.self !== watched.self) changed = true
495
+
496
+ this.watched_values = watched
497
+
498
+ return changed
499
+ }
500
+
501
+ getWatchedFieldValues () {
502
+ return this.watched_values
503
+ }
504
+
505
+ updateHeaderText () {
506
+ if (this.header) {
507
+ const headerText = this.getHeaderText()
508
+ /* If the header has children, only update the text node's value */
509
+ if (this.header.children.length) {
510
+ for (let i = 0; i < this.header.childNodes.length; i++) {
511
+ if (this.header.childNodes[i].nodeType === 3) {
512
+ this.header.childNodes[i].nodeValue = this.cleanText(headerText)
513
+ break
368
514
  }
369
515
  }
516
+ /* Otherwise, just update the entire node */
517
+ } else {
518
+ if (window.DOMPurify) this.header.innerHTML = window.DOMPurify.sanitize(headerText)
519
+ else this.header.textContent = this.cleanText(headerText)
370
520
  }
371
- // Otherwise, just update the entire node
372
- else {
373
- this.header.textContent = this.getHeaderText();
374
- }
375
521
  }
376
- },
377
- getHeaderText: function(title_only) {
378
- if(this.header_text) return this.header_text;
379
- else if(title_only) return this.schema.title;
380
- else return this.getTitle();
381
- },
382
- onWatchedFieldChange: function() {
383
- var vars;
384
- if(this.header_template) {
385
- vars = $extend(this.getWatchedFieldValues(),{
522
+ }
523
+
524
+ purify (val) {
525
+ if (typeof val !== 'string') {
526
+ return val
527
+ }
528
+
529
+ if (window.DOMPurify) {
530
+ val = window.DOMPurify.sanitize(val)
531
+ } else {
532
+ val = this.cleanText(val)
533
+ }
534
+
535
+ return val
536
+ }
537
+
538
+ getHeaderText (titleOnly) {
539
+ if (this.header_text) return this.header_text
540
+ else if (titleOnly) return this.translateProperty(this.schema.title)
541
+ else return this.getTitle()
542
+ }
543
+
544
+ getPathDepth () {
545
+ return this.path.split('.').length
546
+ }
547
+
548
+ cleanText (txt) {
549
+ /* Clean out HTML tags from txt */
550
+ const tmp = document.createElement('div')
551
+ tmp.innerHTML = txt
552
+ return (tmp.textContent || tmp.innerText)
553
+ }
554
+
555
+ onWatchedFieldChange () {
556
+ let vars
557
+
558
+ if (this.header_template) {
559
+ vars = extend(this.getWatchedFieldValues(), {
386
560
  key: this.key,
387
561
  i: this.key,
388
- i0: (this.key*1),
389
- i1: (this.key*1+1),
562
+ i0: (this.key * 1),
563
+ i1: (this.key * 1 + 1),
390
564
  title: this.getTitle()
391
- });
392
- var header_text = this.header_template(vars);
393
-
394
- if(header_text !== this.header_text) {
395
- this.header_text = header_text;
396
- this.updateHeaderText();
397
- this.notify();
398
- //this.fireChangeHeaderEvent();
565
+ })
566
+
567
+ // object properties
568
+ if (this.editors && Object.keys(this.editors).length) {
569
+ vars.properties = {}
570
+
571
+ Object.keys(this.editors).forEach((key) => {
572
+ const editor = this.editors[key]
573
+
574
+ if (editor.schema && editor.schema.enum && editor.schema.options && editor.schema.options.enum_titles) {
575
+ const enumIndex = editor.schema.enum.indexOf(editor.value)
576
+ const enumTitle = editor.options.enum_titles[enumIndex]
577
+ vars.properties[key] = {
578
+ enumTitle
579
+ }
580
+ }
581
+ })
582
+ }
583
+
584
+ const headerText = this.header_template(vars)
585
+
586
+ if (headerText !== this.header_text) {
587
+ this.header_text = headerText
588
+ this.updateHeaderText()
589
+ this.notify()
590
+ /* this.fireChangeHeaderEvent(); */
399
591
  }
400
592
  }
401
- if(this.link_watchers.length) {
402
- vars = this.getWatchedFieldValues();
403
- for(var i=0; i<this.link_watchers.length; i++) {
404
- this.link_watchers[i](vars);
593
+ if (this.link_watchers.length) {
594
+ vars = this.getWatchedFieldValues()
595
+ for (let i = 0; i < this.link_watchers.length; i++) {
596
+ this.link_watchers[i](vars)
405
597
  }
406
598
  }
407
- },
408
- setValue: function(value) {
409
- this.value = value;
410
- },
411
- getValue: function() {
599
+ }
600
+
601
+ setValue (value) {
602
+ value = this.applyConstFilter(value)
603
+ this.value = value
604
+ }
605
+
606
+ applyConstFilter (value) {
607
+ if (this.enforceConstEnabled && typeof this.schema.const !== 'undefined') {
608
+ value = this.schema.const
609
+ }
610
+
611
+ return value
612
+ }
613
+
614
+ getValue () {
412
615
  if (!this.dependenciesFulfilled) {
413
- return undefined;
414
- }
415
- return this.value;
416
- },
417
- refreshValue: function() {
418
-
419
- },
420
- getChildEditors: function() {
421
- return false;
422
- },
423
- destroy: function() {
424
- var self = this;
425
- this.unregister(this);
426
- $each(this.watched,function(name,adjusted_path) {
427
- self.jsoneditor.unwatch(adjusted_path,self.watch_listener);
428
- });
429
- this.watched = null;
430
- this.watched_values = null;
431
- this.watch_listener = null;
432
- this.header_text = null;
433
- this.header_template = null;
434
- this.value = null;
435
- if(this.container && this.container.parentNode) this.container.parentNode.removeChild(this.container);
436
- this.container = null;
437
- this.jsoneditor = null;
438
- this.schema = null;
439
- this.path = null;
440
- this.key = null;
441
- this.parent = null;
442
- },
443
- getDefault: function() {
444
- if (typeof this.schema["default"] !== 'undefined') {
445
- return this.schema["default"];
446
- }
447
-
448
- if (typeof this.schema["enum"] !== 'undefined') {
449
- return this.schema["enum"][0];
450
- }
451
-
452
- var type = this.schema.type || this.schema.oneOf;
453
- if(type && Array.isArray(type)) type = type[0];
454
- if(type && typeof type === "object") type = type.type;
455
- if(type && Array.isArray(type)) type = type[0];
456
-
457
- if(typeof type === "string") {
458
- if(type === "number") return 0.0;
459
- if(type === "boolean") return false;
460
- if(type === "integer") return 0;
461
- if(type === "string") return "";
462
- if(type === "object") return {};
463
- if(type === "array") return [];
464
- }
465
-
466
- return null;
467
- },
468
- getTitle: function() {
469
- return this.schema.title || this.key;
470
- },
471
- enable: function() {
472
- this.disabled = false;
473
- },
474
- disable: function() {
475
- this.disabled = true;
476
- },
477
- isEnabled: function() {
478
- return !this.disabled;
479
- },
480
- isRequired: function() {
481
- if(typeof this.schema.required === "boolean") return this.schema.required;
482
- else if(this.parent && this.parent.schema && Array.isArray(this.parent.schema.required)) return this.parent.schema.required.indexOf(this.key) > -1;
483
- else if(this.jsoneditor.options.required_by_default) return true;
484
- else return false;
485
- },
486
- getDisplayText: function(arr) {
487
- var disp = [];
488
- var used = {};
489
-
490
- // Determine how many times each attribute name is used.
491
- // This helps us pick the most distinct display text for the schemas.
492
- $each(arr,function(i,el) {
493
- if(el.title) {
494
- used[el.title] = used[el.title] || 0;
495
- used[el.title]++;
616
+ return undefined
617
+ }
618
+ return this.value
619
+ }
620
+
621
+ refreshValue () {
622
+
623
+ }
624
+
625
+ getChildEditors () {
626
+ return false
627
+ }
628
+
629
+ destroy () {
630
+ this.unregister(this)
631
+ if (this.watched) {
632
+ Object.values(this.watched).forEach(adjustedPath => this.jsoneditor.unwatch(adjustedPath, this.watch_listener))
633
+ }
634
+
635
+ this.watched = null
636
+ this.watched_values = null
637
+ this.watch_listener = null
638
+ this.header_text = null
639
+ this.header_template = null
640
+ this.value = null
641
+ if (this.container && this.container.parentNode) this.container.parentNode.removeChild(this.container)
642
+ this.container = null
643
+ this.jsoneditor = null
644
+ this.schema = null
645
+ this.path = null
646
+ this.key = null
647
+ this.parent = null
648
+ }
649
+
650
+ isDefaultRequired () {
651
+ return this.isRequired() || !!this.jsoneditor.options.use_default_values
652
+ }
653
+
654
+ getDefault () {
655
+ if (this.enforceConstEnabled && this.schema.const) {
656
+ return this.schema.const
657
+ }
658
+
659
+ if (typeof this.schema.default !== 'undefined') {
660
+ return this.schema.default
661
+ }
662
+
663
+ if (typeof this.schema.enum !== 'undefined') {
664
+ return this.schema.enum[0]
665
+ }
666
+
667
+ let type = this.schema.type || this.schema.oneOf
668
+ if (type && Array.isArray(type)) type = type[0]
669
+ if (type && typeof type === 'object') type = type.type
670
+ if (type && Array.isArray(type)) type = type[0]
671
+
672
+ if (typeof type === 'string') {
673
+ if (type === 'number') return this.isDefaultRequired() ? 0.0 : undefined
674
+ if (type === 'boolean') return this.isDefaultRequired() ? false : undefined
675
+ if (type === 'integer') return this.isDefaultRequired() ? 0 : undefined
676
+ if (type === 'string') return this.isDefaultRequired() ? '' : undefined
677
+ if (type === 'null') return null
678
+ if (type === 'object') return {}
679
+ if (type === 'array') return []
680
+ }
681
+
682
+ return undefined
683
+ }
684
+
685
+ getTitle () {
686
+ return this.translateProperty(this.schema.title || this.key || this.formname)
687
+ }
688
+
689
+ enable () {
690
+ this.disabled = false
691
+ }
692
+
693
+ disable () {
694
+ this.disabled = true
695
+ }
696
+
697
+ isEnabled () {
698
+ return !this.disabled
699
+ }
700
+
701
+ isRequired () {
702
+ if (typeof this.schema.required === 'boolean') return this.schema.required
703
+ else if (this.parent && this.parent.schema && Array.isArray(this.parent.schema.required)) return this.parent.schema.required.includes(this.key)
704
+ else if (this.jsoneditor.options.required_by_default) return true
705
+ else return false
706
+ }
707
+
708
+ getDisplayText (arr) {
709
+ const disp = []
710
+ const used = {}
711
+
712
+ /* Determine how many times each attribute name is used. */
713
+ /* This helps us pick the most distinct display text for the schemas. */
714
+ arr.forEach(el => {
715
+ if (el.title) {
716
+ used[el.title] = used[el.title] || 0
717
+ used[el.title]++
496
718
  }
497
- if(el.description) {
498
- used[el.description] = used[el.description] || 0;
499
- used[el.description]++;
719
+ if (el.description) {
720
+ used[el.description] = used[el.description] || 0
721
+ used[el.description]++
500
722
  }
501
- if(el.format) {
502
- used[el.format] = used[el.format] || 0;
503
- used[el.format]++;
723
+ if (el.format) {
724
+ used[el.format] = used[el.format] || 0
725
+ used[el.format]++
504
726
  }
505
- if(el.type) {
506
- used[el.type] = used[el.type] || 0;
507
- used[el.type]++;
727
+ if (el.type) {
728
+ used[el.type] = used[el.type] || 0
729
+ used[el.type]++
508
730
  }
509
- });
510
-
511
- // Determine display text for each element of the array
512
- $each(arr,function(i,el) {
513
- var name;
514
-
515
- // If it's a simple string
516
- if(typeof el === "string") name = el;
517
- // Object
518
- else if(el.title && used[el.title]<=1) name = el.title;
519
- else if(el.format && used[el.format]<=1) name = el.format;
520
- else if(el.type && used[el.type]<=1) name = el.type;
521
- else if(el.description && used[el.description]<=1) name = el.descripton;
522
- else if(el.title) name = el.title;
523
- else if(el.format) name = el.format;
524
- else if(el.type) name = el.type;
525
- else if(el.description) name = el.description;
526
- else if(JSON.stringify(el).length < 500) name = JSON.stringify(el);
527
- else name = "type";
528
-
529
- disp.push(name);
530
- });
531
-
532
- // Replace identical display text with "text 1", "text 2", etc.
533
- var inc = {};
534
- $each(disp,function(i,name) {
535
- inc[name] = inc[name] || 0;
536
- inc[name]++;
537
-
538
- if(used[name] > 1) disp[i] = name + " " + inc[name];
539
- });
540
-
541
- return disp;
542
- },
543
-
544
- // Replace space(s) with "-" to create valid id value
545
- getValidId: function(id) {
546
- id = id === undefined ? "" : id.toString();
547
- return id.replace(/\s+/g, "-");
548
- },
549
- setInputAttributes: function(inputAttribute) {
731
+ })
732
+
733
+ /* Determine display text for each element of the array */
734
+ arr.forEach(el => {
735
+ let name
736
+
737
+ /* If it's a simple string */
738
+ if (typeof el === 'string') name = el
739
+ /* Object */
740
+ else if (el.title && used[el.title] <= 1) name = el.title
741
+ else if (el.format && used[el.format] <= 1) name = el.format
742
+ else if (el.type && used[el.type] <= 1) name = el.type
743
+ else if (el.description && used[el.description] <= 1) name = el.description
744
+ else if (el.title) name = el.title
745
+ else if (el.format) name = el.format
746
+ else if (el.type) name = el.type
747
+ else if (el.description) name = el.description
748
+ else if (JSON.stringify(el).length < 500) name = JSON.stringify(el)
749
+ else name = 'type'
750
+
751
+ disp.push(name)
752
+ })
753
+
754
+ /* Replace identical display text with "text 1", "text 2", etc. */
755
+ const inc = {}
756
+ disp.forEach((name, i) => {
757
+ inc[name] = inc[name] || 0
758
+ inc[name]++
759
+
760
+ if (used[name] > 1) disp[i] = `${name} ${inc[name]}`
761
+ })
762
+
763
+ return disp
764
+ }
765
+
766
+ /* Replace space(s) with "-" to create valid id value */
767
+ getValidId (id) {
768
+ id = id === undefined ? '' : id.toString()
769
+ return id.replace(/\s+/g, '-')
770
+ }
771
+
772
+ setInputAttributes (inputAttribute, input) {
550
773
  if (this.schema.options && this.schema.options.inputAttributes) {
551
- var inputAttributes = this.schema.options.inputAttributes;
552
- var protectedAttributes = ['name', 'type'].concat(inputAttribute);
553
- for (var key in inputAttributes) {
554
- if (inputAttributes.hasOwnProperty(key) && protectedAttributes.indexOf(key.toLowerCase()) == -1) {
555
- this.input.setAttribute(key, inputAttributes[key]);
774
+ const inputAttributes = this.schema.options.inputAttributes
775
+ const protectedAttributes = ['name', 'type'].concat(inputAttribute)
776
+ const workingInput = input || this.input
777
+ Object.keys(inputAttributes).forEach(key => {
778
+ if (!protectedAttributes.includes(key.toLowerCase())) {
779
+ workingInput.setAttribute(key, inputAttributes[key])
556
780
  }
557
- }
781
+ })
558
782
  }
559
- },
560
- getOption: function(key) {
561
- try {
562
- throw "getOption is deprecated";
563
- }
564
- catch(e) {
565
- window.console.error(e);
783
+ }
784
+
785
+ setContainerAttributes () {
786
+ if (this.schema.options && this.schema.options.containerAttributes) {
787
+ const containerAttributes = this.schema.options.containerAttributes
788
+ const protectedAttributes = ['data-schemapath', 'data-schematype', 'data-schemaid']
789
+ Object.keys(containerAttributes).forEach(key => {
790
+ if (!protectedAttributes.includes(key.toLowerCase())) {
791
+ this.container.setAttribute(key, containerAttributes[key])
792
+ }
793
+ })
566
794
  }
567
-
568
- return this.options[key];
569
- },
570
- showValidationErrors: function(errors) {
795
+ }
796
+
797
+ expandCallbacks (scope, options) {
798
+ const callback = this.defaults.callbacks[scope]
799
+ Object.entries(options).forEach(([key, value]) => {
800
+ if (value === Object(value)) {
801
+ options[key] = this.expandCallbacks(scope, value)
802
+ } else if (typeof value === 'string' &&
803
+ typeof callback === 'object' &&
804
+ typeof callback[value] === 'function') {
805
+ options[key] = callback[value].bind(null, this)
806
+ }
807
+ })
808
+ return options
809
+ }
810
+
811
+ showValidationErrors (errors) {
571
812
 
572
813
  }
573
- });
814
+ }