@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.
Files changed (283) hide show
  1. package/.env-dist +2 -0
  2. package/.eslintrc +7 -0
  3. package/.gitattributes +1 -0
  4. package/.github/PULL_REQUEST_TEMPLATE.md +9 -0
  5. package/.github/issue_template +25 -0
  6. package/.github/workflows/build.yml +58 -0
  7. package/.travis.yml +70 -0
  8. package/CHANGELOG.md +915 -0
  9. package/CONTRIBUTING.md +92 -0
  10. package/LICENSE +20 -0
  11. package/Makefile +26 -0
  12. package/README.md +1646 -0
  13. package/README_ADDON.md +573 -0
  14. package/UPGRADING.md +46 -0
  15. package/build/CssToJson.js +55 -0
  16. package/codecept.conf.js +35 -0
  17. package/config/.eslintrc +7 -0
  18. package/config/codeceptjs_helpers.js +139 -0
  19. package/config/helpers.js +10 -0
  20. package/config/karma.conf.js +93 -0
  21. package/config/readme.md +31 -0
  22. package/config/webpack.common.js +75 -0
  23. package/config/webpack.dev.js +15 -0
  24. package/config/webpack.nonmin.js +19 -0
  25. package/config/webpack.prod.js +25 -0
  26. package/dist/jsoneditor.js +14 -0
  27. package/dist/nonmin/jsoneditor.js +29097 -0
  28. package/dist/nonmin/jsoneditor.js.map +1 -0
  29. package/docs/ace_editor.html +56 -0
  30. package/docs/advanced.html +136 -0
  31. package/docs/basic.html +63 -0
  32. package/docs/basic_person.json +26 -0
  33. package/docs/choices.html +86 -0
  34. package/docs/cleave.html +132 -0
  35. package/docs/colorpicker.html +194 -0
  36. package/docs/css_integration.html +135 -0
  37. package/docs/datetime.html +305 -0
  38. package/docs/describedby.html +161 -0
  39. package/docs/enumsource.html +67 -0
  40. package/docs/images/categoriesDemo.png +0 -0
  41. package/docs/images/inheritance_tree.png +0 -0
  42. package/docs/images/jsoneditor.png +0 -0
  43. package/docs/imask.html +192 -0
  44. package/docs/index.html +579 -0
  45. package/docs/materialize_css.html +164 -0
  46. package/docs/meta_schema.json +705 -0
  47. package/docs/multiple_upload_base64.html +65 -0
  48. package/docs/person.json +73 -0
  49. package/docs/polyfills/assign.js +29 -0
  50. package/docs/radio.html +156 -0
  51. package/docs/recursive.html +170 -0
  52. package/docs/select2.html +99 -0
  53. package/docs/selectize.html +100 -0
  54. package/docs/signature.html +42 -0
  55. package/docs/starrating.html +137 -0
  56. package/docs/upload.html +131 -0
  57. package/docs/uuid.html +70 -0
  58. package/docs/wysiwyg.html +56 -0
  59. package/jasmine.json +11 -0
  60. package/json-editor-json-editor-2.5.3-wb13.tgz +0 -0
  61. package/package.json +100 -0
  62. package/release-notes.md +88 -0
  63. package/src/core.js +412 -0
  64. package/src/defaults.js +402 -0
  65. package/src/editor.js +707 -0
  66. package/src/editors/ace.js +89 -0
  67. package/src/editors/array/choices.js +103 -0
  68. package/src/editors/array/select2.js +110 -0
  69. package/src/editors/array/selectize.js +103 -0
  70. package/src/editors/array.css +9 -0
  71. package/src/editors/array.css.js +3 -0
  72. package/src/editors/array.js +818 -0
  73. package/src/editors/autocomplete.js +58 -0
  74. package/src/editors/base64.js +157 -0
  75. package/src/editors/button.js +97 -0
  76. package/src/editors/checkbox.js +95 -0
  77. package/src/editors/choices.css +3 -0
  78. package/src/editors/choices.css.js +3 -0
  79. package/src/editors/choices.js +69 -0
  80. package/src/editors/colorpicker.js +103 -0
  81. package/src/editors/datetime.js +141 -0
  82. package/src/editors/describedby.js +188 -0
  83. package/src/editors/enum.js +136 -0
  84. package/src/editors/hidden.js +127 -0
  85. package/src/editors/index.js +81 -0
  86. package/src/editors/info.js +20 -0
  87. package/src/editors/integer.js +19 -0
  88. package/src/editors/ip.js +36 -0
  89. package/src/editors/jodit.js +64 -0
  90. package/src/editors/multiple.js +409 -0
  91. package/src/editors/multiselect.js +218 -0
  92. package/src/editors/null.js +18 -0
  93. package/src/editors/number.js +51 -0
  94. package/src/editors/object.css +41 -0
  95. package/src/editors/object.css.js +3 -0
  96. package/src/editors/object.js +1290 -0
  97. package/src/editors/radio.js +111 -0
  98. package/src/editors/sceditor.js +72 -0
  99. package/src/editors/select.js +370 -0
  100. package/src/editors/select2.js +110 -0
  101. package/src/editors/selectize.js +112 -0
  102. package/src/editors/signature.js +113 -0
  103. package/src/editors/simplemde.js +100 -0
  104. package/src/editors/starrating.css +52 -0
  105. package/src/editors/starrating.css.js +3 -0
  106. package/src/editors/starrating.js +135 -0
  107. package/src/editors/stepper.js +27 -0
  108. package/src/editors/string.js +372 -0
  109. package/src/editors/table.js +516 -0
  110. package/src/editors/upload.js +321 -0
  111. package/src/editors/uuid.js +56 -0
  112. package/src/iconlib.js +24 -0
  113. package/src/iconlibs/bootstrap2.js +28 -0
  114. package/src/iconlibs/bootstrap3.js +28 -0
  115. package/src/iconlibs/fontawesome3.js +28 -0
  116. package/src/iconlibs/fontawesome4.js +28 -0
  117. package/src/iconlibs/fontawesome5.js +28 -0
  118. package/src/iconlibs/foundation2.js +24 -0
  119. package/src/iconlibs/foundation3.js +28 -0
  120. package/src/iconlibs/index.js +25 -0
  121. package/src/iconlibs/jqueryui.js +28 -0
  122. package/src/iconlibs/materialicons.js +49 -0
  123. package/src/iconlibs/openiconic.js +28 -0
  124. package/src/iconlibs/spectre.js +28 -0
  125. package/src/resolvers.js +128 -0
  126. package/src/schemaloader.js +408 -0
  127. package/src/style.css +150 -0
  128. package/src/style.css.js +3 -0
  129. package/src/templates/default.js +52 -0
  130. package/src/templates/ejs.js +13 -0
  131. package/src/templates/handlebars.js +1 -0
  132. package/src/templates/hogan.js +10 -0
  133. package/src/templates/index.js +21 -0
  134. package/src/templates/lodash.js +9 -0
  135. package/src/templates/markup.js +9 -0
  136. package/src/templates/mustache.js +9 -0
  137. package/src/templates/swig.js +1 -0
  138. package/src/templates/underscore.js +9 -0
  139. package/src/theme.js +659 -0
  140. package/src/themes/barebones.css +35 -0
  141. package/src/themes/barebones.css.js +3 -0
  142. package/src/themes/barebones.js +28 -0
  143. package/src/themes/bootstrap2.js +319 -0
  144. package/src/themes/bootstrap3.css +0 -0
  145. package/src/themes/bootstrap3.css.js +3 -0
  146. package/src/themes/bootstrap3.js +315 -0
  147. package/src/themes/bootstrap4.css +89 -0
  148. package/src/themes/bootstrap4.css.js +3 -0
  149. package/src/themes/bootstrap4.js +690 -0
  150. package/src/themes/bootstrap5.css.js +3 -0
  151. package/src/themes/foundation.js +569 -0
  152. package/src/themes/html.css +60 -0
  153. package/src/themes/html.css.js +3 -0
  154. package/src/themes/html.js +71 -0
  155. package/src/themes/index.js +28 -0
  156. package/src/themes/jqueryui.js +198 -0
  157. package/src/themes/materialize.js +426 -0
  158. package/src/themes/spectre.css +208 -0
  159. package/src/themes/spectre.css.js +3 -0
  160. package/src/themes/spectre.js +406 -0
  161. package/src/themes/tailwind.css +249 -0
  162. package/src/themes/tailwind.css.js +3 -0
  163. package/src/themes/tailwind.js +443 -0
  164. package/src/utilities.js +138 -0
  165. package/src/validator.js +877 -0
  166. package/src/validators/ip-validator.js +51 -0
  167. package/tests/Dockerfile +3 -0
  168. package/tests/README.md +48 -0
  169. package/tests/codeceptjs/codecept.json +42 -0
  170. package/tests/codeceptjs/constrains/if-then-else_test.js +143 -0
  171. package/tests/codeceptjs/core_test.js +217 -0
  172. package/tests/codeceptjs/editors/advanced_test.js +13 -0
  173. package/tests/codeceptjs/editors/array_any_of_test.js +50 -0
  174. package/tests/codeceptjs/editors/array_test.js +900 -0
  175. package/tests/codeceptjs/editors/button_test.js +35 -0
  176. package/tests/codeceptjs/editors/checkbox_test.js +21 -0
  177. package/tests/codeceptjs/editors/colorpicker_test.js +27 -0
  178. package/tests/codeceptjs/editors/datetime_test.js +33 -0
  179. package/tests/codeceptjs/editors/inheritance_test.js +11 -0
  180. package/tests/codeceptjs/editors/integer_test.js +84 -0
  181. package/tests/codeceptjs/editors/issues/issue-gh-812_test.js +32 -0
  182. package/tests/codeceptjs/editors/jodit_test.js +24 -0
  183. package/tests/codeceptjs/editors/multiselect_test.js +8 -0
  184. package/tests/codeceptjs/editors/number_test.js +82 -0
  185. package/tests/codeceptjs/editors/object_test.js +204 -0
  186. package/tests/codeceptjs/editors/option-no_default_values_test.js +42 -0
  187. package/tests/codeceptjs/editors/programmatic-changes_test.js +20 -0
  188. package/tests/codeceptjs/editors/radio_test.js +10 -0
  189. package/tests/codeceptjs/editors/rating_test.js +13 -0
  190. package/tests/codeceptjs/editors/select_test.js +22 -0
  191. package/tests/codeceptjs/editors/stepper_test.js +27 -0
  192. package/tests/codeceptjs/editors/string_test.js +118 -0
  193. package/tests/codeceptjs/editors/table-confirm-delete_test.js +67 -0
  194. package/tests/codeceptjs/editors/tabs_test.js +14 -0
  195. package/tests/codeceptjs/editors/uuid_test.js +21 -0
  196. package/tests/codeceptjs/editors/validation_test.js +14 -0
  197. package/tests/codeceptjs/meta-schema_test.js +17 -0
  198. package/tests/codeceptjs/schemaloader_test.js +13 -0
  199. package/tests/codeceptjs/steps.d.ts +13 -0
  200. package/tests/codeceptjs/steps_file.js +12 -0
  201. package/tests/codeceptjs/themes_test.js +519 -0
  202. package/tests/docker-compose.yml +34 -0
  203. package/tests/fixtures/basic_person.json +26 -0
  204. package/tests/fixtures/nested_object.json +26 -0
  205. package/tests/fixtures/person.json +55 -0
  206. package/tests/fixtures/recursive.json +8 -0
  207. package/tests/fixtures/some_types.json +32 -0
  208. package/tests/fixtures/string.json +3 -0
  209. package/tests/fixtures/validation.json +1140 -0
  210. package/tests/pages/_demo.html +475 -0
  211. package/tests/pages/advanced.html +137 -0
  212. package/tests/pages/anyof.html +80 -0
  213. package/tests/pages/array-anyof.html +142 -0
  214. package/tests/pages/array-checkboxes.html +41 -0
  215. package/tests/pages/array-choices.html +45 -0
  216. package/tests/pages/array-integers.html +37 -0
  217. package/tests/pages/array-move-events.html +61 -0
  218. package/tests/pages/array-multiselects.html +42 -0
  219. package/tests/pages/array-nested-arrays.html +40 -0
  220. package/tests/pages/array-numbers.html +37 -0
  221. package/tests/pages/array-objects.html +42 -0
  222. package/tests/pages/array-ratings.html +40 -0
  223. package/tests/pages/array-selectize.html +51 -0
  224. package/tests/pages/array-selects.html +36 -0
  225. package/tests/pages/array-strings.html +36 -0
  226. package/tests/pages/array.html +42 -0
  227. package/tests/pages/assets/pages.css +130 -0
  228. package/tests/pages/button-callbacks.html +77 -0
  229. package/tests/pages/checkbox-labels.html +114 -0
  230. package/tests/pages/colorpicker-no-3rd-party.html +43 -0
  231. package/tests/pages/colorpicker-use-vanilla-picker.html +50 -0
  232. package/tests/pages/core.html +118 -0
  233. package/tests/pages/datetime.html +76 -0
  234. package/tests/pages/form-name.html +108 -0
  235. package/tests/pages/grid-strict.html +311 -0
  236. package/tests/pages/grid.html +284 -0
  237. package/tests/pages/if-then-else-allOf.html +117 -0
  238. package/tests/pages/inheritance.html +76 -0
  239. package/tests/pages/integer.html +68 -0
  240. package/tests/pages/issues/_template.html +50 -0
  241. package/tests/pages/issues/issue-gh-812.html +110 -0
  242. package/tests/pages/issues/issue-gh-823-meta-schema.html +35 -0
  243. package/tests/pages/issues/issue-gh-848.html +81 -0
  244. package/tests/pages/meta_schema.json +705 -0
  245. package/tests/pages/number.html +89 -0
  246. package/tests/pages/object-no-additional-properties.html +65 -0
  247. package/tests/pages/object-no-duplicated-id.html +68 -0
  248. package/tests/pages/object-required-properties.html +236 -0
  249. package/tests/pages/object-with-dependencies-array.html +46 -0
  250. package/tests/pages/object-with-dependencies.html +60 -0
  251. package/tests/pages/object.html +79 -0
  252. package/tests/pages/oneof.html +103 -0
  253. package/tests/pages/option-no_default_values.html +58 -0
  254. package/tests/pages/programmatic-changes.html +120 -0
  255. package/tests/pages/read-only.html +105 -0
  256. package/tests/pages/select.html +41 -0
  257. package/tests/pages/stepper.html +59 -0
  258. package/tests/pages/string-ace-editor.html +52 -0
  259. package/tests/pages/string-cleave.html +46 -0
  260. package/tests/pages/string-custom-attributes.html +62 -0
  261. package/tests/pages/string-formats.html +52 -0
  262. package/tests/pages/string-formats2.html +57 -0
  263. package/tests/pages/string-jodit-editor.html +49 -0
  264. package/tests/pages/string-sceditor.html +62 -0
  265. package/tests/pages/table-move-events.html +56 -0
  266. package/tests/pages/table.html +46 -0
  267. package/tests/pages/tabs.html +131 -0
  268. package/tests/pages/themes.html +527 -0
  269. package/tests/pages/translate-property.html +247 -0
  270. package/tests/pages/urn.html +93 -0
  271. package/tests/pages/uuid.html +72 -0
  272. package/tests/pages/validation.html +99 -0
  273. package/tests/unit/.eslintrc +8 -0
  274. package/tests/unit/core.spec.js +309 -0
  275. package/tests/unit/defaults.spec.js +40 -0
  276. package/tests/unit/editor.spec.js +160 -0
  277. package/tests/unit/editors/array.spec.js +86 -0
  278. package/tests/unit/editors/object.spec.js +79 -0
  279. package/tests/unit/editors/table.spec.js +91 -0
  280. package/tests/unit/readme.md +35 -0
  281. package/tests/unit/schemaloader.spec.js +498 -0
  282. package/tests/unit/validator.spec.js +94 -0
  283. package/tests/unit/validators/ip-validator.spec.js +62 -0
