@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.
- package/.claude/settings.local.json +8 -0
- package/.env +2 -0
- package/.env-dist +2 -0
- package/.eslintrc +10 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +9 -0
- package/.github/workflows/build.yml +70 -0
- package/.travis.yml +62 -16
- package/CHANGELOG.md +483 -113
- package/CONTRIBUTING.md +43 -4
- package/Makefile +26 -0
- package/README.md +632 -121
- package/README_ADDON.md +577 -0
- package/UPGRADING.md +46 -0
- package/build/CssToJson.js +55 -0
- package/codecept.conf.js +35 -0
- package/config/.eslintrc +7 -0
- package/config/codeceptjs_helpers.js +146 -0
- package/config/helpers.js +10 -0
- package/config/karma.conf.js +89 -0
- package/config/readme.md +31 -0
- package/config/webpack.common.js +71 -0
- package/config/webpack.dev.js +15 -0
- package/config/webpack.nonmin.js +19 -0
- package/config/webpack.prod.js +24 -0
- package/dist/jsoneditor.js +2 -11550
- package/dist/jsoneditor.js.LICENSE.txt +15 -0
- package/dist/nonmin/jsoneditor.js +31392 -0
- package/dist/nonmin/jsoneditor.js.map +1 -0
- package/docs/basic.html +2 -2
- package/docs/basic_person.json +2 -1
- package/docs/choices.html +86 -0
- package/docs/cleave.html +11 -19
- package/docs/colorpicker.html +194 -0
- package/docs/css_integration.html +56 -54
- package/docs/custom-editor.html +92 -0
- package/docs/datetime.html +48 -21
- package/docs/describedby.html +42 -35
- package/docs/enumsource.html +67 -0
- package/docs/form-submission.html +162 -0
- package/docs/imask.html +192 -0
- package/docs/index.html +600 -582
- package/docs/materialize_css.html +1 -1
- package/docs/meta-schema.html +793 -0
- package/docs/meta_schema.json +446 -400
- package/docs/polyfills/assign.js +29 -0
- package/docs/radio.html +9 -17
- package/docs/recursive.html +15 -17
- package/docs/scripts/ajv-validator.js +8695 -0
- package/docs/select2.html +15 -9
- package/docs/selectize.html +11 -9
- package/docs/signature.html +12 -11
- package/docs/starrating.html +5 -17
- package/docs/upload.html +33 -23
- package/docs/uuid.html +70 -0
- package/docs/wysiwyg.html +14 -7
- package/jasmine.json +11 -0
- package/package.json +74 -25
- package/release-notes.md +90 -0
- package/src/core.js +411 -611
- package/src/defaults.js +381 -327
- package/src/editor.js +733 -544
- package/src/editors/ace.js +90 -0
- package/src/editors/array/choices.js +104 -0
- package/src/editors/array/select2.js +112 -0
- package/src/editors/array/selectize.js +107 -130
- package/src/editors/array.css +9 -0
- package/src/editors/array.css.js +3 -0
- package/src/editors/array.js +812 -695
- package/src/editors/autocomplete.js +59 -0
- package/src/editors/base64.js +148 -129
- package/src/editors/button.js +104 -0
- package/src/editors/checkbox.js +111 -76
- package/src/editors/choices.css +3 -0
- package/src/editors/choices.css.js +3 -0
- package/src/editors/choices.js +71 -0
- package/src/editors/colorpicker.js +105 -0
- package/src/editors/datetime.js +93 -79
- package/src/editors/describedby.js +141 -125
- package/src/editors/enum.js +131 -116
- package/src/editors/hidden.js +100 -86
- package/src/editors/index.js +81 -0
- package/src/editors/info.js +28 -0
- package/src/editors/integer.js +21 -8
- package/src/editors/ip.js +36 -0
- package/src/editors/jodit.js +66 -0
- package/src/editors/multiple.js +380 -241
- package/src/editors/multiselect.js +207 -207
- package/src/editors/null.js +15 -11
- package/src/editors/number.js +39 -30
- package/src/editors/object.css +41 -0
- package/src/editors/object.css.js +3 -0
- package/src/editors/object.js +1190 -1068
- package/src/editors/radio.js +117 -101
- package/src/editors/sceditor.js +74 -0
- package/src/editors/select.js +318 -342
- package/src/editors/select2.js +112 -0
- package/src/editors/selectize.js +89 -338
- package/src/editors/signature.js +100 -104
- package/src/editors/simplemde.js +103 -0
- package/src/{styles → editors}/starrating.css +11 -2
- package/src/editors/starrating.css.js +3 -0
- package/src/editors/starrating.js +126 -95
- package/src/editors/stepper.js +27 -0
- package/src/editors/string.js +352 -426
- package/src/editors/table.js +486 -410
- package/src/editors/upload.js +297 -131
- package/src/editors/uuid.js +49 -0
- package/src/iconlib.js +22 -27
- package/src/iconlibs/bootstrap.js +28 -0
- package/src/iconlibs/bootstrap2.js +28 -17
- package/src/iconlibs/bootstrap3.js +28 -17
- package/src/iconlibs/fontawesome3.js +28 -17
- package/src/iconlibs/fontawesome4.js +28 -18
- package/src/iconlibs/fontawesome5.js +28 -18
- package/src/iconlibs/foundation2.js +24 -17
- package/src/iconlibs/foundation3.js +28 -17
- package/src/iconlibs/index.js +27 -0
- package/src/iconlibs/jqueryui.js +28 -17
- package/src/iconlibs/materialicons.js +49 -44
- package/src/iconlibs/openiconic.js +28 -0
- package/src/iconlibs/spectre.js +28 -0
- package/src/resolvers.js +135 -0
- package/src/schemaloader.js +639 -0
- package/src/style.css +157 -0
- package/src/style.css.js +3 -0
- package/src/templates/default.js +46 -51
- package/src/templates/ejs.js +8 -10
- package/src/templates/handlebars.js +1 -3
- package/src/templates/hogan.js +7 -9
- package/src/templates/index.js +21 -0
- package/src/templates/lodash.js +6 -8
- package/src/templates/markup.js +6 -8
- package/src/templates/mustache.js +6 -8
- package/src/templates/swig.js +1 -3
- package/src/templates/underscore.js +6 -8
- package/src/theme.js +717 -433
- package/src/themes/barebones.css +35 -0
- package/src/themes/barebones.css.js +3 -0
- package/src/themes/barebones.js +31 -0
- package/src/themes/bootstrap2.js +302 -265
- package/src/themes/bootstrap3.css +53 -0
- package/src/themes/bootstrap3.css.js +3 -0
- package/src/themes/bootstrap3.js +355 -262
- package/src/themes/bootstrap4.css +89 -0
- package/src/themes/bootstrap4.css.js +3 -0
- package/src/themes/bootstrap4.js +743 -237
- package/src/themes/bootstrap5.css +97 -0
- package/src/themes/bootstrap5.css.js +3 -0
- package/src/themes/bootstrap5.js +687 -0
- package/src/themes/foundation.js +535 -472
- package/src/themes/html.css +60 -0
- package/src/themes/html.css.js +3 -0
- package/src/themes/html.js +69 -82
- package/src/themes/index.js +29 -0
- package/src/themes/jqueryui.js +181 -167
- package/src/themes/materialize.js +263 -292
- package/src/themes/spectre.css +208 -0
- package/src/themes/spectre.css.js +3 -0
- package/src/themes/spectre.js +443 -0
- package/src/themes/tailwind.css +303 -0
- package/src/themes/tailwind.css.js +3 -0
- package/src/themes/tailwind.js +469 -0
- package/src/utilities.js +127 -68
- package/src/validator.js +909 -565
- package/src/validators/ip-validator.js +51 -0
- package/tests/Dockerfile +3 -0
- package/tests/README.md +25 -5
- package/tests/codeceptjs/codecept.json +9 -2
- package/tests/codeceptjs/constrains/contains_test.js +37 -0
- package/tests/codeceptjs/constrains/dependentRequired_test.js +33 -0
- package/tests/codeceptjs/constrains/dependentSchemas_test.js +16 -0
- package/tests/codeceptjs/constrains/if-then-else_test.js +186 -0
- package/tests/codeceptjs/core_test.js +534 -63
- package/tests/codeceptjs/editors/advanced_test.js +12 -9
- package/tests/codeceptjs/editors/array_any_of_test.js +50 -0
- package/tests/codeceptjs/editors/array_test.js +923 -657
- package/tests/codeceptjs/editors/autocomplete_test.js +15 -0
- package/tests/codeceptjs/editors/button_test.js +50 -0
- package/tests/codeceptjs/editors/checkbox_test.js +21 -6
- package/tests/codeceptjs/editors/colorpicker_test.js +29 -0
- package/tests/codeceptjs/editors/datetime_test.js +33 -0
- package/tests/codeceptjs/editors/inheritance_test.js +10 -0
- package/tests/codeceptjs/editors/integer_test.js +82 -64
- package/tests/codeceptjs/editors/jodit_test.js +23 -0
- package/tests/codeceptjs/editors/multiple_test.js +27 -0
- package/tests/codeceptjs/editors/multiselect_test.js +6 -7
- package/tests/codeceptjs/editors/number_test.js +75 -60
- package/tests/codeceptjs/editors/object_test.js +322 -80
- package/tests/codeceptjs/editors/option-no_default_values_test.js +42 -0
- package/tests/codeceptjs/editors/programmatic-changes_test.js +20 -0
- package/tests/codeceptjs/editors/purify_test.js +26 -0
- package/tests/codeceptjs/editors/radio_test.js +9 -0
- package/tests/codeceptjs/editors/range_test.js +10 -0
- package/tests/codeceptjs/editors/rating_test.js +10 -10
- package/tests/codeceptjs/editors/select_test.js +47 -18
- package/tests/codeceptjs/editors/starrating_test.js +15 -0
- package/tests/codeceptjs/editors/stepper_test.js +37 -0
- package/tests/codeceptjs/editors/string_test.js +107 -91
- package/tests/codeceptjs/editors/table-confirm-delete_test.js +60 -54
- package/tests/codeceptjs/editors/tabs_test.js +15 -11
- package/tests/codeceptjs/editors/uuid_test.js +48 -0
- package/tests/codeceptjs/editors/validation_test.js +13 -9
- package/tests/codeceptjs/issues/issue-gh-1133_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-1158-2_test.js +10 -0
- package/tests/codeceptjs/issues/issue-gh-1158_test.js +8 -0
- package/tests/codeceptjs/issues/issue-gh-1164_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-1171_test.js +11 -0
- package/tests/codeceptjs/issues/issue-gh-1211_test.js +17 -0
- package/tests/codeceptjs/issues/issue-gh-1257_test.js +11 -0
- package/tests/codeceptjs/issues/issue-gh-1272_test.js +21 -0
- package/tests/codeceptjs/issues/issue-gh-1330_test.js +8 -0
- package/tests/codeceptjs/issues/issue-gh-1338_test.js +18 -0
- package/tests/codeceptjs/issues/issue-gh-1347_test.js +8 -0
- package/tests/codeceptjs/issues/issue-gh-1364_test.js +13 -0
- package/tests/codeceptjs/issues/issue-gh-1367_test.js +11 -0
- package/tests/codeceptjs/issues/issue-gh-1383_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-1384_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-1410_test.js +13 -0
- package/tests/codeceptjs/issues/issue-gh-1422_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-1431_test.js +12 -0
- package/tests/codeceptjs/issues/issue-gh-1439_test.js +12 -0
- package/tests/codeceptjs/issues/issue-gh-1452_test.js +10 -0
- package/tests/codeceptjs/issues/issue-gh-1453_test.js +18 -0
- package/tests/codeceptjs/issues/issue-gh-1461_test.js +14 -0
- package/tests/codeceptjs/issues/issue-gh-1463_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-1471_test.js +17 -0
- package/tests/codeceptjs/issues/issue-gh-1485_test.js +13 -0
- package/tests/codeceptjs/issues/issue-gh-1491_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-1525_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-1536_test.js +12 -0
- package/tests/codeceptjs/issues/issue-gh-1538_test.js +10 -0
- package/tests/codeceptjs/issues/issue-gh-1541_test.js +8 -0
- package/tests/codeceptjs/issues/issue-gh-1559_test.js +15 -0
- package/tests/codeceptjs/issues/issue-gh-1562_test.js +12 -0
- package/tests/codeceptjs/issues/issue-gh-1586_test.js +15 -0
- package/tests/codeceptjs/issues/issue-gh-1636_test.js +9 -0
- package/tests/codeceptjs/issues/issue-gh-795_test.js +13 -0
- package/tests/codeceptjs/issues/issue-gh-810_test.js +52 -0
- package/tests/codeceptjs/issues/issue-gh-812_test.js +25 -0
- package/tests/codeceptjs/meta-schema_test.js +85 -0
- package/tests/codeceptjs/schemaloader_test.js +14 -0
- package/tests/codeceptjs/steps.d.ts +13 -0
- package/tests/codeceptjs/steps_file.js +4 -4
- package/tests/codeceptjs/test-config.js +3 -0
- package/tests/codeceptjs/themes_test.js +564 -0
- package/tests/docker-compose-local.yml +5 -0
- package/tests/docker-compose.yml +11 -19
- package/tests/fixtures/basic_person.json +2 -1
- package/tests/fixtures/definitions.json +22 -0
- package/tests/fixtures/nested_object.json +26 -0
- package/tests/fixtures/properties.json +20 -0
- package/tests/fixtures/some_types.json +32 -0
- package/tests/fixtures/validation.json +1347 -0
- package/tests/pages/_demo.html +475 -0
- package/tests/pages/advanced.html +1 -1
- package/tests/pages/anyof-2.html +91 -0
- package/tests/pages/anyof.html +82 -0
- package/tests/pages/array-anyof.html +145 -0
- package/tests/pages/array-checkboxes-infotext.html +55 -0
- package/tests/pages/array-checkboxes.html +5 -2
- package/tests/pages/array-choices.html +48 -0
- package/tests/pages/array-events-table.html +70 -0
- package/tests/pages/array-events.html +75 -0
- package/tests/pages/array-header-template.html +60 -0
- package/tests/pages/array-integers.html +5 -2
- package/tests/pages/array-multiselects.html +5 -2
- package/tests/pages/array-nested-arrays.html +5 -2
- package/tests/pages/array-numbers.html +5 -2
- package/tests/pages/array-objects.html +5 -2
- package/tests/pages/array-ratings.html +5 -2
- package/tests/pages/array-selectize-create.html +63 -0
- package/tests/pages/array-selectize.html +10 -8
- package/tests/pages/array-selects.html +5 -2
- package/tests/pages/array-strings.html +5 -2
- package/tests/pages/array-table-responsive.html +66 -0
- package/tests/pages/array-unique-items-sort.html +81 -0
- package/tests/pages/array.html +5 -2
- package/tests/pages/assets/autocomplete.css +1 -0
- package/tests/pages/assets/autocomplete.min.js +1 -0
- package/tests/pages/assets/pages.css +130 -0
- package/tests/pages/autocomplete.html +72 -0
- package/tests/pages/button-callbacks.html +79 -0
- package/tests/pages/button-icons.html +39 -0
- package/tests/pages/button_state_mode_1.html +35 -0
- package/tests/pages/button_state_mode_2.html +36 -0
- package/tests/pages/checkbox-labels.html +116 -0
- package/tests/pages/colorpicker-no-3rd-party.html +45 -0
- package/tests/pages/colorpicker-use-vanilla-picker.html +52 -0
- package/tests/pages/container-attributes.html +51 -0
- package/tests/pages/contains.html +39 -0
- package/tests/pages/core.html +14 -7
- package/tests/pages/datetime.html +78 -0
- package/tests/pages/dependentRequired.html +72 -0
- package/tests/pages/dependentSchemas.html +53 -0
- package/tests/pages/disable-button-in-object-editors.html +57 -0
- package/tests/pages/editor-show-validation-errors.html +54 -0
- package/tests/pages/enforce-const.html +168 -0
- package/tests/pages/error-messages.html +48 -0
- package/tests/pages/form-name.html +111 -0
- package/tests/pages/grid-strict.html +10 -13
- package/tests/pages/grid.html +4 -7
- package/tests/pages/if-else.html +58 -0
- package/tests/pages/if-then-else-allOf.html +118 -0
- package/tests/pages/if-then-else-disable-fields.html +70 -0
- package/tests/pages/if-then-else.html +65 -0
- package/tests/pages/if-then.html +58 -0
- package/tests/pages/inheritance.html +80 -0
- package/tests/pages/integer.html +17 -10
- package/tests/pages/issues/_template.html +50 -0
- package/tests/pages/issues/issue-gh-1133.html +64 -0
- package/tests/pages/issues/issue-gh-1158-2.html +189 -0
- package/tests/pages/issues/issue-gh-1158.html +50 -0
- package/tests/pages/issues/issue-gh-1164.html +71 -0
- package/tests/pages/issues/issue-gh-1165.html +63 -0
- package/tests/pages/issues/issue-gh-1165.json +8 -0
- package/tests/pages/issues/issue-gh-1171.html +39 -0
- package/tests/pages/issues/issue-gh-1211.html +1022 -0
- package/tests/pages/issues/issue-gh-1233-failing.html +46 -0
- package/tests/pages/issues/issue-gh-1233-passing.html +49 -0
- package/tests/pages/issues/issue-gh-1257.html +77 -0
- package/tests/pages/issues/issue-gh-1272.html +167 -0
- package/tests/pages/issues/issue-gh-1330.html +52 -0
- package/tests/pages/issues/issue-gh-1338.html +74 -0
- package/tests/pages/issues/issue-gh-1347.html +142 -0
- package/tests/pages/issues/issue-gh-1364.html +64 -0
- package/tests/pages/issues/issue-gh-1367.html +49 -0
- package/tests/pages/issues/issue-gh-1383.html +31 -0
- package/tests/pages/issues/issue-gh-1383.json +14 -0
- package/tests/pages/issues/issue-gh-1384.html +31 -0
- package/tests/pages/issues/issue-gh-1384.json +36 -0
- package/tests/pages/issues/issue-gh-1410.html +57 -0
- package/tests/pages/issues/issue-gh-1422.html +68 -0
- package/tests/pages/issues/issue-gh-1431.html +49 -0
- package/tests/pages/issues/issue-gh-1439.html +69 -0
- package/tests/pages/issues/issue-gh-1452.html +98 -0
- package/tests/pages/issues/issue-gh-1453.html +45 -0
- package/tests/pages/issues/issue-gh-1461.html +55 -0
- package/tests/pages/issues/issue-gh-1463.html +41 -0
- package/tests/pages/issues/issue-gh-1466.html +63 -0
- package/tests/pages/issues/issue-gh-1471.html +49 -0
- package/tests/pages/issues/issue-gh-1485.html +59 -0
- package/tests/pages/issues/issue-gh-1491.html +59 -0
- package/tests/pages/issues/issue-gh-1525.html +62 -0
- package/tests/pages/issues/issue-gh-1536.html +55 -0
- package/tests/pages/issues/issue-gh-1538.html +56 -0
- package/tests/pages/issues/issue-gh-1541.html +51 -0
- package/tests/pages/issues/issue-gh-1541.json +9 -0
- package/tests/pages/issues/issue-gh-1559.html +68 -0
- package/tests/pages/issues/issue-gh-1562.html +170 -0
- package/tests/pages/issues/issue-gh-1586.html +48 -0
- package/tests/pages/issues/issue-gh-1636.html +52 -0
- package/tests/pages/issues/issue-gh-795.html +58 -0
- package/tests/pages/issues/issue-gh-810.html +149 -0
- package/tests/pages/issues/issue-gh-812.html +113 -0
- package/tests/pages/issues/issue-gh-823-meta-schema.html +35 -0
- package/tests/pages/issues/issue-gh-848.html +81 -0
- package/tests/pages/keep_only_existing_values.html +81 -0
- package/tests/pages/load-events.html +61 -0
- package/tests/pages/maxContains.html +40 -0
- package/tests/pages/meta-schema.html +793 -0
- package/tests/pages/meta_schema.json +740 -0
- package/tests/pages/minContains.html +40 -0
- package/tests/pages/number.html +53 -6
- package/tests/pages/object-case-sensitive-property-search-false.html +42 -0
- package/tests/pages/object-case-sensitive-property-search-true.html +42 -0
- package/tests/pages/object-no-additional-properties.html +68 -0
- package/tests/pages/object-no-duplicated-id.html +70 -0
- package/tests/pages/object-required-properties.html +56 -20
- package/tests/pages/object-show-opt-in.html +111 -0
- package/tests/pages/object-with-dependencies-array.html +58 -0
- package/tests/pages/object-with-dependencies.html +62 -0
- package/tests/pages/object.html +5 -3
- package/tests/pages/oneof-2.html +91 -0
- package/tests/pages/oneof.html +105 -0
- package/tests/pages/opt-in-widget.html +134 -0
- package/tests/pages/option-dependencies.html +107 -0
- package/tests/pages/option-no_default_values.html +60 -0
- package/tests/pages/per-editor-options.html +44 -0
- package/tests/pages/placeholder-options.html +57 -0
- package/tests/pages/programmatic-changes.html +121 -0
- package/tests/pages/prompt-paste-max-length-reached.html +51 -0
- package/tests/pages/purify.html +66 -0
- package/tests/pages/range.html +62 -0
- package/tests/pages/read-only.html +60 -8
- package/tests/pages/ready.html +44 -0
- package/tests/pages/references.html +168 -0
- package/tests/pages/remove-false-properties.html +85 -0
- package/tests/pages/select-values.html +91 -0
- package/tests/pages/select.html +4 -3
- package/tests/pages/show-validation-errors.html +73 -0
- package/tests/pages/starrating.html +86 -0
- package/tests/pages/stepper-manual.html +59 -0
- package/tests/pages/stepper.html +61 -0
- package/tests/pages/string-ace-editor.html +7 -3
- package/tests/pages/string-cleave.html +48 -0
- package/tests/pages/string-custom-attributes.html +9 -6
- package/tests/pages/string-formats.html +54 -0
- package/tests/pages/string-formats2.html +59 -0
- package/tests/pages/{array-move-events.html → string-jodit-editor.html} +21 -29
- package/tests/pages/string-sceditor.html +9 -7
- package/tests/pages/string-simplemde-editor.html +83 -0
- package/tests/pages/switcher-option.html +69 -0
- package/tests/pages/table.html +4 -2
- package/tests/pages/tabs.html +1 -1
- package/tests/pages/themes.html +517 -0
- package/tests/pages/title-hidden.html +75 -0
- package/tests/pages/translate-property.html +248 -0
- package/tests/pages/urn.html +98 -0
- package/tests/pages/use-name-attributes.html +207 -0
- package/tests/pages/uuid.html +113 -0
- package/tests/pages/validation-messages.json +705 -0
- package/tests/pages/validation.html +20 -730
- package/tests/unit/.eslintrc +8 -0
- package/tests/unit/core.spec.js +320 -0
- package/tests/unit/defaults.spec.js +40 -0
- package/tests/unit/editor.spec.js +172 -0
- package/tests/unit/editors/array.spec.js +87 -0
- package/tests/unit/editors/object.spec.js +81 -0
- package/tests/unit/editors/table.spec.js +93 -0
- package/tests/unit/readme.md +35 -0
- package/tests/unit/schemaloader.spec.js +576 -0
- package/tests/unit/validator.spec.js +104 -0
- package/tests/unit/validators/ip-validator.spec.js +62 -0
- package/Gruntfile.js +0 -228
- package/dist/css/jsoneditor.min.css +0 -1
- package/dist/jsoneditor.js.map +0 -1
- package/dist/jsoneditor.min.js +0 -19
- package/dist/jsoneditor.min.js.map +0 -1
- package/src/class.js +0 -68
- package/src/editors/rating.js +0 -152
- package/src/ie9.js +0 -51
- package/src/intro.js +0 -23
- package/src/jquery.js +0 -64
- package/src/outro.js +0 -2
- 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
|
+
}
|