@wirenboard/json-editor 2.5.3-wb13
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/.env-dist +2 -0
- package/.eslintrc +7 -0
- package/.gitattributes +1 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +9 -0
- package/.github/issue_template +25 -0
- package/.github/workflows/build.yml +58 -0
- package/.travis.yml +70 -0
- package/CHANGELOG.md +915 -0
- package/CONTRIBUTING.md +92 -0
- package/LICENSE +20 -0
- package/Makefile +26 -0
- package/README.md +1646 -0
- package/README_ADDON.md +573 -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 +139 -0
- package/config/helpers.js +10 -0
- package/config/karma.conf.js +93 -0
- package/config/readme.md +31 -0
- package/config/webpack.common.js +75 -0
- package/config/webpack.dev.js +15 -0
- package/config/webpack.nonmin.js +19 -0
- package/config/webpack.prod.js +25 -0
- package/dist/jsoneditor.js +14 -0
- package/dist/nonmin/jsoneditor.js +29097 -0
- package/dist/nonmin/jsoneditor.js.map +1 -0
- package/docs/ace_editor.html +56 -0
- package/docs/advanced.html +136 -0
- package/docs/basic.html +63 -0
- package/docs/basic_person.json +26 -0
- package/docs/choices.html +86 -0
- package/docs/cleave.html +132 -0
- package/docs/colorpicker.html +194 -0
- package/docs/css_integration.html +135 -0
- package/docs/datetime.html +305 -0
- package/docs/describedby.html +161 -0
- package/docs/enumsource.html +67 -0
- package/docs/images/categoriesDemo.png +0 -0
- package/docs/images/inheritance_tree.png +0 -0
- package/docs/images/jsoneditor.png +0 -0
- package/docs/imask.html +192 -0
- package/docs/index.html +579 -0
- package/docs/materialize_css.html +164 -0
- package/docs/meta_schema.json +705 -0
- package/docs/multiple_upload_base64.html +65 -0
- package/docs/person.json +73 -0
- package/docs/polyfills/assign.js +29 -0
- package/docs/radio.html +156 -0
- package/docs/recursive.html +170 -0
- package/docs/select2.html +99 -0
- package/docs/selectize.html +100 -0
- package/docs/signature.html +42 -0
- package/docs/starrating.html +137 -0
- package/docs/upload.html +131 -0
- package/docs/uuid.html +70 -0
- package/docs/wysiwyg.html +56 -0
- package/jasmine.json +11 -0
- package/json-editor-json-editor-2.5.3-wb13.tgz +0 -0
- package/package.json +100 -0
- package/release-notes.md +88 -0
- package/src/core.js +412 -0
- package/src/defaults.js +402 -0
- package/src/editor.js +707 -0
- package/src/editors/ace.js +89 -0
- package/src/editors/array/choices.js +103 -0
- package/src/editors/array/select2.js +110 -0
- package/src/editors/array/selectize.js +103 -0
- package/src/editors/array.css +9 -0
- package/src/editors/array.css.js +3 -0
- package/src/editors/array.js +818 -0
- package/src/editors/autocomplete.js +58 -0
- package/src/editors/base64.js +157 -0
- package/src/editors/button.js +97 -0
- package/src/editors/checkbox.js +95 -0
- package/src/editors/choices.css +3 -0
- package/src/editors/choices.css.js +3 -0
- package/src/editors/choices.js +69 -0
- package/src/editors/colorpicker.js +103 -0
- package/src/editors/datetime.js +141 -0
- package/src/editors/describedby.js +188 -0
- package/src/editors/enum.js +136 -0
- package/src/editors/hidden.js +127 -0
- package/src/editors/index.js +81 -0
- package/src/editors/info.js +20 -0
- package/src/editors/integer.js +19 -0
- package/src/editors/ip.js +36 -0
- package/src/editors/jodit.js +64 -0
- package/src/editors/multiple.js +409 -0
- package/src/editors/multiselect.js +218 -0
- package/src/editors/null.js +18 -0
- package/src/editors/number.js +51 -0
- package/src/editors/object.css +41 -0
- package/src/editors/object.css.js +3 -0
- package/src/editors/object.js +1290 -0
- package/src/editors/radio.js +111 -0
- package/src/editors/sceditor.js +72 -0
- package/src/editors/select.js +370 -0
- package/src/editors/select2.js +110 -0
- package/src/editors/selectize.js +112 -0
- package/src/editors/signature.js +113 -0
- package/src/editors/simplemde.js +100 -0
- package/src/editors/starrating.css +52 -0
- package/src/editors/starrating.css.js +3 -0
- package/src/editors/starrating.js +135 -0
- package/src/editors/stepper.js +27 -0
- package/src/editors/string.js +372 -0
- package/src/editors/table.js +516 -0
- package/src/editors/upload.js +321 -0
- package/src/editors/uuid.js +56 -0
- package/src/iconlib.js +24 -0
- package/src/iconlibs/bootstrap2.js +28 -0
- package/src/iconlibs/bootstrap3.js +28 -0
- package/src/iconlibs/fontawesome3.js +28 -0
- package/src/iconlibs/fontawesome4.js +28 -0
- package/src/iconlibs/fontawesome5.js +28 -0
- package/src/iconlibs/foundation2.js +24 -0
- package/src/iconlibs/foundation3.js +28 -0
- package/src/iconlibs/index.js +25 -0
- package/src/iconlibs/jqueryui.js +28 -0
- package/src/iconlibs/materialicons.js +49 -0
- package/src/iconlibs/openiconic.js +28 -0
- package/src/iconlibs/spectre.js +28 -0
- package/src/resolvers.js +128 -0
- package/src/schemaloader.js +408 -0
- package/src/style.css +150 -0
- package/src/style.css.js +3 -0
- package/src/templates/default.js +52 -0
- package/src/templates/ejs.js +13 -0
- package/src/templates/handlebars.js +1 -0
- package/src/templates/hogan.js +10 -0
- package/src/templates/index.js +21 -0
- package/src/templates/lodash.js +9 -0
- package/src/templates/markup.js +9 -0
- package/src/templates/mustache.js +9 -0
- package/src/templates/swig.js +1 -0
- package/src/templates/underscore.js +9 -0
- package/src/theme.js +659 -0
- package/src/themes/barebones.css +35 -0
- package/src/themes/barebones.css.js +3 -0
- package/src/themes/barebones.js +28 -0
- package/src/themes/bootstrap2.js +319 -0
- package/src/themes/bootstrap3.css +0 -0
- package/src/themes/bootstrap3.css.js +3 -0
- package/src/themes/bootstrap3.js +315 -0
- package/src/themes/bootstrap4.css +89 -0
- package/src/themes/bootstrap4.css.js +3 -0
- package/src/themes/bootstrap4.js +690 -0
- package/src/themes/bootstrap5.css.js +3 -0
- package/src/themes/foundation.js +569 -0
- package/src/themes/html.css +60 -0
- package/src/themes/html.css.js +3 -0
- package/src/themes/html.js +71 -0
- package/src/themes/index.js +28 -0
- package/src/themes/jqueryui.js +198 -0
- package/src/themes/materialize.js +426 -0
- package/src/themes/spectre.css +208 -0
- package/src/themes/spectre.css.js +3 -0
- package/src/themes/spectre.js +406 -0
- package/src/themes/tailwind.css +249 -0
- package/src/themes/tailwind.css.js +3 -0
- package/src/themes/tailwind.js +443 -0
- package/src/utilities.js +138 -0
- package/src/validator.js +877 -0
- package/src/validators/ip-validator.js +51 -0
- package/tests/Dockerfile +3 -0
- package/tests/README.md +48 -0
- package/tests/codeceptjs/codecept.json +42 -0
- package/tests/codeceptjs/constrains/if-then-else_test.js +143 -0
- package/tests/codeceptjs/core_test.js +217 -0
- package/tests/codeceptjs/editors/advanced_test.js +13 -0
- package/tests/codeceptjs/editors/array_any_of_test.js +50 -0
- package/tests/codeceptjs/editors/array_test.js +900 -0
- package/tests/codeceptjs/editors/button_test.js +35 -0
- package/tests/codeceptjs/editors/checkbox_test.js +21 -0
- package/tests/codeceptjs/editors/colorpicker_test.js +27 -0
- package/tests/codeceptjs/editors/datetime_test.js +33 -0
- package/tests/codeceptjs/editors/inheritance_test.js +11 -0
- package/tests/codeceptjs/editors/integer_test.js +84 -0
- package/tests/codeceptjs/editors/issues/issue-gh-812_test.js +32 -0
- package/tests/codeceptjs/editors/jodit_test.js +24 -0
- package/tests/codeceptjs/editors/multiselect_test.js +8 -0
- package/tests/codeceptjs/editors/number_test.js +82 -0
- package/tests/codeceptjs/editors/object_test.js +204 -0
- 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/radio_test.js +10 -0
- package/tests/codeceptjs/editors/rating_test.js +13 -0
- package/tests/codeceptjs/editors/select_test.js +22 -0
- package/tests/codeceptjs/editors/stepper_test.js +27 -0
- package/tests/codeceptjs/editors/string_test.js +118 -0
- package/tests/codeceptjs/editors/table-confirm-delete_test.js +67 -0
- package/tests/codeceptjs/editors/tabs_test.js +14 -0
- package/tests/codeceptjs/editors/uuid_test.js +21 -0
- package/tests/codeceptjs/editors/validation_test.js +14 -0
- package/tests/codeceptjs/meta-schema_test.js +17 -0
- package/tests/codeceptjs/schemaloader_test.js +13 -0
- package/tests/codeceptjs/steps.d.ts +13 -0
- package/tests/codeceptjs/steps_file.js +12 -0
- package/tests/codeceptjs/themes_test.js +519 -0
- package/tests/docker-compose.yml +34 -0
- package/tests/fixtures/basic_person.json +26 -0
- package/tests/fixtures/nested_object.json +26 -0
- package/tests/fixtures/person.json +55 -0
- package/tests/fixtures/recursive.json +8 -0
- package/tests/fixtures/some_types.json +32 -0
- package/tests/fixtures/string.json +3 -0
- package/tests/fixtures/validation.json +1140 -0
- package/tests/pages/_demo.html +475 -0
- package/tests/pages/advanced.html +137 -0
- package/tests/pages/anyof.html +80 -0
- package/tests/pages/array-anyof.html +142 -0
- package/tests/pages/array-checkboxes.html +41 -0
- package/tests/pages/array-choices.html +45 -0
- package/tests/pages/array-integers.html +37 -0
- package/tests/pages/array-move-events.html +61 -0
- package/tests/pages/array-multiselects.html +42 -0
- package/tests/pages/array-nested-arrays.html +40 -0
- package/tests/pages/array-numbers.html +37 -0
- package/tests/pages/array-objects.html +42 -0
- package/tests/pages/array-ratings.html +40 -0
- package/tests/pages/array-selectize.html +51 -0
- package/tests/pages/array-selects.html +36 -0
- package/tests/pages/array-strings.html +36 -0
- package/tests/pages/array.html +42 -0
- package/tests/pages/assets/pages.css +130 -0
- package/tests/pages/button-callbacks.html +77 -0
- package/tests/pages/checkbox-labels.html +114 -0
- package/tests/pages/colorpicker-no-3rd-party.html +43 -0
- package/tests/pages/colorpicker-use-vanilla-picker.html +50 -0
- package/tests/pages/core.html +118 -0
- package/tests/pages/datetime.html +76 -0
- package/tests/pages/form-name.html +108 -0
- package/tests/pages/grid-strict.html +311 -0
- package/tests/pages/grid.html +284 -0
- package/tests/pages/if-then-else-allOf.html +117 -0
- package/tests/pages/inheritance.html +76 -0
- package/tests/pages/integer.html +68 -0
- package/tests/pages/issues/_template.html +50 -0
- package/tests/pages/issues/issue-gh-812.html +110 -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/meta_schema.json +705 -0
- package/tests/pages/number.html +89 -0
- package/tests/pages/object-no-additional-properties.html +65 -0
- package/tests/pages/object-no-duplicated-id.html +68 -0
- package/tests/pages/object-required-properties.html +236 -0
- package/tests/pages/object-with-dependencies-array.html +46 -0
- package/tests/pages/object-with-dependencies.html +60 -0
- package/tests/pages/object.html +79 -0
- package/tests/pages/oneof.html +103 -0
- package/tests/pages/option-no_default_values.html +58 -0
- package/tests/pages/programmatic-changes.html +120 -0
- package/tests/pages/read-only.html +105 -0
- package/tests/pages/select.html +41 -0
- package/tests/pages/stepper.html +59 -0
- package/tests/pages/string-ace-editor.html +52 -0
- package/tests/pages/string-cleave.html +46 -0
- package/tests/pages/string-custom-attributes.html +62 -0
- package/tests/pages/string-formats.html +52 -0
- package/tests/pages/string-formats2.html +57 -0
- package/tests/pages/string-jodit-editor.html +49 -0
- package/tests/pages/string-sceditor.html +62 -0
- package/tests/pages/table-move-events.html +56 -0
- package/tests/pages/table.html +46 -0
- package/tests/pages/tabs.html +131 -0
- package/tests/pages/themes.html +527 -0
- package/tests/pages/translate-property.html +247 -0
- package/tests/pages/urn.html +93 -0
- package/tests/pages/uuid.html +72 -0
- package/tests/pages/validation.html +99 -0
- package/tests/unit/.eslintrc +8 -0
- package/tests/unit/core.spec.js +309 -0
- package/tests/unit/defaults.spec.js +40 -0
- package/tests/unit/editor.spec.js +160 -0
- package/tests/unit/editors/array.spec.js +86 -0
- package/tests/unit/editors/object.spec.js +79 -0
- package/tests/unit/editors/table.spec.js +91 -0
- package/tests/unit/readme.md +35 -0
- package/tests/unit/schemaloader.spec.js +498 -0
- package/tests/unit/validator.spec.js +94 -0
- package/tests/unit/validators/ip-validator.spec.js +62 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AbstractIconLib } from '../iconlib.js'
|
|
2
|
+
|
|
3
|
+
const iconClass = 'material-icons'
|
|
4
|
+
const iconPrefix = ''
|
|
5
|
+
const mapping = {
|
|
6
|
+
collapse: 'arrow_drop_up',
|
|
7
|
+
expand: 'arrow_drop_down',
|
|
8
|
+
delete: 'delete',
|
|
9
|
+
edit: 'edit',
|
|
10
|
+
add: 'add',
|
|
11
|
+
subtract: 'remove',
|
|
12
|
+
cancel: 'cancel',
|
|
13
|
+
save: 'save',
|
|
14
|
+
moveup: 'arrow_upward',
|
|
15
|
+
movedown: 'arrow_downward',
|
|
16
|
+
moveright: 'arrow_forward',
|
|
17
|
+
moveleft: 'arrow_back',
|
|
18
|
+
copy: 'content_copy',
|
|
19
|
+
clear: 'highlight_off',
|
|
20
|
+
time: 'access_time',
|
|
21
|
+
calendar: 'calendar_today',
|
|
22
|
+
upload: 'cloud_upload',
|
|
23
|
+
edit_properties: 'list'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class materialiconsIconlib extends AbstractIconLib {
|
|
27
|
+
constructor () {
|
|
28
|
+
super(iconPrefix, mapping)
|
|
29
|
+
this.icon_class = iconClass
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getIconClass (key) {
|
|
33
|
+
/* This method is unused. */
|
|
34
|
+
return this.icon_class
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getIcon (key) {
|
|
38
|
+
/* Get the mapping. */
|
|
39
|
+
const mapping = this.mapping[key]
|
|
40
|
+
if (!mapping) return null
|
|
41
|
+
|
|
42
|
+
/* @see http://materializecss.com/icons.html */
|
|
43
|
+
const i = document.createElement('i')
|
|
44
|
+
i.classList.add(this.icon_class)
|
|
45
|
+
const t = document.createTextNode(mapping)
|
|
46
|
+
i.appendChild(t)
|
|
47
|
+
return i
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AbstractIconLib } from '../iconlib.js'
|
|
2
|
+
|
|
3
|
+
const iconPrefix = 'oi oi-'
|
|
4
|
+
const mapping = {
|
|
5
|
+
collapse: 'collapse-down',
|
|
6
|
+
expand: 'expand-right',
|
|
7
|
+
delete: 'trash',
|
|
8
|
+
edit: 'pencil',
|
|
9
|
+
add: 'plus',
|
|
10
|
+
subtract: 'minus',
|
|
11
|
+
cancel: 'ban',
|
|
12
|
+
save: 'file',
|
|
13
|
+
moveup: 'arrow-thick-top',
|
|
14
|
+
moveright: 'arrow-thick-right',
|
|
15
|
+
movedown: 'arrow-thick-bottom',
|
|
16
|
+
moveleft: 'arrow-thick-left',
|
|
17
|
+
copy: 'clipboard',
|
|
18
|
+
clear: 'circle-x',
|
|
19
|
+
time: 'clock',
|
|
20
|
+
calendar: 'calendar',
|
|
21
|
+
edit_properties: 'list'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class openiconicIconlib extends AbstractIconLib {
|
|
25
|
+
constructor () {
|
|
26
|
+
super(iconPrefix, mapping)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AbstractIconLib } from '../iconlib.js'
|
|
2
|
+
|
|
3
|
+
const iconPrefix = 'icon icon-'
|
|
4
|
+
const mapping = {
|
|
5
|
+
collapse: 'arrow-down',
|
|
6
|
+
expand: 'arrow-right',
|
|
7
|
+
delete: 'delete',
|
|
8
|
+
edit: 'edit',
|
|
9
|
+
add: 'plus',
|
|
10
|
+
subtract: 'minus',
|
|
11
|
+
cancel: 'cross',
|
|
12
|
+
save: 'check',
|
|
13
|
+
moveup: 'upward',
|
|
14
|
+
moveright: 'forward',
|
|
15
|
+
movedown: 'downward',
|
|
16
|
+
moveleft: 'back',
|
|
17
|
+
copy: 'copy',
|
|
18
|
+
clear: 'close',
|
|
19
|
+
time: 'time',
|
|
20
|
+
calendar: 'bookmark',
|
|
21
|
+
edit_properties: 'menu'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class spectreIconlib extends AbstractIconLib {
|
|
25
|
+
constructor () {
|
|
26
|
+
super(iconPrefix, mapping)
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/resolvers.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* Use "multiple" as a fall back for everything */
|
|
2
|
+
const defaultResolver = schema => typeof schema.type !== 'string' && 'multiple'
|
|
3
|
+
|
|
4
|
+
/* If the type is not set but properties are defined, we can infer the type is actually object */
|
|
5
|
+
const object = schema => !schema.type && schema.properties && 'object'
|
|
6
|
+
|
|
7
|
+
/* If the type is set and it's a basic type, use the primitive editor */
|
|
8
|
+
const primitive = schema => typeof schema.type === 'string' && schema.type
|
|
9
|
+
|
|
10
|
+
/* Use specialized editor for signatures */
|
|
11
|
+
const signature = schema => schema.type === 'string' && schema.format === 'signature' && 'signature'
|
|
12
|
+
|
|
13
|
+
/* Use the select editor for all boolean values */
|
|
14
|
+
const boolean = schema => {
|
|
15
|
+
if (schema.type === 'boolean') {
|
|
16
|
+
/* If explicitly set to 'checkbox', use that */
|
|
17
|
+
if (schema.format === 'checkbox' || (schema.options && schema.options.checkbox)) return 'checkbox'
|
|
18
|
+
/* Otherwise, default to select menu */
|
|
19
|
+
if (schema.format === 'select2') return 'select2'
|
|
20
|
+
if (schema.format === 'selectize') return 'selectize'
|
|
21
|
+
if (schema.format === 'choices') return 'choices'
|
|
22
|
+
return 'select'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Use the multiple editor for schemas where the `type` is set to "any" */
|
|
27
|
+
const any = schema => schema.type === 'any' && 'multiple'
|
|
28
|
+
|
|
29
|
+
/* Editor for base64 encoded files */
|
|
30
|
+
const base64 = schema => schema.type === 'string' && schema.media && schema.media.binaryEncoding === 'base64' && 'base64'
|
|
31
|
+
|
|
32
|
+
/* Editor for uploading files */
|
|
33
|
+
const upload = schema => schema.type === 'string' && schema.format === 'url' && window.FileReader && schema.options && schema.options.upload === Object(schema.options.upload) && 'upload'
|
|
34
|
+
|
|
35
|
+
/* Use the table editor for arrays with the format set to `table` */
|
|
36
|
+
const table = schema => schema.type === 'array' && schema.format === 'table' && 'table'
|
|
37
|
+
|
|
38
|
+
/* Use the `select` editor for dynamic enumSource enums */
|
|
39
|
+
const enumSource = schema => {
|
|
40
|
+
if (schema.enumSource) {
|
|
41
|
+
if (schema.format === 'radio') return 'radio'
|
|
42
|
+
if (schema.format === 'select2') return 'select2'
|
|
43
|
+
if (schema.format === 'selectize') return 'selectize'
|
|
44
|
+
if (schema.format === 'choices') return 'choices'
|
|
45
|
+
return 'select'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Use the `enum` or `select` editors for schemas with enumerated properties */
|
|
50
|
+
const enumeratedProperties = schema => {
|
|
51
|
+
if (schema.enum) {
|
|
52
|
+
if (schema.type === 'array' || schema.type === 'object') return 'enum'
|
|
53
|
+
if (schema.type === 'number' || schema.type === 'integer' || schema.type === 'string') {
|
|
54
|
+
if (schema.format === 'radio') return 'radio'
|
|
55
|
+
if (schema.format === 'select2') return 'select2'
|
|
56
|
+
if (schema.format === 'selectize') return 'selectize'
|
|
57
|
+
if (schema.format === 'choices') return 'choices'
|
|
58
|
+
return 'select'
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Specialized editors for arrays of strings */
|
|
64
|
+
const arraysOfStrings = schema => {
|
|
65
|
+
if (schema.type === 'array' && schema.items && !(Array.isArray(schema.items)) && ['string', 'number', 'integer'].includes(schema.items.type)) {
|
|
66
|
+
if (schema.format === 'choices') return 'arrayChoices'
|
|
67
|
+
if (schema.uniqueItems) {
|
|
68
|
+
/* if 'selectize' enabled it is expected to be selectized control */
|
|
69
|
+
if (schema.format === 'selectize') return 'arraySelectize'
|
|
70
|
+
if (schema.format === 'select2') return 'arraySelect2'
|
|
71
|
+
if (schema.items.enum) return 'multiselect' /* otherwise it is select */
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Use the multiple editor for schemas with `oneOf` or `anyOf` set */
|
|
77
|
+
const oneOf = schema => (schema.oneOf || schema.anyOf) && 'multiple'
|
|
78
|
+
|
|
79
|
+
/* Use the multiple editor for schemas with `if` set */
|
|
80
|
+
const ifThenElse = schema => (schema.if) && 'multiple'
|
|
81
|
+
|
|
82
|
+
/* Specialized editor for date, time and datetime-local formats */
|
|
83
|
+
const date = schema => ['string', 'integer'].includes(schema.type) && ['date', 'time', 'datetime-local'].includes(schema.format) && 'datetime'
|
|
84
|
+
|
|
85
|
+
/* Use a specialized editor for starratings */
|
|
86
|
+
const starratings = schema => ['string', 'integer'].includes(schema.type) && ['starrating', 'rating'].includes(schema.format) && 'starrating'
|
|
87
|
+
|
|
88
|
+
/* Hyper-link describeBy resolver */
|
|
89
|
+
const describeBy = schema => {
|
|
90
|
+
if (schema.links) {
|
|
91
|
+
for (let i = 0; i < schema.links.length; i++) {
|
|
92
|
+
if (schema.links[i].rel && schema.links[i].rel.toLowerCase() === 'describedby') return 'describedBy'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Use the stepper editor for schemas with type `number` or `integer` and format `stepper` */
|
|
98
|
+
const stepper = schema => {
|
|
99
|
+
if ((schema.type === 'integer' || schema.type === 'number') && schema.format === 'stepper') {
|
|
100
|
+
return 'stepper'
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Enable custom editor type */
|
|
105
|
+
const button = schema => schema.format === 'button' && 'button'
|
|
106
|
+
|
|
107
|
+
const info = schema => schema.format === 'info' && 'info'
|
|
108
|
+
|
|
109
|
+
const uuid = schema => schema.type === 'string' && schema.format === 'uuid' && 'uuid'
|
|
110
|
+
|
|
111
|
+
const autoComplete = schema => schema.type === 'string' && schema.format === 'autocomplete' && 'autocomplete'
|
|
112
|
+
|
|
113
|
+
const jodit = schema => schema.type === 'string' && schema.format === 'jodit' && 'jodit'
|
|
114
|
+
|
|
115
|
+
const markdown = schema => schema.type === 'string' && schema.format === 'markdown' && 'simplemde'
|
|
116
|
+
|
|
117
|
+
const xhtml = schema => schema.type === 'string' && ['xhtml', 'bbcode'].includes(schema.format) && 'sceditor'
|
|
118
|
+
|
|
119
|
+
/* Use the ace editor for schemas with format equals any of ace editor modes */
|
|
120
|
+
const aceModes = ['actionscript', 'batchfile', 'c', 'c++', 'cpp', 'coffee', 'csharp', 'css', 'dart', 'django', 'ejs', 'erlang', 'golang', 'groovy', 'handlebars', 'haskell', 'haxe', 'html', 'ini', 'jade', 'java', 'javascript', 'json', 'less', 'lisp', 'lua', 'makefile', 'matlab', 'mysql', 'objectivec', 'pascal', 'perl', 'pgsql', 'php', 'python', 'r', 'ruby', 'sass', 'scala', 'scss', 'smarty', 'sql', 'sqlserver', 'stylus', 'svg', 'twig', 'vbscript', 'xml', 'yaml']
|
|
121
|
+
const ace = schema => schema.type === 'string' && aceModes.includes(schema.format) && 'ace'
|
|
122
|
+
|
|
123
|
+
const ip = schema => schema.type === 'string' && ['ip', 'ipv4', 'ipv6', 'hostname'].includes(schema.format) && 'ip'
|
|
124
|
+
|
|
125
|
+
const colorPicker = schema => schema.type === 'string' && schema.format === 'color' && 'colorpicker'
|
|
126
|
+
|
|
127
|
+
/* Export resolvers in order of discovery, first to last */
|
|
128
|
+
export const resolvers = [colorPicker, ip, ace, xhtml, markdown, jodit, autoComplete, uuid, info, button, stepper, describeBy, starratings, date, oneOf, ifThenElse, arraysOfStrings, enumeratedProperties, enumSource, table, upload, base64, any, boolean, signature, primitive, object, defaultResolver]
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import { extend, hasOwnProperty } from './utilities.js'
|
|
2
|
+
|
|
3
|
+
export class SchemaLoader {
|
|
4
|
+
constructor (options) {
|
|
5
|
+
this.options = options || {}
|
|
6
|
+
this.refs = this.options.refs || {}
|
|
7
|
+
this.refs_with_info = {}
|
|
8
|
+
this.refs_prefix = '#/counter/'
|
|
9
|
+
this.refs_counter = 1
|
|
10
|
+
|
|
11
|
+
this._subSchema1 = {
|
|
12
|
+
/* Version 3 `type` */
|
|
13
|
+
type (schema) {
|
|
14
|
+
if (typeof schema.type === 'object') {
|
|
15
|
+
schema.type = this._expandSubSchema(schema.type)
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
/* Version 3 `disallow` */
|
|
19
|
+
disallow (schema) {
|
|
20
|
+
if (typeof schema.disallow === 'object') {
|
|
21
|
+
schema.disallow = this._expandSubSchema(schema.disallow)
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
/* Version 4 `anyOf` */
|
|
25
|
+
anyOf (schema) {
|
|
26
|
+
Object.entries(schema.anyOf).forEach(([key, value]) => {
|
|
27
|
+
schema.anyOf[key] = this.expandSchema(value)
|
|
28
|
+
})
|
|
29
|
+
},
|
|
30
|
+
/* Version 4 `dependencies` (schema dependencies) */
|
|
31
|
+
dependencies (schema) {
|
|
32
|
+
Object.entries(schema.dependencies).forEach(([key, value]) => {
|
|
33
|
+
if (typeof value === 'object' && !(Array.isArray(value))) {
|
|
34
|
+
schema.dependencies[key] = this.expandSchema(value)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
},
|
|
38
|
+
/* Version 4 `not` */
|
|
39
|
+
not (schema) {
|
|
40
|
+
schema.not = this.expandSchema(schema.not)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this._subSchema2 = {
|
|
45
|
+
/* allOf schemas should be merged into the parent */
|
|
46
|
+
allOf (schema, extended) {
|
|
47
|
+
let _extended = extend({}, extended)
|
|
48
|
+
Object.entries(schema.allOf).forEach(([key, value]) => {
|
|
49
|
+
schema.allOf[key] = this.expandRefs(value, true)
|
|
50
|
+
_extended = this.extendSchemas(_extended, this.expandSchema(value))
|
|
51
|
+
})
|
|
52
|
+
delete _extended.allOf
|
|
53
|
+
return _extended
|
|
54
|
+
},
|
|
55
|
+
/* extends schemas should be merged into parent */
|
|
56
|
+
extends (schema, extended) {
|
|
57
|
+
let _extended
|
|
58
|
+
/* If extends is a schema */
|
|
59
|
+
if (!(Array.isArray(schema.extends))) {
|
|
60
|
+
_extended = this.extendSchemas(extended, this.expandSchema(schema.extends))
|
|
61
|
+
} else {
|
|
62
|
+
/* If extends is an array of schemas */
|
|
63
|
+
_extended = schema.extends.reduce((e, s, i) => {
|
|
64
|
+
return this.extendSchemas(e, this.expandSchema(s))
|
|
65
|
+
}, extended)
|
|
66
|
+
}
|
|
67
|
+
delete _extended.extends
|
|
68
|
+
return _extended
|
|
69
|
+
},
|
|
70
|
+
/* parent should be merged into oneOf schemas */
|
|
71
|
+
oneOf (schema, extended) {
|
|
72
|
+
const tmp = extend({}, extended)
|
|
73
|
+
delete tmp.oneOf
|
|
74
|
+
schema.oneOf.reduce((e, s, i) => {
|
|
75
|
+
e.oneOf[i] = this.extendSchemas(this.expandSchema(s), tmp)
|
|
76
|
+
return e
|
|
77
|
+
}, extended)
|
|
78
|
+
return extended
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
load (schema, callback, fetchUrl, location) {
|
|
84
|
+
this._loadExternalRefs(schema, () => {
|
|
85
|
+
this._getDefinitions(schema, `${fetchUrl}#/definitions/`)
|
|
86
|
+
callback(this.expandRefs(schema))
|
|
87
|
+
}, fetchUrl, this._getFileBase(location))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
expandRefs (schema, recurseAllOf) {
|
|
91
|
+
if (!schema.$ref) return schema
|
|
92
|
+
const _schema = extend({}, schema)
|
|
93
|
+
|
|
94
|
+
const refObj = this.refs_with_info[_schema.$ref]
|
|
95
|
+
delete _schema.$ref
|
|
96
|
+
const fetchUrl = refObj.$ref.startsWith('#')
|
|
97
|
+
? refObj.fetchUrl
|
|
98
|
+
: ''
|
|
99
|
+
const ref = this._getRef(fetchUrl, refObj)
|
|
100
|
+
if (!this.refs[ref]) { /* if reference not found */
|
|
101
|
+
// eslint-disable-next-line no-console
|
|
102
|
+
console.warn(`reference:'${ref}' not found!`)
|
|
103
|
+
} else if (recurseAllOf && hasOwnProperty(this.refs[ref], 'allOf')) {
|
|
104
|
+
const allOf = this.refs[ref].allOf
|
|
105
|
+
Object.keys(allOf).forEach(key => {
|
|
106
|
+
allOf[key] = this.expandRefs(allOf[key], true)
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return this.extendSchemas(_schema, this.expandSchema(this.refs[ref]))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
expandSchema (schema, fileBase) {
|
|
114
|
+
Object.entries(this._subSchema1).forEach(([key, func]) => {
|
|
115
|
+
if (schema[key]) {
|
|
116
|
+
func.call(this, schema)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
let extended = extend({}, schema)
|
|
121
|
+
|
|
122
|
+
Object.entries(this._subSchema2).forEach(([key, func]) => {
|
|
123
|
+
if (schema[key]) {
|
|
124
|
+
extended = func.call(this, schema, extended)
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return this.expandRefs(extended)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
_getRef (fetchUrl, refObj) {
|
|
132
|
+
const ref = fetchUrl + refObj
|
|
133
|
+
|
|
134
|
+
return this.refs[ref] ? ref : fetchUrl + decodeURIComponent(refObj.$ref)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
_expandSubSchema (subschema) {
|
|
138
|
+
/* Array of types */
|
|
139
|
+
if (Array.isArray(subschema)) return subschema.map(m => typeof m === 'object' ? this.expandSchema(m) : m)
|
|
140
|
+
|
|
141
|
+
/* Schema */
|
|
142
|
+
return this.expandSchema(subschema)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
_getDefinitions (schema, path) {
|
|
146
|
+
if (schema.definitions) {
|
|
147
|
+
Object.keys(schema.definitions).forEach(i => {
|
|
148
|
+
this.refs[path + i] = schema.definitions[i]
|
|
149
|
+
if (schema.definitions[i].definitions) {
|
|
150
|
+
this._getDefinitions(schema.definitions[i], `${path + i}/definitions/`)
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
_getExternalRefs (schema, fetchUrl) {
|
|
157
|
+
const refs = {}
|
|
158
|
+
const mergeRefs = newrefs => Object.keys(newrefs).forEach(i => { refs[i] = true })
|
|
159
|
+
|
|
160
|
+
if (schema.$ref && typeof schema.$ref !== 'object') {
|
|
161
|
+
const refCounter = this.refs_prefix + this.refs_counter++
|
|
162
|
+
if (schema.$ref.substr(0, 1) !== '#' && !this.refs[schema.$ref]) {
|
|
163
|
+
refs[schema.$ref] = true
|
|
164
|
+
}
|
|
165
|
+
this.refs_with_info[refCounter] = { fetchUrl, $ref: schema.$ref }
|
|
166
|
+
schema.$ref = refCounter
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Object.values(schema).forEach(value => {
|
|
170
|
+
if (!value || typeof value !== 'object') return
|
|
171
|
+
if (Array.isArray(value)) {
|
|
172
|
+
Object.values(value).forEach(e => {
|
|
173
|
+
if (e && typeof e === 'object') {
|
|
174
|
+
mergeRefs(this._getExternalRefs(e, fetchUrl))
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
} else {
|
|
178
|
+
mergeRefs(this._getExternalRefs(value, fetchUrl))
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
if (schema.id && typeof schema.id === 'string' && schema.id.substr(0, 4) === 'urn:') {
|
|
183
|
+
this.refs[schema.id] = schema
|
|
184
|
+
} else if (schema.$id && typeof schema.$id === 'string' && schema.$id.substr(0, 4) === 'urn:') {
|
|
185
|
+
this.refs[schema.$id] = schema
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return refs
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
_getFileBase (location) {
|
|
192
|
+
if (!location) return '/'
|
|
193
|
+
|
|
194
|
+
const { ajaxBase } = this.options
|
|
195
|
+
|
|
196
|
+
return typeof ajaxBase === 'undefined' ? this._getFileBaseFromFileLocation(location) : ajaxBase
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_getFileBaseFromFileLocation (fileLocationString) {
|
|
200
|
+
const pathItems = fileLocationString.split('/')
|
|
201
|
+
pathItems.pop()
|
|
202
|
+
return `${pathItems.join('/')}/`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_joinUrl (url, fileBase) {
|
|
206
|
+
var fetchUrl = url
|
|
207
|
+
|
|
208
|
+
if (url.substr(0, 7) !== 'http://' &&
|
|
209
|
+
url.substr(0, 8) !== 'https://' &&
|
|
210
|
+
url.substr(0, 5) !== 'blob:' &&
|
|
211
|
+
url.substr(0, 5) !== 'data:' &&
|
|
212
|
+
url.substr(0, 1) !== '#' &&
|
|
213
|
+
url.substr(0, 1) !== '/'
|
|
214
|
+
) {
|
|
215
|
+
fetchUrl = fileBase + url
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// strip #fragment from URI, so json pointers resolve correctly #928
|
|
219
|
+
if (fetchUrl.indexOf('#') > 0) fetchUrl = fetchUrl.substr(0, fetchUrl.indexOf('#'))
|
|
220
|
+
|
|
221
|
+
return fetchUrl
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
_isUniformResourceName (uri) {
|
|
225
|
+
return uri.substr(0, 4) === 'urn:'
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_loadExternalRefs (schema, callback, fetchUrl, fileBase) {
|
|
229
|
+
const refs = this._getExternalRefs(schema, fetchUrl)
|
|
230
|
+
let done = false; let waiting = 0
|
|
231
|
+
|
|
232
|
+
Object.keys(refs).forEach(uri => {
|
|
233
|
+
if (this.refs[uri]) return
|
|
234
|
+
|
|
235
|
+
if (this._isUniformResourceName(uri)) {
|
|
236
|
+
this.refs[uri] = 'loading'
|
|
237
|
+
waiting++
|
|
238
|
+
|
|
239
|
+
const urnResolver = this.options.urn_resolver
|
|
240
|
+
let urn = uri
|
|
241
|
+
if (typeof urnResolver !== 'function') {
|
|
242
|
+
// eslint-disable-next-line no-console
|
|
243
|
+
console.log(`No "urn_resolver" callback defined to resolve "${urn}"`)
|
|
244
|
+
throw new Error(`Must set urn_resolver option to a callback to resolve ${urn}`)
|
|
245
|
+
}
|
|
246
|
+
// theoretically a URN can contain a JSON pointer
|
|
247
|
+
if (urn.indexOf('#') > 0) urn = urn.substr(0, urn.indexOf('#'))
|
|
248
|
+
let response
|
|
249
|
+
try {
|
|
250
|
+
response = urnResolver(urn, responseText => {
|
|
251
|
+
try {
|
|
252
|
+
schema = JSON.parse(responseText)
|
|
253
|
+
} catch (e) {
|
|
254
|
+
// eslint-disable-next-line no-console
|
|
255
|
+
console.log(e)
|
|
256
|
+
throw new Error(`Failed to parse external ref ${urn}`)
|
|
257
|
+
}
|
|
258
|
+
if (!(typeof schema === 'boolean' || typeof schema === 'object') || schema === null || Array.isArray(schema)) {
|
|
259
|
+
throw new Error(`External ref does not contain a valid schema - ${urn}`)
|
|
260
|
+
}
|
|
261
|
+
this.refs[uri] = schema
|
|
262
|
+
this._getDefinitions(schema, `${urn}#/definitions/`)
|
|
263
|
+
this._loadExternalRefs(schema, () => {
|
|
264
|
+
waiting--
|
|
265
|
+
if (done && !waiting) {
|
|
266
|
+
callback()
|
|
267
|
+
}
|
|
268
|
+
}, uri, '/')
|
|
269
|
+
})
|
|
270
|
+
} catch (e) {
|
|
271
|
+
// eslint-disable-next-line no-console
|
|
272
|
+
console.log(e)
|
|
273
|
+
throw new Error(`Failed to parse external ref ${urn}`)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (typeof response !== 'boolean') {
|
|
277
|
+
throw new Error(`External ref does not contain a valid schema - ${urn}`)
|
|
278
|
+
} else if (response !== true) {
|
|
279
|
+
throw new Error(`External ref did not resolve - ${urn}`)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!this.options.ajax) throw new Error(`Must set ajax option to true to load external ref ${uri}`)
|
|
286
|
+
this.refs[uri] = 'loading'
|
|
287
|
+
waiting++
|
|
288
|
+
let url = this._joinUrl(uri, fileBase)
|
|
289
|
+
|
|
290
|
+
const r = new XMLHttpRequest()
|
|
291
|
+
r.overrideMimeType('application/json')
|
|
292
|
+
r.open('GET', url, true)
|
|
293
|
+
if (this.options.ajaxCredentials) r.withCredentials = this.options.ajaxCredentials
|
|
294
|
+
r.onreadystatechange = () => {
|
|
295
|
+
if (r.readyState !== 4) return
|
|
296
|
+
/* Request succeeded */
|
|
297
|
+
if (r.status === 200) {
|
|
298
|
+
let schema
|
|
299
|
+
try {
|
|
300
|
+
schema = JSON.parse(r.responseText)
|
|
301
|
+
} catch (e) {
|
|
302
|
+
// eslint-disable-next-line no-console
|
|
303
|
+
console.log(e)
|
|
304
|
+
throw new Error(`Failed to parse external ref ${url}`)
|
|
305
|
+
}
|
|
306
|
+
if (!(typeof schema === 'boolean' || typeof schema === 'object') || schema === null || Array.isArray(schema)) {
|
|
307
|
+
throw new Error(`External ref does not contain a valid schema - ${url}`)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
this.refs[uri] = schema
|
|
311
|
+
const fileBase = this._getFileBaseFromFileLocation(url)
|
|
312
|
+
|
|
313
|
+
// add leading slash
|
|
314
|
+
if (url !== uri) {
|
|
315
|
+
const pathItems = url.split('/')
|
|
316
|
+
url = (uri.substr(0, 1) === '/' ? '/' : '') + pathItems.pop()
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
this._getDefinitions(schema, `${url}#/definitions/`)
|
|
320
|
+
this._loadExternalRefs(schema, () => {
|
|
321
|
+
waiting--
|
|
322
|
+
if (done && !waiting) {
|
|
323
|
+
callback()
|
|
324
|
+
}
|
|
325
|
+
}, url, fileBase)
|
|
326
|
+
} else {
|
|
327
|
+
/* Request failed */
|
|
328
|
+
// eslint-disable-next-line no-console
|
|
329
|
+
console.log(r)
|
|
330
|
+
throw new Error(`Failed to fetch ref via ajax - ${uri}`)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
r.send()
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
done = true
|
|
337
|
+
if (!waiting) {
|
|
338
|
+
callback()
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
extendSchemas (obj1, obj2) {
|
|
343
|
+
obj1 = extend({}, obj1)
|
|
344
|
+
obj2 = extend({}, obj2)
|
|
345
|
+
|
|
346
|
+
const extended = {}
|
|
347
|
+
const isRequiredOrDefaultProperties = (prop, val) => (prop === 'required' || prop === 'defaultProperties') && typeof val === 'object' && Array.isArray(val)
|
|
348
|
+
const merge = (prop, val) => {
|
|
349
|
+
/* Required and defaultProperties arrays should be unioned together */
|
|
350
|
+
if (isRequiredOrDefaultProperties(prop, val)) {
|
|
351
|
+
/* Union arrays and unique */
|
|
352
|
+
extended[prop] = val.concat(obj2[prop]).reduce((p, c) => {
|
|
353
|
+
if (!p.includes(c)) p.push(c)
|
|
354
|
+
return p
|
|
355
|
+
}, [])
|
|
356
|
+
} else if (prop === 'type' && (typeof val === 'string' || Array.isArray(val))) {
|
|
357
|
+
mergeType(val)
|
|
358
|
+
} else if (typeof val === 'object' && !Array.isArray(val) && val !== null) {
|
|
359
|
+
/* Objects should be recursively merged */
|
|
360
|
+
extended[prop] = this.extendSchemas(val, obj2[prop])
|
|
361
|
+
} else {
|
|
362
|
+
/* Otherwise, use the first value */
|
|
363
|
+
extended[prop] = val
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const mergeType = (val) => {
|
|
368
|
+
/* Type should be intersected and is either an array or string */
|
|
369
|
+
/* Make sure we're dealing with arrays */
|
|
370
|
+
if (typeof val === 'string') val = [val]
|
|
371
|
+
if (typeof obj2.type === 'string') obj2.type = [obj2.type]
|
|
372
|
+
|
|
373
|
+
/* If type is only defined in the first schema, keep it */
|
|
374
|
+
if (!obj2.type || !obj2.type.length) {
|
|
375
|
+
extended.type = val
|
|
376
|
+
} else {
|
|
377
|
+
/* If type is defined in both schemas, do an intersect */
|
|
378
|
+
extended.type = val.filter(n => obj2.type.includes(n))
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* If there's only 1 type and it's a primitive, use a string instead of array */
|
|
382
|
+
if (extended.type.length === 1 && typeof extended.type[0] === 'string') {
|
|
383
|
+
extended.type = extended.type[0]
|
|
384
|
+
} else if (extended.type.length === 0) {
|
|
385
|
+
/* Remove the type property if it's empty */
|
|
386
|
+
delete extended.type
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
Object.entries(obj1).forEach(([prop, val]) => {
|
|
391
|
+
/* If this key is also defined in obj2, merge them */
|
|
392
|
+
if (typeof obj2[prop] !== 'undefined') {
|
|
393
|
+
merge(prop, val)
|
|
394
|
+
} else {
|
|
395
|
+
/* Otherwise, just use the one in obj1 */
|
|
396
|
+
extended[prop] = val
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
/* Properties in obj2 that aren't in obj1 */
|
|
401
|
+
Object.entries(obj2).forEach(([prop, val]) => {
|
|
402
|
+
if (typeof obj1[prop] === 'undefined') {
|
|
403
|
+
extended[prop] = val
|
|
404
|
+
}
|
|
405
|
+
})
|
|
406
|
+
return extended
|
|
407
|
+
}
|
|
408
|
+
}
|