@@ -0,0 +1,877 @@
1
+ import { ipValidator } from './validators/ip-validator.js'
2
+ import { extend, hasOwnProperty } from './utilities.js'
3
+
4
+ export class Validator {
5
+ constructor (jsoneditor, schema, options, defaults) {
6
+ this.jsoneditor = jsoneditor
7
+ /* Work on a copy of the schema */
8
+ this.schema = extend({}, schema || this.jsoneditor.schema)
9
+ this.options = options || {}
10
+ this.translate = this.jsoneditor.translate || defaults.translate
11
+ this.translateProperty = this.jsoneditor.translateProperty || defaults.translateProperty
12
+ this.defaults = defaults
13
+
14
+ this._validateSubSchema = {
15
+ const (schema, value, path) {
16
+ const valid = JSON.stringify(schema.const) === JSON.stringify(value) && !(Array.isArray(value) || typeof value === 'object')
17
+ if (!valid) {
18
+ return [{
19
+ path,
20
+ property: 'const',
21
+ message: this.translate('error_const')
22
+ }]
23
+ }
24
+ return []
25
+ },
26
+ enum (schema, value, path) {
27
+ const stringified = JSON.stringify(value)
28
+ const valid = schema.enum.some(e => stringified === JSON.stringify(e))
29
+ if (!valid) {
30
+ return [{
31
+ path,
32
+ property: 'enum',
33
+ message: this.translate('error_enum')
34
+ }]
35
+ }
36
+ return []
37
+ },
38
+ extends (schema, value, path) {
39
+ const validate = (errors, e) => {
40
+ errors.push(...this._validateSchema(e, value, path))
41
+ return errors
42
+ }
43
+ return schema.extends.reduce(validate, [])
44
+ },
45
+ allOf (schema, value, path) {
46
+ const validate = (errors, e) => {
47
+ errors.push(...this._validateSchema(e, value, path))
48
+ return errors
49
+ }
50
+ return schema.allOf.reduce(validate, [])
51
+ },
52
+ anyOf (schema, value, path) {
53
+ const valid = schema.anyOf.some(e => !this._validateSchema(e, value, path).length)
54
+ if (!valid) {
55
+ return [{
56
+ path,
57
+ property: 'anyOf',
58
+ message: this.translate('error_anyOf')
59
+ }]
60
+ }
61
+ return []
62
+ },
63
+ oneOf (schema, value, path) {
64
+ let valid = 0
65
+ const oneofErrors = []
66
+ schema.oneOf.forEach((o, i) => {
67
+ /* Set the error paths to be path.oneOf[i].rest.of.path */
68
+ const tmp = this._validateSchema(o, value, path)
69
+ if (!tmp.length) {
70
+ valid++
71
+ }
72
+
73
+ tmp.forEach(e => {
74
+ e.path = `${path}.oneOf[${i}]${e.path.substr(path.length)}`
75
+ })
76
+ oneofErrors.push(...tmp)
77
+ })
78
+ const errors = []
79
+ if (valid !== 1) {
80
+ errors.push({
81
+ path,
82
+ property: 'oneOf',
83
+ message: this.translate('error_oneOf', [valid])
84
+ })
85
+ errors.push(...oneofErrors)
86
+ }
87
+ return errors
88
+ },
89
+ not (schema, value, path) {
90
+ if (!this._validateSchema(schema.not, value, path).length) {
91
+ return [{
92
+ path,
93
+ property: 'not',
94
+ message: this.translate('error_not')
95
+ }]
96
+ }
97
+ return []
98
+ },
99
+ type (schema, value, path) {
100
+ /* Union type */
101
+ if (Array.isArray(schema.type)) {
102
+ const valid = schema.type.some(e => this._checkType(e, value))
103
+ if (!valid) {
104
+ return [{
105
+ path,
106
+ property: 'type',
107
+ message: this.translate('error_type_union')
108
+ }]
109
+ }
110
+ } else {
111
+ /* Simple type */
112
+ if (['date', 'time', 'datetime-local'].includes(schema.format) && schema.type === 'integer') {
113
+ /* Hack to get validator to validate as string even if value is integer */
114
+ /* As validation of 'date', 'time', 'datetime-local' is done in separate validator */
115
+ if (!this._checkType('string', `${value}`)) {
116
+ return [{
117
+ path,
118
+ property: 'type',
119
+ message: this.translate('error_type', [schema.format])
120
+ }]
121
+ }
122
+ } else if (!this._checkType(schema.type, value)) {
123
+ return [{
124
+ path,
125
+ property: 'type',
126
+ message: this.translate('error_type', [schema.type])
127
+ }]
128
+ }
129
+ }
130
+ return []
131
+ },
132
+ disallow (schema, value, path) {
133
+ /* Union type */
134
+ if (Array.isArray(schema.disallow)) {
135
+ const invalid = schema.disallow.some(e => this._checkType(e, value))
136
+ if (invalid) {
137
+ return [{
138
+ path,
139
+ property: 'disallow',
140
+ message: this.translate('error_disallow_union')
141
+ }]
142
+ }
143
+ } else {
144
+ /* Simple type */
145
+ if (this._checkType(schema.disallow, value)) {
146
+ return [{
147
+ path,
148
+ property: 'disallow',
149
+ message: this.translate('error_disallow', [schema.disallow])
150
+ }]
151
+ }
152
+ }
153
+ return []
154
+ }
155
+ }
156
+
157
+ this._validateNumberSubSchema = {
158
+ multipleOf (schema, value, path) { return this._validateNumberSubSchemaMultipleDivisible(schema, value, path) },
159
+ divisibleBy (schema, value, path) { return this._validateNumberSubSchemaMultipleDivisible(schema, value, path) },
160
+ maximum (schema, value, path) {
161
+ /* Vanilla JS, prone to floating point rounding errors (e.g. .999999999999999 == 1) */
162
+ let valid = schema.exclusiveMaximum ? (value < schema.maximum) : (value <= schema.maximum)
163
+
164
+ /* Use math.js is available */
165
+ if (window.math) {
166
+ valid = window.math[schema.exclusiveMaximum ? 'smaller' : 'smallerEq'](
167
+ window.math.bignumber(value),
168
+ window.math.bignumber(schema.maximum)
169
+ )
170
+ } else if (window.Decimal) {
171
+ /* Use Decimal.js if available */
172
+ valid = (new window.Decimal(value))[schema.exclusiveMaximum ? 'lt' : 'lte'](new window.Decimal(schema.maximum))
173
+ }
174
+
175
+ if (!valid) {
176
+ return [{
177
+ path,
178
+ property: 'maximum',
179
+ message: this.translate(
180
+ (schema.exclusiveMaximum ? 'error_maximum_excl' : 'error_maximum_incl'),
181
+ [schema.maximum]
182
+ )
183
+ }]
184
+ }
185
+ return []
186
+ },
187
+ minimum (schema, value, path) {
188
+ /* Vanilla JS, prone to floating point rounding errors (e.g. .999999999999999 == 1) */
189
+ let valid = schema.exclusiveMinimum ? (value > schema.minimum) : (value >= schema.minimum)
190
+
191
+ /* Use math.js is available */
192
+ if (window.math) {
193
+ valid = window.math[schema.exclusiveMinimum ? 'larger' : 'largerEq'](
194
+ window.math.bignumber(value),
195
+ window.math.bignumber(schema.minimum)
196
+ )
197
+ /* Use Decimal.js if available */
198
+ } else if (window.Decimal) {
199
+ valid = (new window.Decimal(value))[schema.exclusiveMinimum ? 'gt' : 'gte'](new window.Decimal(schema.minimum))
200
+ }
201
+
202
+ if (!valid) {
203
+ return [{
204
+ path,
205
+ property: 'minimum',
206
+ message: this.translate(
207
+ (schema.exclusiveMinimum ? 'error_minimum_excl' : 'error_minimum_incl'),
208
+ [schema.minimum]
209
+ )
210
+ }]
211
+ }
212
+ return []
213
+ }
214
+ }
215
+
216
+ this._validateStringSubSchema = {
217
+ maxLength (schema, value, path) {
218
+ const errors = []
219
+ if ((`${value}`).length > schema.maxLength) {
220
+ errors.push({
221
+ path,
222
+ property: 'maxLength',
223
+ message: this.translate('error_maxLength', [schema.maxLength])
224
+ })
225
+ }
226
+ return errors
227
+ },
228
+ /* `minLength` */
229
+ minLength (schema, value, path) {
230
+ if ((`${value}`).length < schema.minLength) {
231
+ return [{
232
+ path,
233
+ property: 'minLength',
234
+ message: this.translate((schema.minLength === 1 ? 'error_notempty' : 'error_minLength'), [schema.minLength])
235
+ }]
236
+ }
237
+ return []
238
+ },
239
+ /* `pattern` */
240
+ pattern (schema, value, path) {
241
+ if (!(new RegExp(schema.pattern)).test(value)) {
242
+ return [{
243
+ path,
244
+ property: 'pattern',
245
+ message: (schema.options && schema.options.patternmessage) ? this.translateProperty(schema.options.patternmessage) : this.translate('error_pattern', [schema.pattern])
246
+ }]
247
+ }
248
+ return []
249
+ }
250
+ }
251
+
252
+ this._validateArraySubSchema = {
253
+ items (schema, value, path) {
254
+ const errors = []
255
+ if (Array.isArray(schema.items)) {
256
+ for (let i = 0; i < value.length; i++) {
257
+ /* If this item has a specific schema tied to it */
258
+ /* Validate against it */
259
+ if (schema.items[i]) {
260
+ errors.push(...this._validateSchema(schema.items[i], value[i], `${path}.${i}`))
261
+ /* If all additional items are allowed */
262
+ } else if (schema.additionalItems === true) {
263
+ break
264
+ /* If additional items is a schema */
265
+ /* TODO: Incompatibility between version 3 and 4 of the spec */
266
+ } else if (schema.additionalItems) {
267
+ errors.push(...this._validateSchema(schema.additionalItems, value[i], `${path}.${i}`))
268
+ /* If no additional items are allowed */
269
+ } else if (schema.additionalItems === false) {
270
+ errors.push({
271
+ path,
272
+ property: 'additionalItems',
273
+ message: this.translate('error_additionalItems')
274
+ })
275
+ break
276
+ /* Default for `additionalItems` is an empty schema */
277
+ } else {
278
+ break
279
+ }
280
+ }
281
+ /* `items` is a schema */
282
+ } else {
283
+ /* Each item in the array must validate against the schema */
284
+ value.forEach((e, i) => {
285
+ errors.push(...this._validateSchema(schema.items, e, `${path}.${i}`))
286
+ })
287
+ }
288
+ return errors
289
+ },
290
+ maxItems (schema, value, path) {
291
+ if (value.length > schema.maxItems) {
292
+ return [{
293
+ path,
294
+ property: 'maxItems',
295
+ message: this.translate('error_maxItems', [schema.maxItems])
296
+ }]
297
+ }
298
+ return []
299
+ },
300
+ minItems (schema, value, path) {
301
+ if (value.length < schema.minItems) {
302
+ return [{
303
+ path,
304
+ property: 'minItems',
305
+ message: this.translate('error_minItems', [schema.minItems])
306
+ }]
307
+ }
308
+ return []
309
+ },
310
+ uniqueItems (schema, value, path) {
311
+ const seen = {}
312
+ for (let i = 0; i < value.length; i++) {
313
+ const valid = JSON.stringify(value[i])
314
+ if (seen[valid]) {
315
+ return [{
316
+ path,
317
+ property: 'uniqueItems',
318
+ message: this.translate('error_uniqueItems')
319
+ }]
320
+ }
321
+ seen[valid] = true
322
+ }
323
+ return []
324
+ }
325
+ }
326
+
327
+ this._validateObjectSubSchema = {
328
+ maxProperties (schema, value, path) {
329
+ if (Object.keys(value).length > schema.maxProperties) {
330
+ return [{
331
+ path,
332
+ property: 'maxProperties',
333
+ message: this.translate('error_maxProperties', [schema.maxProperties])
334
+ }]
335
+ }
336
+ return []
337
+ },
338
+ minProperties (schema, value, path) {
339
+ if (Object.keys(value).length < schema.minProperties) {
340
+ return [{
341
+ path,
342
+ property: 'minProperties',
343
+ message: this.translate('error_minProperties', [schema.minProperties])
344
+ }]
345
+ }
346
+ return []
347
+ },
348
+ required (schema, value, path) {
349
+ const errors = []
350
+ if (Array.isArray(schema.required)) {
351
+ schema.required.forEach(e => {
352
+ if (typeof value[e] !== 'undefined') return
353
+ const editor = this.jsoneditor.getEditor(`${path}.${e}`)
354
+ /* Ignore required error if editor is of type "button" or "info" */
355
+ if (editor && ['button', 'info'].includes(editor.schema.format || editor.schema.type)) return
356
+ errors.push({
357
+ path,
358
+ property: 'required',
359
+ message: this.translate('error_required', [schema && schema.properties && schema.properties[e] && schema.properties[e].title ? schema.properties[e].title : e])
360
+ })
361
+ })
362
+ }
363
+ return errors
364
+ },
365
+ properties (schema, value, path, validatedProperties) {
366
+ const errors = []
367
+ Object.entries(schema.properties).forEach(([key, prop]) => {
368
+ validatedProperties[key] = true
369
+ errors.push(...this._validateSchema(prop, value[key], `${path}.${key}`))
370
+ })
371
+ return errors
372
+ },
373
+ patternProperties (schema, value, path, validatedProperties) {
374
+ const errors = []
375
+ Object.entries(schema.patternProperties).forEach(([i, prop]) => {
376
+ const regex = new RegExp(i)
377
+ /* Check which properties match */
378
+ Object.entries(value).forEach(([j, v]) => {
379
+ if (regex.test(j)) {
380
+ validatedProperties[j] = true
381
+ errors.push(...this._validateSchema(prop, v, `${path}.${j}`))
382
+ }
383
+ })
384
+ })
385
+ return errors
386
+ }
387
+ }
388
+
389
+ this._validateObjectSubSchema2 = {
390
+ propertyNames (schema, value, path, validatedProperties) {
391
+ const errors = []
392
+ const keys = Object.keys(value)
393
+ let k = null
394
+ for (let i = 0; i < keys.length; i++) {
395
+ let msg = ''
396
+ let truthy = false
397
+ k = keys[i]
398
+ /* Check property names that don't match */
399
+ if (typeof schema.propertyNames === 'boolean') {
400
+ if (schema.propertyNames === true) {
401
+ continue
402
+ }
403
+ errors.push({
404
+ path,
405
+ property: 'propertyNames',
406
+ message: this.translate('error_property_names_false', [k])
407
+ })
408
+ break
409
+ }
410
+ truthy = Object.entries(schema.propertyNames).every(([j, prop]) => {
411
+ let match = false
412
+ let regex = null
413
+ switch (j) {
414
+ case 'maxLength':
415
+ if (typeof prop !== 'number') {
416
+ msg = 'error_property_names_maxlength'
417
+ break
418
+ }
419
+ if (k.length > prop) {
420
+ msg = 'error_property_names_exceeds_maxlength'
421
+ break
422
+ }
423
+ return true
424
+ case 'const':
425
+ if (prop !== k) {
426
+ msg = 'error_property_names_const_mismatch'
427
+ break
428
+ }
429
+ return true
430
+ case 'enum':
431
+ if (!Array.isArray(prop)) {
432
+ msg = 'error_property_names_enum'
433
+ break
434
+ }
435
+ prop.forEach(p => {
436
+ if (p === k) {
437
+ match = true
438
+ }
439
+ })
440
+ if (!match) {
441
+ msg = 'error_property_names_enum_mismatch'
442
+ break
443
+ }
444
+ return true
445
+ case 'pattern':
446
+ if (typeof prop !== 'string') {
447
+ msg = 'error_property_names_pattern'
448
+ break
449
+ }
450
+ regex = new RegExp(prop)
451
+ if (!regex.test(k)) {
452
+ msg = 'error_property_names_pattern_mismatch'
453
+ break
454
+ }
455
+ return true
456
+ default:
457
+ errors.push({
458
+ path,
459
+ property: 'propertyNames',
460
+ message: this.translate('error_property_names_unsupported', [j])
461
+ })
462
+ return false
463
+ }
464
+ errors.push({
465
+ path,
466
+ property: 'propertyNames',
467
+ message: this.translate(msg, [k])
468
+ })
469
+ return false
470
+ })
471
+ if (!truthy) break
472
+ }
473
+ return errors
474
+ },
475
+ additionalProperties (schema, value, path, validatedProperties) {
476
+ const errors = []
477
+ const keys = Object.keys(value)
478
+ for (let i = 0; i < keys.length; i++) {
479
+ const k = keys[i]
480
+ if (validatedProperties[k]) continue
481
+ /* No extra properties allowed */
482
+ if (!schema.additionalProperties) {
483
+ errors.push({
484
+ path,
485
+ property: 'additionalProperties',
486
+ message: this.translate('error_additional_properties', [k])
487
+ })
488
+ break
489
+ /* Allowed */
490
+ } else if (schema.additionalProperties === true) {
491
+ break
492
+ /* Must match schema */
493
+ /* TODO: incompatibility between version 3 and 4 of the spec */
494
+ } else {
495
+ errors.push(...this._validateSchema(schema.additionalProperties, value[k], `${path}.${k}`))
496
+ }
497
+ }
498
+ return errors
499
+ },
500
+ dependencies (schema, value, path) {
501
+ const errors = []
502
+ Object.entries(schema.dependencies).forEach(([i, dep]) => {
503
+ /* Doesn't need to meet the dependency */
504
+ if (typeof value[i] === 'undefined') return
505
+
506
+ /* Property dependency */
507
+ if (Array.isArray(dep)) {
508
+ dep.forEach(d => {
509
+ if (typeof value[d] === 'undefined') {
510
+ errors.push({
511
+ path,
512
+ property: 'dependencies',
513
+ message: this.translate('error_dependency', [d])
514
+ })
515
+ }
516
+ })
517
+ /* Schema dependency */
518
+ } else {
519
+ errors.push(...this._validateSchema(dep, value, path))
520
+ }
521
+ })
522
+ return errors
523
+ }
524
+ }
525
+ }
526
+
527
+ fitTest (value, givenSchema, weight = 10000000) {
528
+ const fit = { match: 0, extra: 0 }
529
+ if (typeof value === 'object' && value !== null) {
530
+ /* Work on a copy of the schema */
531
+ const properties = this._getSchema(givenSchema).properties
532
+
533
+ for (const i in properties) {
534
+ if (!hasOwnProperty(properties, i)) {
535
+ fit.extra += weight
536
+ continue
537
+ }
538
+ if (typeof value[i] === 'object' && typeof properties[i] === 'object' && typeof properties[i].properties === 'object') {
539
+ const result = this.fitTest(value[i], properties[i], weight / 100)
540
+ fit.match += result.match
541
+ fit.extra += result.extra
542
+ }
543
+ if (typeof value[i] !== 'undefined') {
544
+ fit.match += weight
545
+ }
546
+ }
547
+ }
548
+ return fit
549
+ }
550
+
551
+ _getSchema (schema) {
552
+ return typeof schema === 'undefined' ? extend({}, this.jsoneditor.expandRefs(this.schema)) : schema
553
+ }
554
+
555
+ validate (value) {
556
+ return this._validateSchema(this.schema, value)
557
+ }
558
+
559
+ _validateSchema (schema, value, path) {
560
+ const errors = []
561
+ path = path || this.jsoneditor.root.formname
562
+
563
+ Object.assign(schema, this.jsoneditor.expandRefs(schema))
564
+ delete schema.$ref
565
+
566
+ /*
567
+ * Type Agnostic Validation
568
+ */
569
+ /* Version 3 `required` and `required_by_default` */
570
+ if (typeof value === 'undefined') {
571
+ return this._validateV3Required(schema, value, path)
572
+ }
573
+
574
+ try {
575
+ /* custom validator */
576
+ errors.push(...this._validateCustomValidator(schema, value, path))
577
+ } catch (err) {
578
+ errors.push({
579
+ path: path,
580
+ property: 'custom validator',
581
+ message: err.message
582
+ })
583
+ return errors
584
+ }
585
+
586
+ Object.keys(schema).forEach(key => {
587
+ if (this._validateSubSchema[key]) {
588
+ errors.push(...this._validateSubSchema[key].call(this, schema, value, path))
589
+ }
590
+ })
591
+
592
+ /*
593
+ * Type Specific Validation
594
+ */
595
+ errors.push(...this._validateByValueType(schema, value, path))
596
+
597
+ if (schema.links) {
598
+ schema.links.forEach((s, m) => {
599
+ if (s.rel && s.rel.toLowerCase() === 'describedby') {
600
+ schema = this._expandSchemaLink(schema, m)
601
+ errors.push(...this._validateSchema(schema, value, path, this.translate))
602
+ }
603
+ })
604
+ }
605
+
606
+ /* date, time and datetime-local validation */
607
+ if (['date', 'time', 'datetime-local'].includes(schema.format)) {
608
+ errors.push(...this._validateDateTimeSubSchema(schema, value, path))
609
+ }
610
+
611
+ /* uuid validation */
612
+ if (['uuid'].includes(schema.format)) {
613
+ errors.push(...this._validateUUIDSchema(schema, value, path))
614
+ }
615
+
616
+ /* Remove duplicate errors and add "errorcount" property */
617
+ return this._removeDuplicateErrors(errors)
618
+ }
619
+
620
+ _expandSchemaLink (schema, m) {
621
+ const href = schema.links[m].href
622
+ const data = this.jsoneditor.root.getValue()
623
+ const template = this.jsoneditor.compileTemplate(href, this.jsoneditor.template)
624
+ const ref = document.location.origin + document.location.pathname + template(data)
625
+
626
+ schema.links = schema.links.slice(0, m).concat(schema.links.slice(m + 1))
627
+ return extend({}, schema, this.jsoneditor.refs[ref])
628
+ }
629
+
630
+ _validateV3Required (schema, value, path) {
631
+ if (((typeof schema.required !== 'undefined' && schema.required === true) || (typeof schema.required === 'undefined' && this.jsoneditor.options.required_by_default === true)) && (schema.type !== 'info')) {
632
+ return [{
633
+ path,
634
+ property: 'required',
635
+ message: this.translate('error_notset')
636
+ }]
637
+ }
638
+ return []
639
+ }
640
+
641
+ _validateByValueType (schema, value, path) {
642
+ const errors = []
643
+ if (value === null) return errors
644
+ /* Number Specific Validation */
645
+ if (typeof value === 'number') {
646
+ /* `multipleOf` and `divisibleBy` */
647
+ /* `maximum` */
648
+ /* `minimum` */
649
+ Object.keys(schema).forEach(key => {
650
+ if (this._validateNumberSubSchema[key]) {
651
+ errors.push(...this._validateNumberSubSchema[key].call(this, schema, value, path))
652
+ }
653
+ })
654
+ /* String specific validation */
655
+ } else if (typeof value === 'string') {
656
+ /* `maxLength` */
657
+ /* `minLength` */
658
+ /* `pattern` */
659
+ Object.keys(schema).forEach(key => {
660
+ if (this._validateStringSubSchema[key]) {
661
+ errors.push(...this._validateStringSubSchema[key].call(this, schema, value, path))
662
+ }
663
+ })
664
+ /* Array specific validation */
665
+ } else if (Array.isArray(value)) {
666
+ /* `items` and `additionalItems`
667
+ /* `maxItems`
668
+ /* `minItems`
669
+ /* `uniqueItems` */
670
+ Object.keys(schema).forEach(key => {
671
+ if (this._validateArraySubSchema[key]) {
672
+ errors.push(...this._validateArraySubSchema[key].call(this, schema, value, path))
673
+ }
674
+ })
675
+ /* Object specific validation */
676
+ } else if (typeof value === 'object') {
677
+ const validatedProperties = {}
678
+ /* `maxProperties`
679
+ /* `minProperties`
680
+ /* Version 4 `required`
681
+ /* `properties`
682
+ /* `patternProperties` */
683
+ Object.keys(schema).forEach(key => {
684
+ if (this._validateObjectSubSchema[key]) {
685
+ errors.push(...this._validateObjectSubSchema[key].call(this, schema, value, path, validatedProperties))
686
+ }
687
+ })
688
+
689
+ /* The no_additional_properties option currently doesn't work with extended schemas that use oneOf or anyOf or allOf */
690
+ if (typeof schema.additionalProperties === 'undefined' && this.jsoneditor.options.no_additional_properties && !schema.oneOf && !schema.anyOf && !schema.allOf) {
691
+ schema.additionalProperties = false
692
+ }
693
+
694
+ /* `additionalProperties` */
695
+ /* `dependencies` */
696
+ Object.keys(schema).forEach(key => {
697
+ if (typeof this._validateObjectSubSchema2[key] !== 'undefined') {
698
+ errors.push(...this._validateObjectSubSchema2[key].call(this, schema, value, path, validatedProperties))
699
+ }
700
+ })
701
+ }
702
+ return errors
703
+ }
704
+
705
+ _validateUUIDSchema (schema, value, path) {
706
+ if (!(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value))) {
707
+ return [{
708
+ path,
709
+ property: 'format',
710
+ message: this.translate('error_pattern', ['^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$'])
711
+ }]
712
+ }
713
+ return []
714
+ }
715
+
716
+ _validateNumberSubSchemaMultipleDivisible (schema, value, path) {
717
+ const divisor = schema.multipleOf || schema.divisibleBy
718
+ /* Vanilla JS, prone to floating point rounding errors (e.g. 1.14 / .01 == 113.99999) */
719
+ let valid = (value / divisor === Math.floor(value / divisor))
720
+
721
+ /* Use math.js is available */
722
+ if (window.math) {
723
+ valid = window.math.mod(window.math.bignumber(value), window.math.bignumber(divisor)).equals(0)
724
+ } else if (window.Decimal) {
725
+ /* Use decimal.js is available */
726
+ valid = (new window.Decimal(value)).mod(new window.Decimal(divisor)).equals(0)
727
+ }
728
+
729
+ if (!valid) {
730
+ return [{
731
+ path,
732
+ property: schema.multipleOf ? 'multipleOf' : 'divisibleBy',
733
+ message: this.translate('error_multipleOf', [divisor])
734
+ }]
735
+ }
736
+ return []
737
+ }
738
+
739
+ _validateDateTimeSubSchema (schema, value, path) {
740
+ const _validateInteger = (schema, value, path) => {
741
+ /* The value is a timestamp */
742
+ if (value * 1 < 1) {
743
+ /* If value is less than 1, then it's an invalid epoch date before 00:00:00 UTC Thursday, 1 January 1970 */
744
+ return [{
745
+ path,
746
+ property: 'format',
747
+ message: this.translate('error_invalid_epoch')
748
+ }]
749
+ } else if (value !== Math.abs(parseInt(value))) {
750
+ /* not much to check for, so we assume value is ok if it's a positive number */
751
+ return [{
752
+ path,
753
+ property: 'format',
754
+ message: this.translate(`error_${schema.format.replace(/-/g, '_')}`, [dateFormat])
755
+ }]
756
+ }
757
+ return []
758
+ }
759
+ const _validateFlatPicker = (schema, value, path, editor) => {
760
+ if (value !== '') {
761
+ let compareValue
762
+ if (editor.flatpickr.config.mode !== 'single') {
763
+ const seperator = editor.flatpickr.config.mode === 'range' ? editor.flatpickr.l10n.rangeSeparator : ', '
764
+ const selectedDates = editor.flatpickr.selectedDates.map(val =>
765
+ editor.flatpickr.formatDate(val, editor.flatpickr.config.dateFormat)
766
+ )
767
+ compareValue = selectedDates.join(seperator)
768
+ }
769
+
770
+ try {
771
+ if (compareValue) {
772
+ /* Not the best validation method, but range and multiple mode are special */
773
+ /* Optimal solution would be if it is possible to change the return format from string/integer to array */
774
+ if (compareValue !== value) throw new Error(`${editor.flatpickr.config.mode} mismatch`)
775
+ } else if (editor.flatpickr.formatDate(editor.flatpickr.parseDate(value, editor.flatpickr.config.dateFormat), editor.flatpickr.config.dateFormat) !== value) {
776
+ throw new Error('mismatch')
777
+ }
778
+ } catch (err) {
779
+ const errorDateFormat = editor.flatpickr.config.errorDateFormat !== undefined ? editor.flatpickr.config.errorDateFormat : editor.flatpickr.config.dateFormat
780
+ return [{
781
+ path,
782
+ property: 'format',
783
+ message: this.translate(`error_${editor.format.replace(/-/g, '_')}`, [errorDateFormat])
784
+ }]
785
+ }
786
+ }
787
+ return []
788
+ }
789
+
790
+ const validatorRx = {
791
+ date: /^(\d{4}\D\d{2}\D\d{2})?$/,
792
+ time: /^(\d{2}:\d{2}(?::\d{2})?)?$/,
793
+ 'datetime-local': /^(\d{4}\D\d{2}\D\d{2}[ T]\d{2}:\d{2}(?::\d{2})?)?$/
794
+ }
795
+ const format = {
796
+ date: '"YYYY-MM-DD"',
797
+ time: '"HH:MM"',
798
+ 'datetime-local': '"YYYY-MM-DD HH:MM"'
799
+ }
800
+
801
+ const editor = this.jsoneditor.getEditor(path)
802
+ const dateFormat = (editor && editor.flatpickr) ? editor.flatpickr.config.dateFormat : format[schema.format]
803
+
804
+ if (schema.type === 'integer') {
805
+ return _validateInteger(schema, value, path)
806
+ } else if (!editor || !editor.flatpickr) {
807
+ /* Standard string input, without flatpickr */
808
+ if (!validatorRx[schema.format].test(value)) {
809
+ return [{
810
+ path,
811
+ property: 'format',
812
+ message: this.translate(`error_${schema.format.replace(/-/g, '_')}`, [dateFormat])
813
+ }]
814
+ }
815
+ } else if (editor) {
816
+ /* Flatpickr validation */
817
+ return _validateFlatPicker(schema, value, path, editor)
818
+ }
819
+ return []
820
+ }
821
+
822
+ _validateCustomValidator (schema, value, path) {
823
+ const errors = []
824
+ const validate = validator => {
825
+ errors.push(...validator.call(this, schema, value, path))
826
+ }
827
+ /* Custom type validation (global) */
828
+ this.defaults.custom_validators.forEach(validate)
829
+ /* Custom type validation (instance specific) */
830
+ if (this.options.custom_validators) {
831
+ this.options.custom_validators.forEach(validate)
832
+ }
833
+ /* Internal validators using the custom validator format */
834
+ errors.push(...ipValidator.call(this, schema, value, path, this.translate))
835
+
836
+ return errors
837
+ }
838
+
839
+ _removeDuplicateErrors (errors) {
840
+ return errors.reduce((err, obj) => {
841
+ let first = true
842
+ if (!err) err = []
843
+ err.forEach(a => {
844
+ if (a.message === obj.message && a.path === obj.path && a.property === obj.property) {
845
+ a.errorcount++
846
+ first = false
847
+ }
848
+ })
849
+ if (first) {
850
+ obj.errorcount = 1
851
+ err.push(obj)
852
+ }
853
+ return err
854
+ }, [])
855
+ }
856
+
857
+ _checkType (type, value) {
858
+ const types = {
859
+ string: value => typeof value === 'string',
860
+ number: value => typeof value === 'number',
861
+ integer: value => typeof value === 'number' && value === Math.floor(value),
862
+ boolean: value => typeof value === 'boolean',
863
+ array: value => Array.isArray(value),
864
+ object: value => value !== null && !(Array.isArray(value)) && typeof value === 'object',
865
+ null: value => value === null
866
+ }
867
+ /* Simple types */
868
+ if (typeof type === 'string') {
869
+ if (types[type]) {
870
+ return types[type](value)
871
+ } else return true
872
+ /* Schema */
873
+ } else {
874
+ return !this._validateSchema(type, value).length
875
+ }
876
+ }
877
+ }