@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
@@ -0,0 +1,639 @@
1
+ import { extend, hasOwnProperty } from './utilities.js'
2
+
3
+ /**
4
+ * Handles loading Schema and tracking references.
5
+ */
6
+ export class SchemaLoader {
7
+ constructor (options) {
8
+ /**
9
+ * @prop {object}
10
+ * Options of the schema. @see readme.
11
+ */
12
+ this.options = options || {}
13
+
14
+ /**
15
+ * @prop {object}
16
+ * The orginial schema to load
17
+ */
18
+ this.schema = {}
19
+
20
+ /**
21
+ * @prop {object}
22
+ * Storage of External ref. Exemple :
23
+ * refs = {
24
+ * "fully/realized/path/to/schema.json": { ... }
25
+ * "mylocalschema.json": { ... }
26
+ * }
27
+ */
28
+ this.refs = this.options.refs || {}
29
+
30
+ /**
31
+ * @prop {object}
32
+ * Mapping between the schema and ref. Exemple:
33
+ * refs_with_info = {
34
+ * "#/counter/1": "fully/realized/path/to/schema.json"
35
+ * "#/counter/2": "mylocalschema.json"
36
+ * }
37
+ */
38
+ this.refs_with_info = {}
39
+
40
+ /**
41
+ * @prop {string}
42
+ * String to eewrite external ref with.
43
+ */
44
+ this.refs_prefix = '#/counter/'
45
+
46
+ /**
47
+ * @prop {int}
48
+ * Counter of ref
49
+ */
50
+ this.refs_counter = 1
51
+
52
+ this._subSchema1 = {
53
+ /* Version 3 `type` */
54
+ type (schema) {
55
+ if (typeof schema.type === 'object') {
56
+ schema.type = this._expandSubSchema(schema.type)
57
+ }
58
+ },
59
+ /* Version 3 `disallow` */
60
+ disallow (schema) {
61
+ if (typeof schema.disallow === 'object') {
62
+ schema.disallow = this._expandSubSchema(schema.disallow)
63
+ }
64
+ },
65
+ /* Version 4 `anyOf` */
66
+ anyOf (schema) {
67
+ Object.entries(schema.anyOf).forEach(([key, value]) => {
68
+ schema.anyOf[key] = this.expandSchema(value)
69
+ })
70
+ },
71
+ /* Version 4 `dependencies` (schema dependencies) */
72
+ dependencies (schema) {
73
+ Object.entries(schema.dependencies).forEach(([key, value]) => {
74
+ if (typeof value === 'object' && !(Array.isArray(value))) {
75
+ schema.dependencies[key] = this.expandSchema(value)
76
+ }
77
+ })
78
+ },
79
+ /* Version 4 `not` */
80
+ not (schema) {
81
+ schema.not = this.expandSchema(schema.not)
82
+ }
83
+ }
84
+
85
+ this._subSchema2 = {
86
+ /* allOf schemas should be merged into the parent */
87
+ allOf (schema, extended) {
88
+ let _extended = extend({}, extended)
89
+ Object.entries(schema.allOf).forEach(([key, value]) => {
90
+ schema.allOf[key] = this.expandRefs(value, true)
91
+ _extended = this.extendSchemas(_extended, this.expandSchema(value))
92
+ })
93
+ delete _extended.allOf
94
+ return _extended
95
+ },
96
+ /* extends schemas should be merged into parent */
97
+ extends (schema, extended) {
98
+ let _extended
99
+ /* If extends is a schema */
100
+ if (!(Array.isArray(schema.extends))) {
101
+ _extended = this.extendSchemas(extended, this.expandSchema(schema.extends))
102
+ } else {
103
+ /* If extends is an array of schemas */
104
+ _extended = schema.extends.reduce((e, s, i) => {
105
+ return this.extendSchemas(e, this.expandSchema(s))
106
+ }, extended)
107
+ }
108
+ delete _extended.extends
109
+ return _extended
110
+ },
111
+ /* parent should be merged into oneOf schemas */
112
+ oneOf (schema, extended) {
113
+ const tmp = extend({}, extended)
114
+ delete tmp.oneOf
115
+ schema.oneOf.reduce((e, s, i) => {
116
+ e.oneOf[i] = this.extendSchemas(this.expandSchema(s), tmp)
117
+ return e
118
+ }, extended)
119
+ return extended
120
+ }
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Fully loads and expands JSON schema for a provided schema object and URL.
126
+ *
127
+ * The callback receives a expanded JSON Schema object with references
128
+ * replaced with loaded schemas.
129
+ *
130
+ * @param {object} schema - A JSON Schema.
131
+ * @param {string} fetchUrl - Base path from which to store the definitions.
132
+ * Typically the URI of the schema.
133
+ * @param {*} location - The base URL from which to load relative paths.
134
+ * @returns {object} A JSON Schema with references expanded.
135
+ */
136
+ async load (schema, fetchUrl, location) {
137
+ this.schema = schema
138
+ await this._asyncloadExternalRefs(schema, fetchUrl, this._getFileBase(location), true)
139
+ return this.expandRefs(schema)
140
+ }
141
+
142
+ /**
143
+ * Recursively expands loaded references in a provided schema.
144
+ *
145
+ * @param {object} schema - A JSON Schema with references already loaded.
146
+ * @param {boolean} recurseAllOf - Set true to recurse allOf properties.
147
+ * @returns {object} A JSON Schema with references expanded.
148
+ */
149
+ expandRefs (schema, recurseAllOf) {
150
+ const _schema = extend({}, schema)
151
+
152
+ if (!_schema.$ref) return _schema
153
+ // This split the ref to get the Json point if it exists
154
+ // exemple #/counter/1#/definition/address +
155
+ // [1] -> /counter/1
156
+ // [2] -> /definition/address
157
+ const refWithPointerSplit = _schema.$ref.split('#')
158
+ // If local ref
159
+ if (refWithPointerSplit.length === 2 && !this.refs_with_info[_schema.$ref]) {
160
+ const sub = this.expandRecursivePointer(this.schema, refWithPointerSplit[1])
161
+ const expandedSchema = this.extendSchemas(_schema, this.expandSchema(sub))
162
+ delete expandedSchema.$ref
163
+ return expandedSchema
164
+ }
165
+ const refObj = (refWithPointerSplit.length > 2)
166
+ ? this.refs_with_info['#' + refWithPointerSplit[1]]
167
+ : this.refs_with_info[_schema.$ref]
168
+ delete _schema.$ref
169
+ const fetchUrl = refObj.$ref.startsWith('#')
170
+ ? refObj.fetchUrl
171
+ : ''
172
+ const ref = this._getRef(fetchUrl, refObj)
173
+
174
+ if (!this.refs[ref]) { /* if reference not found */
175
+ // eslint-disable-next-line no-console
176
+ console.warn(`reference:'${ref}' not found!`)
177
+ } else if (recurseAllOf && hasOwnProperty(this.refs[ref], 'allOf')) {
178
+ const allOf = this.refs[ref].allOf
179
+ Object.keys(allOf).forEach(key => {
180
+ allOf[key] = this.expandRefs(allOf[key], true)
181
+ })
182
+ }
183
+ if (refWithPointerSplit.length > 2) {
184
+ return this.extendSchemas(_schema, this.expandSchema(this.expandRecursivePointer(this.refs[ref], refWithPointerSplit[2])))
185
+ } else {
186
+ return this.extendSchemas(_schema, this.expandSchema(this.refs[ref]))
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Returns a subschema based on a JSON Pointer path.
192
+ * @param {object} schema - Schema too into
193
+ * @param {string} pointer - path to look for
194
+ * @param {object} original_schema - the Original schema
195
+ * @returns the subschema pointed to by the path
196
+ */
197
+ expandRecursivePointer (schema, pointer) {
198
+ let subschema = schema
199
+ pointer.split('/').slice(1).forEach(i => {
200
+ if (subschema[i]) {
201
+ subschema = subschema[i]
202
+ }
203
+ })
204
+ // If the result is a pointer, let's go for another turn
205
+ if (subschema.$refs && subschema.$refs.startsWith('#')) {
206
+ return this.expandRecursivePointer(schema, subschema.$refs)
207
+ }
208
+ return subschema
209
+ }
210
+
211
+ /**
212
+ * Expands a JSON schema and its references.
213
+ *
214
+ * @param {object} schema - A JSON Schema with references already loaded.
215
+ * @returns {object} A JSON Schema with references expanded.
216
+ */
217
+ expandSchema (schema) {
218
+ Object.entries(this._subSchema1).forEach(([key, func]) => {
219
+ if (schema[key]) {
220
+ func.call(this, schema)
221
+ }
222
+ })
223
+
224
+ let extended = extend({}, schema)
225
+
226
+ Object.entries(this._subSchema2).forEach(([key, func]) => {
227
+ if (schema[key]) {
228
+ extended = func.call(this, schema, extended)
229
+ }
230
+ })
231
+
232
+ return this.expandRefs(extended)
233
+ }
234
+
235
+ _getRef (fetchUrl, refObj) {
236
+ const ref = fetchUrl + refObj
237
+
238
+ return this.refs[ref] ? ref : fetchUrl + decodeURIComponent(refObj.$ref)
239
+ }
240
+
241
+ _expandSubSchema (subschema) {
242
+ /* Array of types */
243
+ if (Array.isArray(subschema)) return subschema.map(m => typeof m === 'object' ? this.expandSchema(m) : m)
244
+
245
+ /* Schema */
246
+ return this.expandSchema(subschema)
247
+ }
248
+
249
+ /**
250
+ * Rewrite the passed schema's JSON pointers to prepend with the current reference's path, so that it will be converted to a reference "counter".
251
+ *
252
+ * @example
253
+ * In file "../otherreferencedfile.json", referenced from "schema.json":
254
+ *
255
+ * "$ref": "#/definitions/myschema" => "$ref": "../path/to/my/referenced/schemafile.json#/definitions/myschema"
256
+ * ...which will then be parsed later in loadExternalReferences() to "$ref": "#/counter/1#/definitions/myschema"
257
+ *
258
+ * @param {object} schema - A JSON Schema with the definitions key present.
259
+ * @param {string} path - Base path from which to store the definitions in refs. (exemple ../path/to/my/referenced/schemafile.json)
260
+ * Typically the URI of the schema.
261
+ */
262
+ _manageRecursivePointer (schema, path) {
263
+ Object.keys(schema).forEach(i => {
264
+ if (schema[i] !== null && schema[i].$ref && schema[i].$ref.indexOf('#') === 0) {
265
+ schema[i].$ref = path + schema[i].$ref
266
+ }
267
+ })
268
+ }
269
+
270
+ /**
271
+ * Recursively parse a (sub)schema to populate loader reference info.
272
+ *
273
+ * @param {object} schema - A JSON Schema
274
+ * @param {string} fetchUrl - Base path from which to store the definitions.
275
+ * @param {boolean} firstIteration - Is it the first time we load this function? Help making difference between external ref vs internal pointer
276
+ * @returns {array} Refs in the format of uri => true if external.
277
+ */
278
+ _getExternalRefs (schema, fetchUrl, firstIteration = false) {
279
+ if (!firstIteration) this._manageRecursivePointer(schema, fetchUrl)
280
+ const refs = {}
281
+ const mergeRefs = newrefs => Object.keys(newrefs).forEach(i => { refs[i] = true })
282
+ if (schema.$ref && typeof schema.$ref !== 'object' && !(schema.$ref.indexOf('#') === 0 && firstIteration)) {
283
+ let refBase = schema.$ref
284
+ let pointer = ''
285
+ // Strip any JSON pointers found for external refs.
286
+ if (refBase.indexOf('#') > 0) refBase = refBase.substr(0, refBase.indexOf('#'))
287
+ if (refBase !== schema.$ref) pointer = schema.$ref.substr(schema.$ref.indexOf('#'))
288
+ // We use a fragment idenfier to track json pointer in top of our pointer
289
+ const refCounter = this.refs_prefix + this.refs_counter++
290
+ const refPointer = refCounter + pointer
291
+ if (schema.$ref.substr(0, 1) !== '#' && !this.refs[schema.$ref]) {
292
+ refs[refBase] = true
293
+ }
294
+ this.refs_with_info[refCounter] = { fetchUrl, $ref: refBase }
295
+ schema.$ref = refPointer
296
+ }
297
+
298
+ Object.values(schema).forEach(value => {
299
+ if (!value || typeof value !== 'object') return
300
+ if (Array.isArray(value)) {
301
+ Object.values(value).forEach(e => {
302
+ if (e && typeof e === 'object') {
303
+ mergeRefs(this._getExternalRefs(e, fetchUrl, firstIteration))
304
+ }
305
+ })
306
+ } else {
307
+ // Merge Ref if it's not a Pointer
308
+ if (!value.$ref || !(typeof value.$ref === 'string' && value.$ref.startsWith('#'))) {
309
+ mergeRefs(this._getExternalRefs(value, fetchUrl, firstIteration))
310
+ }
311
+ }
312
+ })
313
+
314
+ if (schema.id && typeof schema.id === 'string' && schema.id.substr(0, 4) === 'urn:') {
315
+ this.refs[schema.id] = schema
316
+ } else if (schema.$id && typeof schema.$id === 'string' && schema.$id.substr(0, 4) === 'urn:') {
317
+ this.refs[schema.$id] = schema
318
+ }
319
+ return refs
320
+ }
321
+
322
+ _getFileBase (location) {
323
+ if (!location) return '/'
324
+
325
+ const { ajaxBase } = this.options
326
+
327
+ return typeof ajaxBase === 'undefined' ? this._getFileBaseFromFileLocation(location) : ajaxBase
328
+ }
329
+
330
+ _getFileBaseFromFileLocation (fileLocationString) {
331
+ const pathItems = fileLocationString.split('/')
332
+ pathItems.pop()
333
+ return `${pathItems.join('/')}/`
334
+ }
335
+
336
+ _joinUrl (url, fileBase) {
337
+ let fetchUrl = url
338
+
339
+ if (url.substr(0, 7) !== 'http://' &&
340
+ url.substr(0, 8) !== 'https://' &&
341
+ url.substr(0, 5) !== 'blob:' &&
342
+ url.substr(0, 5) !== 'data:' &&
343
+ url.substr(0, 1) !== '#' &&
344
+ url.substr(0, 1) !== '/'
345
+ ) {
346
+ fetchUrl = fileBase + url
347
+ }
348
+
349
+ // strip #fragment from URI, so json pointers resolve correctly #928
350
+ if (fetchUrl.indexOf('#') > 0) fetchUrl = fetchUrl.substr(0, fetchUrl.indexOf('#'))
351
+
352
+ return fetchUrl
353
+ }
354
+
355
+ _isUniformResourceName (uri) {
356
+ return uri.substr(0, 4) === 'urn:'
357
+ }
358
+
359
+ /**
360
+ * Loads external references via AJAX.
361
+ *
362
+ * Will fail if this.options.ajax is not set to true.
363
+ *
364
+ * @param {object} schema - JSON Schema with external references.
365
+ * @param {string} fetchUrl - Base path from which to store the definitions.
366
+ * Typically the URI of the schema.
367
+ * @param {string} fileBase - The base URL from which to load relative paths.
368
+ * Typically the URI of the schema minus filename, with trailing slash.
369
+ * @param {boolean} firstIteration - Is it the first time we load this function? Help making difference between external ref vs internal pointer
370
+ *
371
+ * @return {boolean}
372
+ * @throws Error
373
+ */
374
+ async _asyncloadExternalRefs (schema, fetchUrl, fileBase, firstIteration = false) {
375
+ const refs = this._getExternalRefs(schema, fetchUrl, firstIteration)
376
+ let waiting = 0
377
+ // Loop into all schema references
378
+ for (const uri of Object.keys(refs)) {
379
+ if (typeof uri === 'undefined') continue
380
+ if (this.refs[uri]) continue
381
+ if (this._isUniformResourceName(uri)) {
382
+ this.refs[uri] = 'loading'
383
+ waiting++
384
+ const urnResolver = this.options.urn_resolver
385
+ let urn = uri
386
+ if (typeof urnResolver !== 'function') {
387
+ // eslint-disable-next-line no-console
388
+ console.log(`No "urn_resolver" callback defined to resolve "${urn}"`)
389
+ throw new Error(`Must set urn_resolver option to a callback to resolve ${urn}`)
390
+ }
391
+ // theoretically a URN can contain a JSON pointer
392
+ if (urn.indexOf('#') > 0) urn = urn.substr(0, urn.indexOf('#'))
393
+ let response
394
+ try {
395
+ let externalSchema
396
+ response = await urnResolver(urn)
397
+ try {
398
+ externalSchema = JSON.parse(response)
399
+ } catch (e) {
400
+ // eslint-disable-next-line no-console
401
+ console.log(e)
402
+ throw new Error(`Failed to parse external ref ${urn}`)
403
+ }
404
+ if (!(typeof externalSchema === 'boolean' || typeof externalSchema === 'object') || externalSchema === null || Array.isArray(externalSchema)) {
405
+ throw new Error(`External ref does not contain a valid schema - ${urn}`)
406
+ }
407
+
408
+ this.refs[uri] = externalSchema
409
+
410
+ await this._asyncloadExternalRefs(externalSchema, uri, fileBase)
411
+ } catch (e) {
412
+ // eslint-disable-next-line no-console
413
+ console.log(e)
414
+ throw new Error(`Failed to parse external ref ${urn}`)
415
+ }
416
+
417
+ if (typeof response === 'boolean') {
418
+ throw new Error(`External ref does not contain a valid schema - ${urn}`)
419
+ }
420
+ continue
421
+ }
422
+ if (!this.options.ajax) throw new Error(`Must set ajax option to true to load external ref ${uri}`)
423
+ waiting++
424
+
425
+ let url = this._joinUrl(uri, fileBase)
426
+
427
+ let externalSchema
428
+ if (this.options.ajax_cache_responses) {
429
+ const schemaFromCache = this.cacheGet(url)
430
+ if (schemaFromCache) {
431
+ externalSchema = schemaFromCache
432
+ }
433
+ }
434
+
435
+ if (!externalSchema) {
436
+ const response = await new Promise(resolve => {
437
+ const r = new XMLHttpRequest()
438
+ if (this.options.ajaxCredentials) r.withCredentials = this.options.ajaxCredentials
439
+ r.overrideMimeType('application/json')
440
+ r.open('GET', url, true)
441
+ r.onload = () => {
442
+ resolve(r)
443
+ }
444
+ r.onerror = (e) => {
445
+ resolve(undefined)
446
+ }
447
+ r.send()
448
+ })
449
+ if (typeof response === 'undefined') throw new Error(`Failed to fetch ref via ajax - ${uri}`)
450
+ try {
451
+ externalSchema = JSON.parse(response.responseText)
452
+
453
+ this.onSchemaLoaded({
454
+ schema: externalSchema,
455
+ schemaUrl: url
456
+ })
457
+
458
+ if (this.options.ajax_cache_responses) {
459
+ this.cacheSet(url, externalSchema)
460
+ }
461
+ } catch (e) {
462
+ // eslint-disable-next-line no-console
463
+ console.log(e)
464
+ throw new Error(`Failed to parse external ref ${url}`)
465
+ }
466
+ }
467
+
468
+ if (!(typeof externalSchema === 'boolean' || typeof externalSchema === 'object') || externalSchema === null || Array.isArray(externalSchema)) {
469
+ throw new Error(`External ref does not contain a valid schema - ${url}`)
470
+ }
471
+ this.refs[uri] = externalSchema
472
+ const newfileBase = this._getFileBaseFromFileLocation(url)
473
+
474
+ // Add leading slash.
475
+ if (url !== uri) {
476
+ const pathItems = url.split('/')
477
+ url = (uri.substr(0, 1) === '/' ? '/' : '') + pathItems.pop()
478
+ }
479
+ await this._asyncloadExternalRefs(externalSchema, url, newfileBase)
480
+ }
481
+
482
+ if (!waiting) {
483
+ return true
484
+ }
485
+
486
+ this.onAllSchemasLoaded()
487
+ }
488
+
489
+ onSchemaLoaded (payload) {}
490
+
491
+ onAllSchemasLoaded () {}
492
+
493
+ extendSchemas (obj1, obj2) {
494
+ obj1 = extend({}, obj1)
495
+ obj2 = extend({}, obj2)
496
+
497
+ const extended = {}
498
+ const isRequiredOrDefaultProperties = (prop, val) => (prop === 'required' || prop === 'defaultProperties') && typeof val === 'object' && Array.isArray(val)
499
+ const merge = (prop, val) => {
500
+ /* Required and defaultProperties arrays should be unioned together */
501
+ if (isRequiredOrDefaultProperties(prop, val)) {
502
+ /* Union arrays and unique */
503
+ extended[prop] = val.concat(obj2[prop]).reduce((p, c) => {
504
+ if (!p.includes(c)) p.push(c)
505
+ return p
506
+ }, [])
507
+ } else if (prop === 'type' && (typeof val === 'string' || Array.isArray(val))) {
508
+ mergeType(val)
509
+ } else if (typeof val === 'object' && !Array.isArray(val) && val !== null) {
510
+ /* Objects should be recursively merged */
511
+ extended[prop] = this.extendSchemas(val, obj2[prop])
512
+ } else {
513
+ /* Otherwise, use the first value */
514
+ extended[prop] = val
515
+ }
516
+ }
517
+
518
+ const mergeType = (val) => {
519
+ /* Type should be intersected and is either an array or string */
520
+ /* Make sure we're dealing with arrays */
521
+ if (typeof val === 'string') val = [val]
522
+ if (typeof obj2.type === 'string') obj2.type = [obj2.type]
523
+
524
+ /* If type is only defined in the first schema, keep it */
525
+ if (!obj2.type || !obj2.type.length) {
526
+ extended.type = val
527
+ } else {
528
+ /* If type is defined in both schemas, do an intersect */
529
+ extended.type = val.filter(n => obj2.type.includes(n))
530
+ }
531
+
532
+ /* If there's only 1 type and it's a primitive, use a string instead of array */
533
+ if (extended.type.length === 1 && typeof extended.type[0] === 'string') {
534
+ extended.type = extended.type[0]
535
+ } else if (extended.type.length === 0) {
536
+ /* Remove the type property if it's empty */
537
+ delete extended.type
538
+ }
539
+ }
540
+
541
+ Object.entries(obj1).forEach(([prop, val]) => {
542
+ /* If this key is also defined in obj2, merge them */
543
+ if (typeof obj2[prop] !== 'undefined') {
544
+ merge(prop, val)
545
+ } else {
546
+ /* Otherwise, just use the one in obj1 */
547
+ extended[prop] = val
548
+ }
549
+ })
550
+
551
+ /* Properties in obj2 that aren't in obj1 */
552
+ Object.entries(obj2).forEach(([prop, val]) => {
553
+ if (typeof obj1[prop] === 'undefined') {
554
+ extended[prop] = val
555
+ }
556
+ })
557
+ return extended
558
+ }
559
+
560
+ /**
561
+ * Gets a cache key namespaced for JSON Editor.
562
+ *
563
+ * @param {*} key
564
+ * The schema's key, e.g., URL.
565
+ * @returns {string}
566
+ * A namespaced cache key, by prefixing "je-cache::".
567
+ */
568
+ getCacheKey (key) {
569
+ return ['je-cache', key].join('::')
570
+ }
571
+
572
+ /**
573
+ * Returns the schema cache buster from JSON Editor settings.
574
+ *
575
+ * @returns {string}
576
+ * The configured cache buster, if any. Otherwise, returns the current date
577
+ * in ISO 8601 simplified format (e.g., 2011-10-05 for October 5, 2011).
578
+ */
579
+ getCacheBuster () {
580
+ return this.options.ajax_cache_buster || new Date().toISOString().slice(0, 10)
581
+ }
582
+
583
+ /**
584
+ * Sets a schema into localStorage cache.
585
+ *
586
+ * @param {string} key
587
+ * The schema's key, e.g., URL.
588
+ * @param {mixed} data
589
+ * The schema to store. Can be any data type.
590
+ */
591
+ cacheSet (key, data) {
592
+ try {
593
+ window.localStorage.setItem(this.getCacheKey(key), JSON.stringify({
594
+ cacheBuster: this.getCacheBuster(),
595
+ schema: data
596
+ }))
597
+ } catch (e) {
598
+ // eslint-disable-next-line no-console
599
+ console.error(e)
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Fetches a schema from localStorage cache.
605
+ *
606
+ * @param {string} key
607
+ * The schema's key, e.g., URL.
608
+ *
609
+ * @returns {mixed}
610
+ * If found, returns the schema.
611
+ */
612
+ cacheGet (key) {
613
+ try {
614
+ const resultRaw = window.localStorage.getItem(this.getCacheKey(key))
615
+ if (resultRaw) {
616
+ const resultDecoded = JSON.parse(resultRaw)
617
+ if (resultDecoded.cacheBuster && resultDecoded.schema) {
618
+ if (resultDecoded.cacheBuster === this.getCacheBuster()) {
619
+ return resultDecoded.schema
620
+ }
621
+ }
622
+ this.cacheDelete(key)
623
+ }
624
+ } catch (e) {
625
+ // eslint-disable-next-line no-console
626
+ console.error(e)
627
+ }
628
+ }
629
+
630
+ /**
631
+ * Deletes a schema from localStorage cache.
632
+ *
633
+ * @param {string} key
634
+ * The schema's key, e.g., URL.
635
+ */
636
+ cacheDelete (key) {
637
+ window.localStorage.removeItem(this.getCacheKey(key))
638
+ }
639
+ }