@json-editor/json-editor 1.4.0-beta.0 → 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 (435) 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 +62 -16
  8. package/CHANGELOG.md +483 -113
  9. package/CONTRIBUTING.md +43 -4
  10. package/Makefile +26 -0
  11. package/README.md +632 -121
  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 -11550
  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 +42 -35
  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 +600 -582
  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 +9 -17
  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 +381 -327
  61. package/src/editor.js +733 -544
  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 +107 -130
  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 +141 -125
  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 +1190 -1068
  93. package/src/editors/radio.js +117 -101
  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 -433
  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 -265
  141. package/src/themes/bootstrap3.css +53 -0
  142. package/src/themes/bootstrap3.css.js +3 -0
  143. package/src/themes/bootstrap3.js +355 -262
  144. package/src/themes/bootstrap4.css +89 -0
  145. package/src/themes/bootstrap4.css.js +3 -0
  146. package/src/themes/bootstrap4.js +743 -237
  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 +535 -472
  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 -167
  156. package/src/themes/materialize.js +263 -292
  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 +909 -565
  165. package/src/validators/ip-validator.js +51 -0
  166. package/tests/Dockerfile +3 -0
  167. package/tests/README.md +25 -5
  168. package/tests/codeceptjs/codecept.json +9 -2
  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 -63
  174. package/tests/codeceptjs/editors/advanced_test.js +12 -9
  175. package/tests/codeceptjs/editors/array_any_of_test.js +50 -0
  176. package/tests/codeceptjs/editors/array_test.js +923 -657
  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 -6
  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 -64
  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 -7
  187. package/tests/codeceptjs/editors/number_test.js +75 -60
  188. package/tests/codeceptjs/editors/object_test.js +322 -80
  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 -10
  195. package/tests/codeceptjs/editors/select_test.js +47 -18
  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 +107 -91
  199. package/tests/codeceptjs/editors/table-confirm-delete_test.js +60 -54
  200. package/tests/codeceptjs/editors/tabs_test.js +15 -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 +11 -19
  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 +10 -8
  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 +10 -13
  301. package/tests/pages/grid.html +4 -7
  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 +56 -20
  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 -228
  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/src/class.js +0 -68
  430. package/src/editors/rating.js +0 -152
  431. package/src/ie9.js +0 -51
  432. package/src/intro.js +0 -23
  433. package/src/jquery.js +0 -64
  434. package/src/outro.js +0 -2
  435. package/src/themes/jsoneditor.barebones-theme.js +0 -60
package/src/editor.js CHANGED
@@ -1,625 +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
- isActive: function() {
32
- return this.active;
33
- },
34
- activate: function() {
35
- this.active = true;
36
- this.optInCheckbox.checked = true;
37
- this.enable();
38
- this.change();
39
- },
40
- deactivate: function() {
41
- // only non required properties can be deactivated.
42
- if (!this.isRequired()) {
43
- this.active = false;
44
- this.optInCheckbox.checked = false;
45
- this.disable();
46
- this.change();
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()
47
56
  }
48
- },
49
- init: function(options) {
50
- this.jsoneditor = options.jsoneditor;
51
57
 
52
- this.theme = this.jsoneditor.theme;
53
- this.template_engine = this.jsoneditor.template;
54
- this.iconlib = this.jsoneditor.iconlib;
58
+ if (bubble) this.change(eventData)
59
+ }
55
60
 
56
- this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate;
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
+ }
57
99
 
58
- this.original_schema = options.schema;
59
- this.schema = this.jsoneditor.expandSchema(this.original_schema);
100
+ registerDependencies () {
101
+ this.dependenciesFulfilled = true
102
+ const deps = this.options.dependencies
103
+ if (!deps) {
104
+ return
105
+ }
60
106
 
61
- this.active = true;
107
+ Object.keys(deps).forEach(dependency => {
108
+ let path
109
+ const isFullPath = dependency.startsWith(this.jsoneditor.root.path)
62
110
 
63
- this.options = $extend({}, (this.options || {}), (this.schema.options || {}), (options.schema.options || {}), options);
111
+ if (isFullPath) {
112
+ path = dependency
113
+ } else {
114
+ path = this.path.split('.')
115
+ path[path.length - 1] = dependency
116
+ path = path.join('.')
117
+ }
64
118
 
65
- if(!options.path && !this.schema.id) this.schema.id = 'root';
66
- this.path = options.path || 'root';
67
- this.formname = options.formname || this.path.replace(/\.([^.]+)/g,'[$1]');
68
- if(this.jsoneditor.options.form_name_root) this.formname = this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+'[');
69
- this.key = this.path.split('.').pop();
70
- this.parent = options.parent;
119
+ this.jsoneditor.watch(path, () => {
120
+ this.evaluateDependencies()
121
+ })
122
+ })
123
+ }
71
124
 
72
- this.link_watchers = [];
125
+ evaluateDependencies () {
126
+ const wrapper = this.container || this.control
127
+ if (!wrapper || this.jsoneditor === null) {
128
+ return
129
+ }
73
130
 
74
- if(options.container) this.setContainer(options.container);
75
- this.registerDependencies();
76
- },
77
- registerDependencies: function() {
78
- this.dependenciesFulfilled = true;
79
- var deps = this.options.dependencies;
131
+ const deps = this.options.dependencies
80
132
  if (!deps) {
81
- return;
82
- }
83
-
84
- var self = this;
85
- Object.keys(deps).forEach(function(dependency) {
86
- var path = self.path.split('.');
87
- path[path.length - 1] = dependency;
88
- path = path.join('.');
89
- var choices = deps[dependency];
90
- self.jsoneditor.watch(path, function() {
91
- self.checkDependency(path, choices);
92
- });
93
- });
94
- },
95
- checkDependency: function(path, choices) {
96
- var wrapper = this.container || this.control;
97
- if (this.path === path || !wrapper || this.jsoneditor === null) {
98
- return;
99
- }
100
-
101
- var self = this;
102
- var editor = this.jsoneditor.getEditor(path);
103
- var value = editor ? editor.getValue() : undefined;
104
- var previousStatus = this.dependenciesFulfilled;
105
- this.dependenciesFulfilled = false;
106
-
107
- if (!editor || !editor.dependenciesFulfilled) {
108
- this.dependenciesFulfilled = false;
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
109
181
  } else if (Array.isArray(choices)) {
110
- choices.some(function(choice) {
111
- if (value === choice) {
112
- self.dependenciesFulfilled = true;
113
- return true;
182
+ this.dependenciesFulfilled = choices.some(choice => {
183
+ if (JSON.stringify(value) === JSON.stringify(choice)) {
184
+ return true
114
185
  }
115
- });
186
+ })
116
187
  } else if (typeof choices === 'object') {
117
188
  if (typeof value !== 'object') {
118
- this.dependenciesFulfilled = choices === value;
189
+ this.dependenciesFulfilled = choices === value
119
190
  } else {
120
- Object.keys(choices).some(function(key) {
121
- if (!choices.hasOwnProperty(key)) {
122
- return false;
191
+ Object.keys(choices).some(key => {
192
+ if (!hasOwnProperty(choices, key)) {
193
+ return false
123
194
  }
124
- if (!value.hasOwnProperty(key) || choices[key] !== value[key]) {
125
- self.dependenciesFulfilled = false;
126
- return true;
195
+ if (!hasOwnProperty(value, key) || choices[key] !== value[key]) {
196
+ this.dependenciesFulfilled = false
197
+ return true
127
198
  }
128
- self.dependenciesFulfilled = true;
129
- });
199
+ })
130
200
  }
131
201
  } else if (typeof choices === 'string' || typeof choices === 'number') {
132
- this.dependenciesFulfilled = value === choices;
202
+ this.dependenciesFulfilled = this.dependenciesFulfilled && value === choices
133
203
  } else if (typeof choices === 'boolean') {
134
204
  if (choices) {
135
- this.dependenciesFulfilled = value && value.length > 0;
205
+ this.dependenciesFulfilled = this.dependenciesFulfilled && (value || value.length > 0)
136
206
  } else {
137
- this.dependenciesFulfilled = !value || value.length === 0;
207
+ this.dependenciesFulfilled = this.dependenciesFulfilled && (!value || value.length === 0)
138
208
  }
139
209
  }
210
+ }
140
211
 
141
- if (this.dependenciesFulfilled !== previousStatus) {
142
- this.notify();
143
- }
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
144
222
 
145
- if (this.dependenciesFulfilled) {
146
- wrapper.style.display = 'block';
223
+ if (this.optInWidget === 'switch') {
224
+ optIn = this.theme.getOptInSwitch(this.formname)
147
225
  } else {
148
- wrapper.style.display = 'none';
149
- }
150
- },
151
- setContainer: function(container) {
152
- this.container = container;
153
- if(this.schema.id) this.container.setAttribute('data-schemaid',this.schema.id);
154
- if(this.schema.type && typeof this.schema.type === "string") this.container.setAttribute('data-schematype',this.schema.type);
155
- this.container.setAttribute('data-schemapath',this.path);
156
- },
157
- setOptInCheckbox: function(header) {
158
- // the active/deactive checbox control.
159
- var self = this;
160
- this.optInCheckbox = document.createElement('input');
161
- this.optInCheckbox.setAttribute('type', 'checkbox');
162
- this.optInCheckbox.setAttribute('style', 'margin: 0 10px 0 0;');
163
- this.optInCheckbox.classList.add('json-editor-opt-in');
164
-
165
- this.optInCheckbox.addEventListener('click', function () {
166
- if (self.isActive()) {
167
- self.deactivate();
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()
168
235
  } else {
169
- self.activate();
236
+ this.activate()
170
237
  }
171
- });
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)
172
245
 
173
- // append active/deactive checkbox if show_opt_in is true
174
- if (this.jsoneditor.options.show_opt_in || this.options.show_opt_in) {
175
- // and control to type object editors if they are not required
246
+ if (parentOptInEnabled || (!parentOptInDisabled && globalOptIn) || (!parentOptInDefined && globalOptIn)) {
247
+ /* and control to type object editors if they are not required */
176
248
  if (this.parent && this.parent.schema.type === 'object' && !this.isRequired() && this.header) {
177
- this.header.appendChild(this.optInCheckbox);
178
- this.header.insertBefore(this.optInCheckbox, this.header.firstChild);
249
+ this.header.insertBefore(this.optInContainer, this.header.firstChild)
250
+ this.optInAppended = true
179
251
  }
180
252
  }
253
+ }
181
254
 
182
- },
183
- preBuild: function() {
184
-
185
- },
186
- build: function() {
187
-
188
- },
189
- postBuild: function() {
190
- this.setupWatchListeners();
191
- this.addLinks();
192
- this.setValue(this.getDefault(), true);
193
- this.updateHeaderText();
194
- this.register();
195
- this.onWatchedFieldChange();
196
- },
197
- setupWatchListeners: function() {
198
- var self = this;
199
-
200
- // Watched fields
201
- this.watched = {};
202
- if(this.schema.vars) this.schema.watch = this.schema.vars;
203
- this.watched_values = {};
204
- this.watch_listener = function() {
205
- if(self.refreshWatchedFieldValues()) {
206
- self.onWatchedFieldChange();
207
- }
208
- };
255
+ preBuild () {
209
256
 
210
- if(this.schema.hasOwnProperty('watch')) {
211
- var path,path_parts,first,root,adjusted_path;
257
+ }
212
258
 
213
- for(var name in this.schema.watch) {
214
- if(!this.schema.watch.hasOwnProperty(name)) continue;
215
- path = this.schema.watch[name];
259
+ build () {
216
260
 
217
- if(Array.isArray(path)) {
218
- if(path.length<2) continue;
219
- path_parts = [path[0]].concat(path[1].split('.'));
220
- }
221
- else {
222
- path_parts = path.split('.');
223
- if(!self.theme.closest(self.container,'[data-schemaid="'+path_parts[0]+'"]')) path_parts.unshift('#');
261
+ }
262
+
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()
289
+ }
290
+ }
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('#')
224
304
  }
225
- first = path_parts.shift();
305
+ first = pathParts.shift()
226
306
 
227
- if(first === '#') first = self.jsoneditor.schema.id || 'root';
307
+ if (first === '#') first = this.jsoneditor.schema.id || this.jsoneditor.root.formname
228
308
 
229
- // Find the root node for this template variable
230
- root = self.theme.closest(self.container,'[data-schemaid="'+first+'"]');
231
- if(!root) throw "Could not find ancestor node with id "+first;
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}`)
232
312
 
233
- // Keep track of the root node and path for use when rendering the template
234
- adjusted_path = root.getAttribute('data-schemapath') + '.' + path_parts.join('.');
313
+ /* Keep track of the root node and path for use when rendering the template */
314
+ adjustedPath = `${root.getAttribute('data-schemapath')}.${pathParts.join('.')}`
235
315
 
236
- self.jsoneditor.watch(adjusted_path,self.watch_listener);
316
+ if (myPath.startsWith(adjustedPath)) this.watchLoop = true
317
+ this.jsoneditor.watch(adjustedPath, this.watch_listener)
237
318
 
238
- self.watched[name] = adjusted_path;
239
- }
319
+ this.watched[name] = adjustedPath
320
+ })
240
321
  }
241
322
 
242
- // Dynamic header
243
- if(this.schema.headerTemplate) {
244
- this.header_template = this.jsoneditor.compileTemplate(this.schema.headerTemplate, this.template_engine);
323
+ /* Dynamic header */
324
+ if (this.schema.headerTemplate) {
325
+ this.header_template = this.jsoneditor.compileTemplate(this.schema.headerTemplate, this.template_engine)
245
326
  }
246
- },
327
+ }
247
328
 
248
- addLinks: function() {
249
- // Add links
250
- if(!this.no_link_holder) {
251
- this.link_holder = this.theme.getLinksHolder();
252
- this.container.appendChild(this.link_holder);
253
- if(this.schema.links) {
254
- for(var i=0; i<this.schema.links.length; i++) {
255
- this.addLink(this.getLink(this.schema.links[i]));
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]))
256
340
  }
257
341
  }
258
342
  }
259
- },
260
- onMove: function() {},
261
- getButton: function(text, icon, title) {
262
- var btnClass = 'json-editor-btn-'+icon;
263
- if(!this.iconlib) icon = null;
264
- else icon = this.iconlib.getIcon(icon);
265
-
266
- if(!icon && title) {
267
- text = title;
268
- title = null;
269
- }
270
-
271
- var btn = this.theme.getButton(text, icon, title);
272
- btn.classList.add(btnClass);
273
- return btn;
274
- },
275
- setButtonText: function(button, text, icon, title) {
276
- if(!this.iconlib) icon = null;
277
- else icon = this.iconlib.getIcon(icon);
278
-
279
- if(!icon && title) {
280
- text = title;
281
- title = null;
282
- }
283
-
284
- return this.theme.setButtonText(button, text, icon, title);
285
- },
286
- addLink: function(link) {
287
- if(this.link_holder) this.link_holder.appendChild(link);
288
- },
289
- getLink: function(data) {
290
- var holder, link;
291
-
292
- // Get mime type of the link
293
- var mime = data.mediaType || 'application/javascript';
294
- var type = mime.split('/')[0];
295
-
296
- // Template to generate the link href
297
- var href = this.jsoneditor.compileTemplate(data.href,this.template_engine);
298
- var relTemplate = this.jsoneditor.compileTemplate(data.rel ? data.rel : data.href,this.template_engine);
299
-
300
- // Template to generate the link's download attribute
301
- var download = null;
302
- if(data.download) download = data.download;
303
-
304
- if(download && download !== true) {
305
- download = this.jsoneditor.compileTemplate(download, this.template_engine);
306
- }
307
-
308
- // Image links
309
- if(type === 'image') {
310
- holder = this.theme.getBlockLinkHolder();
311
- link = document.createElement('a');
312
- link.setAttribute('target','_blank');
313
- var image = document.createElement('img');
314
-
315
- this.theme.createImageLink(holder,link,image);
316
-
317
- // When a watched field changes, update the url
318
- this.link_watchers.push(function(vars) {
319
- var url = href(vars);
320
- var rel = relTemplate(vars);
321
- link.setAttribute('href',url);
322
- link.setAttribute('title',rel || url);
323
- image.setAttribute('src',url);
324
- });
325
- }
326
- // Audio/Video links
327
- else if(['audio','video'].indexOf(type) >=0) {
328
- holder = this.theme.getBlockLinkHolder();
329
-
330
- link = this.theme.getBlockLink();
331
- link.setAttribute('target','_blank');
332
-
333
- var media = document.createElement(type);
334
- media.setAttribute('controls','controls');
335
-
336
- this.theme.createMediaLink(holder,link,media);
337
-
338
- // When a watched field changes, update the url
339
- this.link_watchers.push(function(vars) {
340
- var url = href(vars);
341
- var rel = relTemplate(vars);
342
- link.setAttribute('href',url);
343
- link.textContent = rel || url;
344
- media.setAttribute('src',url);
345
- });
346
- }
347
- // Text links
348
- else {
349
- link = holder = this.theme.getBlockLink();
350
- holder.setAttribute('target','_blank');
351
- holder.textContent = data.rel;
352
-
353
- // When a watched field changes, update the url
354
- this.link_watchers.push(function(vars) {
355
- var url = href(vars);
356
- var rel = relTemplate(vars);
357
- holder.setAttribute('href',url);
358
- holder.textContent = rel || url;
359
- });
360
- }
361
-
362
- if(download && link) {
363
- if(download === true) {
364
- link.setAttribute('download','');
365
- }
366
- else {
367
- this.link_watchers.push(function(vars) {
368
- link.setAttribute('download',download(vars));
369
- });
370
- }
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)
371
402
  }
372
403
 
373
- if(data.class) link.classList.add(data.class);
374
-
375
- return holder;
376
- },
377
- refreshWatchedFieldValues: function() {
378
- if(!this.watched_values) return;
379
- var watched = {};
380
- var changed = false;
381
- var self = this;
382
-
383
- if(this.watched) {
384
- var val,editor;
385
- for(var name in this.watched) {
386
- if(!this.watched.hasOwnProperty(name)) continue;
387
- editor = self.jsoneditor.getEditor(this.watched[name]);
388
- val = editor? editor.getValue() : null;
389
- if(self.watched_values[name] !== val) changed = true;
390
- watched[name] = val;
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
+ })
456
+ }
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
+ })
391
465
  }
392
466
  }
393
467
 
394
- watched.self = this.getValue();
395
- if(this.watched_values.self !== watched.self) changed = true;
396
-
397
- this.watched_values = watched;
398
-
399
- return changed;
400
- },
401
- getWatchedFieldValues: function() {
402
- return this.watched_values;
403
- },
404
- updateHeaderText: function() {
405
- if(this.header) {
406
- var header_text = this.getHeaderText();
407
- // If the header has children, only update the text node's value
408
- if(this.header.children.length) {
409
- for(var i=0; i<this.header.childNodes.length; i++) {
410
- if(this.header.childNodes[i].nodeType===3) {
411
- this.header.childNodes[i].nodeValue = this.cleanText(header_text);
412
- break;
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
413
514
  }
414
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)
415
520
  }
416
- // Otherwise, just update the entire node
417
- else {
418
- if (window.DOMPurify) this.header.innerHTML = window.DOMPurify.sanitize(header_text);
419
- else this.header.textContent = this.cleanText(header_text);
420
- }
421
521
  }
422
- },
423
- getHeaderText: function(title_only) {
424
- if(this.header_text) return this.header_text;
425
- else if(title_only) return this.schema.title;
426
- else return this.getTitle();
427
- },
428
- cleanText: function(txt) {
429
- // Clean out HTML tags from txt
430
- var tmp = document.createElement('div');
431
- tmp.innerHTML = txt;
432
- return (tmp.textContent || tmp.innerText);
433
- },
434
- onWatchedFieldChange: function() {
435
- var vars;
436
- if(this.header_template) {
437
- 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(), {
438
560
  key: this.key,
439
561
  i: this.key,
440
- i0: (this.key*1),
441
- i1: (this.key*1+1),
562
+ i0: (this.key * 1),
563
+ i1: (this.key * 1 + 1),
442
564
  title: this.getTitle()
443
- });
444
- var header_text = this.header_template(vars);
445
-
446
- if(header_text !== this.header_text) {
447
- this.header_text = header_text;
448
- this.updateHeaderText();
449
- this.notify();
450
- //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(); */
451
591
  }
452
592
  }
453
- if(this.link_watchers.length) {
454
- vars = this.getWatchedFieldValues();
455
- for(var i=0; i<this.link_watchers.length; i++) {
456
- 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)
457
597
  }
458
598
  }
459
- },
460
- setValue: function(value) {
461
- this.value = value;
462
- },
463
- 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 () {
464
615
  if (!this.dependenciesFulfilled) {
465
- return undefined;
466
- }
467
- return this.value;
468
- },
469
- refreshValue: function() {
470
-
471
- },
472
- getChildEditors: function() {
473
- return false;
474
- },
475
- destroy: function() {
476
- var self = this;
477
- this.unregister(this);
478
- $each(this.watched,function(name,adjusted_path) {
479
- self.jsoneditor.unwatch(adjusted_path,self.watch_listener);
480
- });
481
- this.watched = null;
482
- this.watched_values = null;
483
- this.watch_listener = null;
484
- this.header_text = null;
485
- this.header_template = null;
486
- this.value = null;
487
- if(this.container && this.container.parentNode) this.container.parentNode.removeChild(this.container);
488
- this.container = null;
489
- this.jsoneditor = null;
490
- this.schema = null;
491
- this.path = null;
492
- this.key = null;
493
- this.parent = null;
494
- },
495
- getDefault: function() {
496
- if (typeof this.schema["default"] !== 'undefined') {
497
- return this.schema["default"];
498
- }
499
-
500
- if (typeof this.schema["enum"] !== 'undefined') {
501
- return this.schema["enum"][0];
502
- }
503
-
504
- var type = this.schema.type || this.schema.oneOf;
505
- if(type && Array.isArray(type)) type = type[0];
506
- if(type && typeof type === "object") type = type.type;
507
- if(type && Array.isArray(type)) type = type[0];
508
-
509
- if(typeof type === "string") {
510
- if(type === "number") return 0.0;
511
- if(type === "boolean") return false;
512
- if(type === "integer") return 0;
513
- if(type === "string") return "";
514
- if(type === "object") return {};
515
- if(type === "array") return [];
516
- }
517
-
518
- return null;
519
- },
520
- getTitle: function() {
521
- return this.schema.title || this.key;
522
- },
523
- enable: function() {
524
- this.disabled = false;
525
- },
526
- disable: function() {
527
- this.disabled = true;
528
- },
529
- isEnabled: function() {
530
- return !this.disabled;
531
- },
532
- isRequired: function() {
533
- if(typeof this.schema.required === "boolean") return this.schema.required;
534
- else if(this.parent && this.parent.schema && Array.isArray(this.parent.schema.required)) return this.parent.schema.required.indexOf(this.key) > -1;
535
- else if(this.jsoneditor.options.required_by_default) return true;
536
- else return false;
537
- },
538
- getDisplayText: function(arr) {
539
- var disp = [];
540
- var used = {};
541
-
542
- // Determine how many times each attribute name is used.
543
- // This helps us pick the most distinct display text for the schemas.
544
- $each(arr,function(i,el) {
545
- if(el.title) {
546
- used[el.title] = used[el.title] || 0;
547
- 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]++
548
718
  }
549
- if(el.description) {
550
- used[el.description] = used[el.description] || 0;
551
- used[el.description]++;
719
+ if (el.description) {
720
+ used[el.description] = used[el.description] || 0
721
+ used[el.description]++
552
722
  }
553
- if(el.format) {
554
- used[el.format] = used[el.format] || 0;
555
- used[el.format]++;
723
+ if (el.format) {
724
+ used[el.format] = used[el.format] || 0
725
+ used[el.format]++
556
726
  }
557
- if(el.type) {
558
- used[el.type] = used[el.type] || 0;
559
- used[el.type]++;
727
+ if (el.type) {
728
+ used[el.type] = used[el.type] || 0
729
+ used[el.type]++
560
730
  }
561
- });
562
-
563
- // Determine display text for each element of the array
564
- $each(arr,function(i,el) {
565
- var name;
566
-
567
- // If it's a simple string
568
- if(typeof el === "string") name = el;
569
- // Object
570
- else if(el.title && used[el.title]<=1) name = el.title;
571
- else if(el.format && used[el.format]<=1) name = el.format;
572
- else if(el.type && used[el.type]<=1) name = el.type;
573
- else if(el.description && used[el.description]<=1) name = el.descripton;
574
- else if(el.title) name = el.title;
575
- else if(el.format) name = el.format;
576
- else if(el.type) name = el.type;
577
- else if(el.description) name = el.description;
578
- else if(JSON.stringify(el).length < 500) name = JSON.stringify(el);
579
- else name = "type";
580
-
581
- disp.push(name);
582
- });
583
-
584
- // Replace identical display text with "text 1", "text 2", etc.
585
- var inc = {};
586
- $each(disp,function(i,name) {
587
- inc[name] = inc[name] || 0;
588
- inc[name]++;
589
-
590
- if(used[name] > 1) disp[i] = name + " " + inc[name];
591
- });
592
-
593
- return disp;
594
- },
595
-
596
- // Replace space(s) with "-" to create valid id value
597
- getValidId: function(id) {
598
- id = id === undefined ? "" : id.toString();
599
- return id.replace(/\s+/g, "-");
600
- },
601
- 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) {
602
773
  if (this.schema.options && this.schema.options.inputAttributes) {
603
- var inputAttributes = this.schema.options.inputAttributes;
604
- var protectedAttributes = ['name', 'type'].concat(inputAttribute);
605
- for (var key in inputAttributes) {
606
- if (inputAttributes.hasOwnProperty(key) && protectedAttributes.indexOf(key.toLowerCase()) == -1) {
607
- 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])
608
780
  }
609
- }
610
- }
611
- },
612
- getOption: function(key) {
613
- try {
614
- throw "getOption is deprecated";
781
+ })
615
782
  }
616
- catch(e) {
617
- 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
+ })
618
794
  }
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
+ }
619
810
 
620
- return this.options[key];
621
- },
622
- showValidationErrors: function(errors) {
811
+ showValidationErrors (errors) {
623
812
 
624
813
  }
625
- });
814
+ }