alchemymvc 1.3.21 → 1.4.0-alpha.1

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 (155) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -3
  3. package/lib/app/behaviour/publishable_behaviour.js +5 -5
  4. package/lib/app/behaviour/revision_behaviour.js +10 -10
  5. package/lib/app/behaviour/sluggable_behaviour.js +14 -14
  6. package/lib/app/conduit/electron_conduit.js +9 -9
  7. package/lib/app/conduit/http_conduit.js +13 -13
  8. package/lib/app/conduit/loopback_conduit.js +15 -15
  9. package/lib/app/conduit/socket_conduit.js +43 -43
  10. package/lib/app/config/routes.js +26 -0
  11. package/lib/app/controller/00-default_app_controller.js +21 -0
  12. package/lib/app/controller/alchemy_info_controller.js +12 -12
  13. package/lib/app/datasource/mongo_datasource.js +16 -16
  14. package/lib/app/element/00-default_app_element.js +19 -0
  15. package/lib/app/element/time_ago.js +5 -5
  16. package/lib/app/helper/00-default_app_helper.js +11 -0
  17. package/lib/app/helper/alchemy_helper.js +22 -22
  18. package/lib/app/helper/backed_map.js +1 -1
  19. package/lib/app/helper/breadcrumb.js +10 -10
  20. package/lib/app/helper/client_collection.js +3 -3
  21. package/lib/app/helper/cron.js +29 -29
  22. package/lib/app/helper/enum_values.js +6 -6
  23. package/lib/app/helper/pagination_helper.js +36 -36
  24. package/lib/app/helper/router_helper.js +35 -35
  25. package/lib/app/helper/socket_helper.js +57 -57
  26. package/lib/app/helper/syncable.js +84 -59
  27. package/lib/app/helper_component/paginate_component.js +9 -9
  28. package/lib/app/helper_controller/component.js +1 -1
  29. package/lib/app/helper_controller/conduit.js +31 -31
  30. package/lib/app/helper_controller/controller.js +54 -39
  31. package/lib/app/helper_datasource/00-nosql_datasource.js +624 -70
  32. package/lib/app/helper_datasource/05-fallback_datasource.js +10 -10
  33. package/lib/app/helper_datasource/idb_datasource.js +6 -6
  34. package/lib/app/helper_datasource/indexed_db.js +22 -22
  35. package/lib/app/helper_datasource/remote_datasource.js +5 -5
  36. package/lib/app/helper_error/http_error.js +4 -4
  37. package/lib/app/helper_error/model_error.js +2 -2
  38. package/lib/app/helper_error/validation_error.js +12 -12
  39. package/lib/app/helper_field/00-objectid_field.js +7 -7
  40. package/lib/app/helper_field/05-string_field.js +16 -12
  41. package/lib/app/helper_field/06-text_field.js +2 -4
  42. package/lib/app/helper_field/10-number_field.js +9 -12
  43. package/lib/app/helper_field/11-date_field.js +15 -15
  44. package/lib/app/helper_field/15-local_temporal_field.js +10 -10
  45. package/lib/app/helper_field/20-decimal_field.js +8 -9
  46. package/lib/app/helper_field/belongsto_field.js +1 -1
  47. package/lib/app/helper_field/big_int_field.js +8 -8
  48. package/lib/app/helper_field/boolean_field.js +9 -11
  49. package/lib/app/helper_field/datetime_field.js +3 -3
  50. package/lib/app/helper_field/enum_field.js +13 -8
  51. package/lib/app/helper_field/fixed_decimal_field.js +6 -7
  52. package/lib/app/helper_field/geopoint_field.js +9 -10
  53. package/lib/app/helper_field/habtm_field.js +3 -3
  54. package/lib/app/helper_field/hasoneparent_field.js +1 -1
  55. package/lib/app/helper_field/html_field.js +2 -4
  56. package/lib/app/helper_field/integer_field.js +8 -11
  57. package/lib/app/helper_field/local_date_field.js +5 -5
  58. package/lib/app/helper_field/local_date_time_field.js +5 -5
  59. package/lib/app/helper_field/local_time_field.js +5 -5
  60. package/lib/app/helper_field/mixed_field.js +5 -5
  61. package/lib/app/helper_field/object_field.js +8 -8
  62. package/lib/app/helper_field/password_field.js +3 -3
  63. package/lib/app/helper_field/regexp_field.js +7 -9
  64. package/lib/app/helper_field/schema_field.js +91 -88
  65. package/lib/app/helper_field/settings_field.js +92 -0
  66. package/lib/app/helper_field/time_field.js +6 -6
  67. package/lib/app/helper_field/url_field.js +2 -4
  68. package/lib/app/helper_model/00-base_criteria.js +662 -0
  69. package/lib/app/helper_model/05-criteria_expressions.js +605 -0
  70. package/lib/app/helper_model/10-model_criteria.js +1182 -0
  71. package/lib/app/helper_model/data_provider.js +2 -2
  72. package/lib/app/helper_model/document.js +103 -92
  73. package/lib/app/helper_model/document_list.js +14 -14
  74. package/lib/app/helper_model/field_config.js +11 -11
  75. package/lib/app/helper_model/field_set.js +17 -17
  76. package/lib/app/helper_model/model.js +203 -124
  77. package/lib/app/helper_model/remote_data_provider.js +2 -2
  78. package/lib/app/helper_validator/00_validator.js +16 -16
  79. package/lib/app/helper_validator/not_empty_validator.js +9 -9
  80. package/lib/app/model/00-default_app_model.js +18 -0
  81. package/lib/app/model/05-system_model.js +27 -0
  82. package/lib/app/model/{alchemy_migration_model.js → system_migration_model.js} +4 -4
  83. package/lib/app/model/system_setting_model.js +154 -0
  84. package/lib/app/model/{alchemy_task_history_model.js → system_task_history_model.js} +7 -7
  85. package/lib/app/model/{alchemy_task_model.js → system_task_model.js} +11 -11
  86. package/lib/bootstrap.js +22 -312
  87. package/lib/class/accumulator.js +5 -5
  88. package/lib/class/behaviour.js +5 -5
  89. package/lib/class/component.js +3 -3
  90. package/lib/class/conduit.js +203 -163
  91. package/lib/class/controller.js +42 -42
  92. package/lib/class/datasource.js +74 -79
  93. package/lib/class/document.js +74 -95
  94. package/lib/class/document_list.js +5 -5
  95. package/lib/class/element.js +17 -17
  96. package/lib/class/error.js +3 -3
  97. package/lib/class/field.js +169 -91
  98. package/lib/class/field_value.js +6 -6
  99. package/lib/class/helper.js +3 -3
  100. package/lib/class/inode.js +17 -17
  101. package/lib/class/inode_dir.js +12 -12
  102. package/lib/class/inode_file.js +50 -25
  103. package/lib/class/inode_list.js +4 -4
  104. package/lib/class/migration.js +4 -4
  105. package/lib/class/model.js +182 -168
  106. package/lib/class/path_definition.js +22 -22
  107. package/lib/class/path_evaluator.js +5 -5
  108. package/lib/class/path_param_definition.js +7 -7
  109. package/lib/class/plugin.js +312 -0
  110. package/lib/class/postponement.js +29 -29
  111. package/lib/class/reciprocal.js +8 -8
  112. package/lib/class/route.js +33 -33
  113. package/lib/class/router.js +73 -73
  114. package/lib/class/schema.js +21 -21
  115. package/lib/class/schema_client.js +73 -67
  116. package/lib/class/session.js +63 -29
  117. package/lib/class/session_scene.js +4 -4
  118. package/lib/class/sitemap.js +16 -16
  119. package/lib/class/task.js +39 -39
  120. package/lib/class/task_service.js +43 -47
  121. package/lib/{init → core}/alchemy.js +413 -374
  122. package/lib/{init/functions.js → core/alchemy_functions.js} +171 -108
  123. package/lib/core/alchemy_load_functions.js +715 -0
  124. package/lib/core/base.js +50 -62
  125. package/lib/core/client_alchemy.js +144 -152
  126. package/lib/core/client_base.js +39 -52
  127. package/lib/core/discovery.js +16 -18
  128. package/lib/core/middleware.js +54 -43
  129. package/lib/core/{routing.js → prefix.js} +14 -16
  130. package/lib/core/setting.js +1684 -0
  131. package/lib/core/stage.js +758 -0
  132. package/lib/scripts/create_constants.js +119 -0
  133. package/lib/{init/languages.js → scripts/create_languages.js} +5 -5
  134. package/lib/scripts/create_settings.js +449 -0
  135. package/lib/scripts/create_shared_constants.js +95 -0
  136. package/lib/scripts/create_stages.js +55 -0
  137. package/lib/scripts/init_alchemy.js +51 -0
  138. package/lib/{init/requirements.js → scripts/preload_modules.js} +15 -2
  139. package/lib/scripts/setup_devwatch.js +238 -0
  140. package/lib/stages/00-load_core.js +342 -0
  141. package/lib/stages/05-load_app.js +57 -0
  142. package/lib/stages/10-datasource.js +61 -0
  143. package/lib/stages/15-tasks.js +27 -0
  144. package/lib/stages/20-settings.js +68 -0
  145. package/lib/stages/50-routes.js +218 -0
  146. package/lib/stages/90-server.js +347 -0
  147. package/package.json +5 -7
  148. package/lib/app/helper_model/criteria.js +0 -2294
  149. package/lib/app/helper_model/db_query.js +0 -1488
  150. package/lib/app/routes.js +0 -11
  151. package/lib/core/socket.js +0 -171
  152. package/lib/init/constants.js +0 -158
  153. package/lib/init/devwatch.js +0 -238
  154. package/lib/init/load_functions.js +0 -973
  155. package/lib/stages.js +0 -513
@@ -0,0 +1,1684 @@
1
+ const SettingNs = Function.getNamespace('Alchemy.Setting');
2
+ const VALUE = Symbol('value');
3
+
4
+ /**
5
+ * The Base Setting Definition class.
6
+ * These settings are stored in some kind of database.
7
+ * They differ from `alchemy.settings`: those are hard-coded.
8
+ *
9
+ * @author Jelle De Loecker <jelle@elevenways.be>
10
+ * @since 1.4.0
11
+ * @version 1.4.0
12
+ *
13
+ * @param {string} name The name of the setting in its group
14
+ * @param {Object} config The settings of this definition
15
+ * @param {Group} group The parent group
16
+ */
17
+ const Base = Function.inherits('Alchemy.Base', 'Alchemy.Setting', function Base(name, config, group) {
18
+
19
+ // The parent group
20
+ this.group = group;
21
+
22
+ // The path of the setting in its group
23
+ this.name = name;
24
+
25
+ // The complete setting id
26
+ this.setting_id = (group?.setting_id ? group.setting_id + '.' : '') + name;
27
+
28
+ // The description
29
+ this.description = config?.description;
30
+
31
+ // Does this setting require any permission to view?
32
+ this.view_permission = config?.view_permission;
33
+
34
+ // Does this setting require any permission to edit?
35
+ this.edit_persmission = config?.edit_persmission;
36
+ });
37
+
38
+ /**
39
+ * Is this a group?
40
+ *
41
+ * @author Jelle De Loecker <jelle@elevenways.be>
42
+ * @since 1.4.0
43
+ * @version 1.4.0
44
+ *
45
+ * @type {boolean}
46
+ */
47
+ Base.setProperty('is_group', false);
48
+
49
+ /**
50
+ * The Base Setting Definition class
51
+ *
52
+ * @author Jelle De Loecker <jelle@elevenways.be>
53
+ * @since 1.4.0
54
+ * @version 1.4.0
55
+ *
56
+ * @param {string} name The name of the setting in its group
57
+ * @param {Object} config The settings of this definition
58
+ * @param {Group} group The parent group
59
+ */
60
+ const Definition = Function.inherits('Alchemy.Setting.Base', function Definition(name, config, group) {
61
+
62
+ Definition.super.call(this, name, config, group);
63
+
64
+ // The type of the definition (string, number, etc)
65
+ this.type = config.type;
66
+
67
+ // Allowed values (makes it an enum)
68
+ this.allowed_values = config.values || config.allowed_values;
69
+
70
+ // Possible validation pattern
71
+ this.validation_pattern = config.validation_pattern;
72
+
73
+ // The default value
74
+ this.default_value = config.default;
75
+
76
+ // Is the default value an object?
77
+ this.default_value_needs_cloning = this.default_value && typeof this.default_value == 'object';
78
+
79
+ // Show a description?
80
+ this.show_description = config.show_description;
81
+
82
+ // Is this setting locked?
83
+ // (Meaning: can not be edited in the frontend)
84
+ this.locked = config.locked;
85
+
86
+ // Does this setting require a reboot?
87
+ this.requires_restart = config.requires_restart;
88
+
89
+ // The intended target of this setting.
90
+ // This is mostly used to differentiate between 'user' or 'visitor' settings
91
+ this.target = config.target;
92
+
93
+ // The action to execute
94
+ this.action = config.action;
95
+
96
+ // Other settings it might require (needs to be truthy)
97
+ this.requires = config.requires ? Array.cast(config.requires) : undefined;
98
+
99
+ // Other settings it might depend on
100
+ this.depends_on = config.depends_on ? Array.cast(config.depends_on) : undefined;
101
+ });
102
+
103
+ /**
104
+ * unDry an object
105
+ *
106
+ * @author Jelle De Loecker <jelle@elevenways.be>
107
+ * @since 1.4.0
108
+ * @version 1.4.0
109
+ *
110
+ * @param {Object} obj
111
+ * @param {boolean|string} cloned
112
+ *
113
+ * @return {Definition}
114
+ */
115
+ Definition.setStatic(function unDry(obj, cloned) {
116
+
117
+ let config = obj.config;
118
+
119
+ let result = new Definition(config.name, config, null);
120
+ result.setting_id = config.setting_id;
121
+
122
+ return result;
123
+ });
124
+
125
+ /**
126
+ * Get all the dependency ids
127
+ *
128
+ * @author Jelle De Loecker <jelle@elevenways.be>
129
+ * @since 1.4.0
130
+ * @version 1.4.0
131
+ *
132
+ * @return {string[]}
133
+ */
134
+ Definition.setProperty(function all_dependencies() {
135
+
136
+ let result = [];
137
+
138
+ if (this.depends_on) {
139
+ if (Array.isArray(this.depends_on)) {
140
+ result.push(...this.depends_on);
141
+ } else {
142
+ result.push(this.depends_on);
143
+ }
144
+ }
145
+
146
+ if (this.requires) {
147
+ let requires = Array.cast(this.requires);
148
+
149
+ for (let entry of requires) {
150
+ if (result.indexOf(entry) == -1) {
151
+ result.push(entry);
152
+ }
153
+ }
154
+ }
155
+
156
+ return result;
157
+ });
158
+
159
+ /**
160
+ * Get the schema of this setting
161
+ *
162
+ * @author Jelle De Loecker <jelle@elevenways.be>
163
+ * @since 1.4.0
164
+ * @version 1.4.0
165
+ */
166
+ Definition.enforceProperty(function schema(new_value, old_value) {
167
+
168
+ if (new_value == null) {
169
+ new_value = alchemy.createSchema();
170
+
171
+ let type = this.type;
172
+
173
+ if (type == 'primitive') {
174
+ type = 'string';
175
+ }
176
+
177
+ if (type == 'percentage') {
178
+ type = 'integer';
179
+ }
180
+
181
+ if (type == 'path') {
182
+ type = 'string';
183
+ }
184
+
185
+ if (type == 'duration') {
186
+ type = 'string';
187
+ }
188
+
189
+ if (type == 'function') {
190
+ type = 'string';
191
+ }
192
+
193
+ if (!type) {
194
+ type = 'string';
195
+ }
196
+
197
+ new_value.addField('value', type, {
198
+ description: this.description,
199
+ allowed_values: this.allowed_values,
200
+ validation_pattern: this.validation_pattern,
201
+ default: this.default_value,
202
+ });
203
+ }
204
+
205
+ return new_value;
206
+ });
207
+
208
+ /**
209
+ * Cast the given value
210
+ *
211
+ * @author Jelle De Loecker <jelle@elevenways.be>
212
+ * @since 1.4.0
213
+ * @version 1.4.0
214
+ *
215
+ * @return {Mixed}
216
+ */
217
+ Definition.setMethod(function cast(value) {
218
+
219
+ if (value == null) {
220
+ return value;
221
+ }
222
+
223
+ if (this.type == 'boolean') {
224
+ if (value === 'false') {
225
+ value = false;
226
+ } else if (value === 'true') {
227
+ value = true;
228
+ } else {
229
+ value = !!value;
230
+ }
231
+ } else if (this.type == 'primitive') {
232
+ let nr = Number(value);
233
+
234
+ if (!isNaN(nr)) {
235
+ value = nr;
236
+ }
237
+ } else if (this.type == 'percentage' || this.type == 'integer') {
238
+ value = parseInt(value);
239
+ } else if (this.type == 'number') {
240
+ value = Number(value);
241
+ }
242
+
243
+ return value;
244
+ });
245
+
246
+ /**
247
+ * Create the settings with default values
248
+ *
249
+ * @author Jelle De Loecker <jelle@elevenways.be>
250
+ * @since 1.4.0
251
+ * @version 1.4.0
252
+ *
253
+ * @return {Alchemy.Setting.Value}
254
+ */
255
+ Definition.setMethod(function generateValue() {
256
+
257
+ let result = new SettingValue(this);
258
+
259
+ result.setDefaultValue(this.default_value);
260
+
261
+ return result;
262
+ });
263
+
264
+ /**
265
+ * Return the json-dry representation
266
+ *
267
+ * @author Jelle De Loecker <jelle@elevenways.be>
268
+ * @since 1.4.0
269
+ * @version 1.4.0
270
+ */
271
+ Definition.setMethod(function toDry() {
272
+
273
+ let config = this.toJSON();
274
+
275
+ return {
276
+ value: {
277
+ config,
278
+
279
+ // Always convert the schema to the client-side version.
280
+ // This saves us a lot of serialization headaches.
281
+ schema : JSON.clone(this.schema, 'toHawkejs'),
282
+ }
283
+ };
284
+ });
285
+
286
+ /**
287
+ * Return the json representation
288
+ *
289
+ * @author Jelle De Loecker <jelle@elevenways.be>
290
+ * @since 1.4.0
291
+ * @version 1.4.0
292
+ */
293
+ Definition.setMethod(function toJSON() {
294
+
295
+ let result = {
296
+ name : this.name,
297
+ type : this.type,
298
+ setting_id : this.setting_id,
299
+ allowed_values : this.allowed_values,
300
+ validation_pattern : this.validation_pattern,
301
+ default_value : this.default_value,
302
+ show_description : this.show_description,
303
+ target : this.target,
304
+ view_permission : this.view_permission,
305
+ edit_persmission : this.edit_persmission,
306
+ requires_restart : this.requires_restart,
307
+ locked : this.locked,
308
+ };
309
+
310
+ return result;
311
+ });
312
+
313
+ /**
314
+ * Create an enum entry
315
+ *
316
+ * @author Jelle De Loecker <jelle@elevenways.be>
317
+ * @since 1.4.0
318
+ * @version 1.4.0
319
+ */
320
+ Definition.setMethod(function toEnumEntry() {
321
+ let result = this.toJSON();
322
+ result.schema = JSON.clone(this.schema, 'toHawkejs');
323
+ return result;
324
+ });
325
+
326
+ /**
327
+ * Get the configuration for the editor
328
+ * (Including the current value)
329
+ *
330
+ * @author Jelle De Loecker <jelle@elevenways.be>
331
+ * @since 1.4.0
332
+ * @version 1.4.0
333
+ *
334
+ * @param {Value} root_value
335
+ * @param {Conduit} editor_context
336
+ *
337
+ * @return {Object}
338
+ */
339
+ Definition.setMethod(function getEditorConfiguration(root_value, editor_context) {
340
+
341
+ let result = this.toEnumEntry();
342
+
343
+ let value = root_value.getPath(this.setting_id);
344
+
345
+ if (value) {
346
+ result.current_value = value.get();
347
+ result.current_value_is_default = value.has_default_value;
348
+ }
349
+
350
+ if (result.show_description !== false && this.description) {
351
+ result.description = this.description;
352
+ }
353
+
354
+ if (this.edit_persmission && editor_context && !editor_context.hasPermission(this.edit_persmission)) {
355
+ result.locked = true;
356
+ }
357
+
358
+ return result;
359
+ });
360
+
361
+ /**
362
+ * Create a configuration object (for storing in the database)
363
+ *
364
+ * @author Jelle De Loecker <jelle@elevenways.be>
365
+ * @since 1.4.0
366
+ * @version 1.4.0
367
+ *
368
+ * @param {Mixed} new_value
369
+ *
370
+ * @return {Object}
371
+ */
372
+ Definition.setMethod(function createConfigurationObject(new_value) {
373
+
374
+ // @TODO: make sure the value is valid
375
+ let result = {
376
+ value : new_value,
377
+ };
378
+
379
+ return result;
380
+ });
381
+
382
+ /**
383
+ * Can this setting by edited by the given context?
384
+ *
385
+ * @author Jelle De Loecker <jelle@elevenways.be>
386
+ * @since 1.4.0
387
+ * @version 1.4.0
388
+ *
389
+ * @param {Conduit} permission_context
390
+ *
391
+ * @return {boolean}
392
+ */
393
+ Definition.setMethod(function canBeEditedBy(permission_context) {
394
+
395
+ // Locked settings can never be edited
396
+ if (this.locked) {
397
+ return false;
398
+ }
399
+
400
+ // If no edit permission is required, it can be edited
401
+ if (!this.edit_persmission) {
402
+ return true;
403
+ }
404
+
405
+ // If no context is given, it can't be edited
406
+ if (!permission_context) {
407
+ return false;
408
+ }
409
+
410
+ return permission_context.hasPermission(this.edit_persmission);
411
+ });
412
+
413
+ /**
414
+ * Execute the action (if any is linked)
415
+ *
416
+ * @author Jelle De Loecker <jelle@elevenways.be>
417
+ * @since 1.4.0
418
+ * @version 1.4.0
419
+ *
420
+ * @param {Alchemy.Setting.Value} value_instance
421
+ */
422
+ Definition.setMethod(function executeAction(value_instance) {
423
+
424
+ if (!this.action) {
425
+ return;
426
+ }
427
+
428
+ return this.action.call(this, value_instance.get(), value_instance);
429
+ });
430
+
431
+ /**
432
+ * The Setting Group class
433
+ *
434
+ * @author Jelle De Loecker <jelle@elevenways.be>
435
+ * @since 1.4.0
436
+ * @version 1.4.0
437
+ *
438
+ * @param {string} name The name of the setting in its (parent) group
439
+ * @param {Object} config The settings of this definition
440
+ * @param {Group} group The parent group
441
+ */
442
+ const Group = Function.inherits('Alchemy.Setting.Base', function Group(name, config, group) {
443
+
444
+ if (!group) {
445
+ if (config && config instanceof Group) {
446
+ group = config;
447
+ config = null;
448
+ }
449
+ }
450
+
451
+ Group.super.call(this, name, config, group);
452
+
453
+ // All the children
454
+ this.children = new Map();
455
+ });
456
+
457
+ /**
458
+ * unDry the group
459
+ *
460
+ * @author Jelle De Loecker <jelle@elevenways.be>
461
+ * @since 1.4.0
462
+ * @version 1.4.0
463
+ *
464
+ * @param {Object} obj
465
+ * @param {boolean|string} cloned
466
+ *
467
+ * @return {Group}
468
+ */
469
+ Group.setStatic(function unDry(obj, cloned) {
470
+
471
+ let config = obj.config;
472
+
473
+ let result = new Group(config.name, null, null);
474
+ result.setting_id = config.setting_id;
475
+
476
+ let children = obj.children,
477
+ child;
478
+
479
+ for (child of children) {
480
+ result.children.set(child.name, child);
481
+ child.group = result;
482
+ }
483
+
484
+ return result;
485
+ });
486
+
487
+ /**
488
+ * Is this a group?
489
+ *
490
+ * @author Jelle De Loecker <jelle@elevenways.be>
491
+ * @since 1.4.0
492
+ * @version 1.4.0
493
+ *
494
+ * @type {boolean}
495
+ */
496
+ Group.setProperty('is_group', true);
497
+
498
+ /**
499
+ * Return the json-dry representation
500
+ *
501
+ * @author Jelle De Loecker <jelle@elevenways.be>
502
+ * @since 1.4.0
503
+ * @version 1.4.0
504
+ */
505
+ Group.setMethod(function toDry() {
506
+
507
+ let config = {
508
+ name : this.name,
509
+ setting_id : this.setting_id,
510
+ description : this.description,
511
+ view_permission : this.view_permission,
512
+ edit_persmission : this.edit_persmission,
513
+ };
514
+
515
+ let children = [...this.children.values()];
516
+
517
+ return {
518
+ value: {
519
+ config,
520
+ children,
521
+ }
522
+ };
523
+ });
524
+
525
+ /**
526
+ * Get a child
527
+ *
528
+ * @author Jelle De Loecker <jelle@elevenways.be>
529
+ * @since 1.4.0
530
+ * @version 1.4.0
531
+ *
532
+ * @param {string} name
533
+ *
534
+ * @return {Alchemy.Setting.Group|Alchemy.Setting.Definition}
535
+ */
536
+ Group.setMethod(function get(name) {
537
+
538
+ let pieces = name.split('.'),
539
+ current = this,
540
+ piece;
541
+
542
+ if (pieces[0] == current.name) {
543
+ pieces.shift();
544
+ }
545
+
546
+ while (pieces.length) {
547
+ piece = pieces.shift();
548
+
549
+ current = current.children.get(piece);
550
+
551
+ if (!current) {
552
+ return;
553
+ }
554
+ }
555
+
556
+ return current;
557
+ });
558
+
559
+ /**
560
+ * Return the basic record for JSON
561
+ *
562
+ * @author Jelle De Loecker <jelle@elevenways.be>
563
+ * @since 1.4.0
564
+ * @version 1.4.0
565
+ *
566
+ * @param {string} name
567
+ *
568
+ * @return {Alchemy.Setting.Group}
569
+ */
570
+ Group.setMethod(function createGroup(name) {
571
+
572
+ let existing = this.get(name);
573
+
574
+ if (existing) {
575
+ throw new Error('Cannot create setting group "' + name + '", it already exists');
576
+ }
577
+
578
+ let group = new Group(name, this);
579
+ this.children.set(name, group);
580
+
581
+ return group;
582
+ });
583
+
584
+ /**
585
+ * Add a setting
586
+ *
587
+ * @author Jelle De Loecker <jelle@elevenways.be>
588
+ * @since 1.4.0
589
+ * @version 1.4.0
590
+ *
591
+ * @param {string} name
592
+ * @param {Object} config
593
+ *
594
+ * @return {Alchemy.Setting.Definition}
595
+ */
596
+ Group.setMethod(function addSetting(name, config) {
597
+
598
+ let existing = this.get(name);
599
+
600
+ if (existing) {
601
+ throw new Error('Cannot create setting "' + name + '", it already exists');
602
+ }
603
+
604
+ let setting = new Definition(name, config, this);
605
+ this.children.set(name, setting);
606
+
607
+ return setting;
608
+ });
609
+
610
+ /**
611
+ * Create the settings with default values
612
+ *
613
+ * @author Jelle De Loecker <jelle@elevenways.be>
614
+ * @since 1.4.0
615
+ * @version 1.4.0
616
+ */
617
+ Group.setMethod(function generateValue() {
618
+
619
+ const result = new GroupValue(this);
620
+ const object = {};
621
+
622
+ let definition,
623
+ key;
624
+
625
+ for ([key, definition] of this.children) {
626
+ object[key] = definition.generateValue();
627
+ }
628
+
629
+ this.setDefaultValue(result, object);
630
+
631
+ return result;
632
+ });
633
+
634
+
635
+ /**
636
+ * Set the default value if no value has been set yet
637
+ *
638
+ * @author Jelle De Loecker <jelle@elevenways.be>
639
+ * @since 1.4.0
640
+ * @version 1.4.0
641
+ *
642
+ * @param {Alchemy.Setting.Value} target
643
+ * @param {Mixed} value
644
+ */
645
+ Group.setMethod(function setDefaultValue(target, value) {
646
+
647
+ if (!value || typeof value != 'object') {
648
+ return;
649
+ }
650
+
651
+ if (value instanceof Value) {
652
+
653
+ if (!value.is_group) {
654
+ throw new Error('Cannot set default value of non-group value');
655
+ }
656
+
657
+ value = value[VALUE];
658
+ }
659
+
660
+ let object = this.assign(target[VALUE], value, true);
661
+ target[VALUE] = object;
662
+
663
+ return target;
664
+ });
665
+
666
+ /**
667
+ * Set the values silently
668
+ *
669
+ * @author Jelle De Loecker <jelle@elevenways.be>
670
+ * @since 1.4.0
671
+ * @version 1.4.0
672
+ *
673
+ * @param {Alchemy.Setting.Value} target
674
+ * @param {Mixed} value
675
+ */
676
+ Group.setMethod(function setValueSilently(target, value) {
677
+
678
+ if (!value || typeof value != 'object') {
679
+ return;
680
+ }
681
+
682
+ if (value instanceof Value) {
683
+
684
+ if (!value.is_group) {
685
+ throw new Error('Cannot set default value of non-group value');
686
+ }
687
+
688
+ value = value[VALUE];
689
+ }
690
+
691
+ let object = this.assign(target[VALUE], value, false, false);
692
+ target[VALUE] = object;
693
+
694
+ return target;
695
+ });
696
+
697
+ /**
698
+ * Set the value
699
+ *
700
+ * @author Jelle De Loecker <jelle@elevenways.be>
701
+ * @since 1.4.0
702
+ * @version 1.4.0
703
+ *
704
+ * @param {Alchemy.Setting.Value} target
705
+ * @param {Mixed} value
706
+ */
707
+ Group.setMethod(function setValue(target, value) {
708
+
709
+ if (!value || typeof value != 'object') {
710
+ return;
711
+ }
712
+
713
+ if (value instanceof Value) {
714
+
715
+ if (!value.is_group) {
716
+ throw new Error('Cannot set default value of non-group value');
717
+ }
718
+
719
+ value = value[VALUE];
720
+ }
721
+
722
+ let object = this.assign(target[VALUE], value, false, true);
723
+ target[VALUE] = object;
724
+
725
+ return target;
726
+ });
727
+
728
+ /**
729
+ * Apply the given values to the given object
730
+ *
731
+ * @author Jelle De Loecker <jelle@elevenways.be>
732
+ * @since 1.4.0
733
+ * @version 1.4.0
734
+ *
735
+ * @param {Object} target
736
+ * @param {Object} values
737
+ * @param {boolean} default_only
738
+ * @param {boolean} do_actions
739
+ */
740
+ Group.setMethod(function assign(target, values, default_only, do_actions = true) {
741
+
742
+ if (!Object.isPlainObject(target)) {
743
+ if (target.is_group) {
744
+ target = target[VALUE];
745
+ } else {
746
+ throw new Error('Cannot assign values to non-object');
747
+ }
748
+ }
749
+
750
+ if (!values) {
751
+ return target;
752
+ }
753
+
754
+ if (!Object.isPlainObject(values)) {
755
+ if (values.is_group) {
756
+ values = values[VALUE];
757
+ } else {
758
+ throw new Error('Cannot assign non-object values');
759
+ }
760
+ }
761
+
762
+ let current_value,
763
+ source_value,
764
+ definition,
765
+ group,
766
+ key;
767
+
768
+ for (key in values) {
769
+
770
+ definition = this.get(key);
771
+ current_value = target[key];
772
+ source_value = values[key];
773
+
774
+ if (!definition) {
775
+ if (source_value && (typeof source_value == 'object' && !(source_value instanceof SettingValue))) {
776
+ definition = new Group(key, this);
777
+ } else {
778
+ definition = new Definition(key, {}, this);
779
+ }
780
+ }
781
+
782
+ if (definition.is_group) {
783
+ group = definition;
784
+ } else {
785
+ group = null;
786
+ }
787
+
788
+ // Make sure it's correct
789
+ if (group) {
790
+ if (!target[key] || typeof target[key] !== 'object') {
791
+ target[key] = group.generateValue();
792
+ }
793
+
794
+ group.assign(target[key], values[key], default_only, do_actions);
795
+ continue;
796
+ }
797
+
798
+ if (!current_value) {
799
+ current_value = definition.generateValue();
800
+ target[key] = current_value;
801
+ }
802
+
803
+ // Set the value by default
804
+ if (default_only) {
805
+ current_value.setDefaultValue(values[key]);
806
+ } else if (do_actions) {
807
+ current_value.setValue(values[key]);
808
+ } else {
809
+ current_value.setValueSilently(values[key]);
810
+ }
811
+ }
812
+
813
+ return target;
814
+ });
815
+
816
+ /**
817
+ * Create a recursive flat map
818
+ *
819
+ * @author Jelle De Loecker <jelle@elevenways.be>
820
+ * @since 1.4.0
821
+ * @version 1.4.0
822
+ *
823
+ * @param {boolean} add_groups
824
+ *
825
+ * @return {Map}
826
+ */
827
+ Group.setMethod(function createFlatMap(add_groups = false) {
828
+ return this._addToMap(new Map(), add_groups);
829
+ });
830
+
831
+ /**
832
+ * Add to the given map
833
+ *
834
+ * @author Jelle De Loecker <jelle@elevenways.be>
835
+ * @since 1.4.0
836
+ * @version 1.4.0
837
+ *
838
+ * @param {boolean} add_groups
839
+ *
840
+ * @return {Map}
841
+ */
842
+ Group.setMethod(function _addToMap(map, add_groups = false) {
843
+
844
+ let definition,
845
+ key,
846
+ id;
847
+
848
+ for ([key, definition] of this.children) {
849
+
850
+ if (definition.is_group) {
851
+ if (add_groups) {
852
+ // @TODO
853
+ }
854
+
855
+ definition._addToMap(map, add_groups);
856
+ continue;
857
+ }
858
+
859
+ id = definition.setting_id;
860
+ map.set(id, definition.toEnumEntry());
861
+ }
862
+
863
+ return map;
864
+ });
865
+
866
+
867
+ /**
868
+ * Create the backed map for use in enums
869
+ *
870
+ * @author Jelle De Loecker <jelle@elevenways.be>
871
+ * @since 1.4.0
872
+ * @version 1.4.0
873
+ *
874
+ * @return {Alchemy.Map.Enum}
875
+ */
876
+ Group.setMethod(function createEnumMap() {
877
+ return new Classes.Alchemy.Map.Enum(() => {
878
+ return this.createFlatMap();
879
+ });
880
+ });
881
+
882
+ /**
883
+ * Get the configuration for the editor
884
+ *
885
+ * @author Jelle De Loecker <jelle@elevenways.be>
886
+ * @since 1.4.0
887
+ * @version 1.4.0
888
+ *
889
+ * @param {Value} root_value
890
+ * @param {Conduit} editor_context
891
+ *
892
+ * @return {Object}
893
+ */
894
+ Group.setMethod(function getEditorConfiguration(root_value, editor_context) {
895
+
896
+ let result = {
897
+ is_root : this.group == null,
898
+ name : this.name,
899
+ group_id : this.setting_id,
900
+ settings : [],
901
+ children : [],
902
+ };
903
+
904
+ let definition,
905
+ key;
906
+
907
+ for ([key, definition] of this.children) {
908
+
909
+ if (definition.view_permission) {
910
+ if (editor_context) {
911
+ if (!editor_context.hasPermission(definition.view_permission)) {
912
+ continue;
913
+ }
914
+ }
915
+ }
916
+
917
+ if (definition.is_group) {
918
+ result.children.push(definition.getEditorConfiguration(root_value, editor_context));
919
+ continue;
920
+ }
921
+
922
+ result.settings.push(definition.getEditorConfiguration(root_value, editor_context));
923
+ }
924
+
925
+ return result;
926
+ });
927
+
928
+ /**
929
+ * The base Value class
930
+ *
931
+ * @author Jelle De Loecker <jelle@elevenways.be>
932
+ * @since 1.4.0
933
+ * @version 1.4.0
934
+ *
935
+ * @param {Alchemy.Setting.Base} definition The definition or group
936
+ */
937
+ const Value = Function.inherits('Alchemy.Setting.Base', function Value(definition) {
938
+ // The definition of this setting
939
+ this.definition = definition;
940
+ });
941
+
942
+ /**
943
+ * Mark this as an "abstract" class
944
+ *
945
+ * @author Jelle De Loecker <jelle@elevenways.be>
946
+ * @since 1.4.0
947
+ * @version 1.4.0
948
+ */
949
+ Value.makeAbstractClass(true);
950
+
951
+ /**
952
+ * Get the setting_id
953
+ *
954
+ * @author Jelle De Loecker <jelle@elevenways.be>
955
+ * @since 1.4.0
956
+ * @version 1.4.0
957
+ *
958
+ * @type {string}
959
+ */
960
+ Value.setProperty(function setting_id() {
961
+ return this.definition.setting_id;
962
+ });
963
+
964
+ /**
965
+ * A group value instance
966
+ *
967
+ * @author Jelle De Loecker <jelle@elevenways.be>
968
+ * @since 1.4.0
969
+ * @version 1.4.0
970
+ *
971
+ * @param {Alchemy.Setting.Group} group The definition of the group
972
+ */
973
+ const GroupValue = Function.inherits('Alchemy.Setting.Value', function GroupValue(group) {
974
+
975
+ GroupValue.super.call(this, group);
976
+
977
+ this.has_default_value = false;
978
+ this[VALUE] = {};
979
+ });
980
+
981
+ /**
982
+ * unDry an object
983
+ *
984
+ * @author Jelle De Loecker <jelle@elevenways.be>
985
+ * @since 1.4.0
986
+ * @version 1.4.0
987
+ *
988
+ * @param {Object} obj
989
+ * @param {boolean|string} cloned
990
+ *
991
+ * @return {GroupValue}
992
+ */
993
+ GroupValue.setStatic(function unDry(obj, cloned) {
994
+
995
+ let result = new GroupValue(obj.group);
996
+
997
+ for (let key in obj.children) {
998
+ let value = obj.children[key];
999
+ result[VALUE][key] = value;
1000
+ }
1001
+
1002
+ return result;
1003
+ });
1004
+
1005
+ /**
1006
+ * Is this a group?
1007
+ *
1008
+ * @author Jelle De Loecker <jelle@elevenways.be>
1009
+ * @since 1.4.0
1010
+ * @version 1.4.0
1011
+ *
1012
+ * @type {boolean}
1013
+ */
1014
+ GroupValue.setProperty(function is_group() {
1015
+ return true;
1016
+ });
1017
+
1018
+ /**
1019
+ * Return the json-dry representation
1020
+ *
1021
+ * @author Jelle De Loecker <jelle@elevenways.be>
1022
+ * @since 1.4.0
1023
+ * @version 1.4.0
1024
+ */
1025
+ GroupValue.setMethod(function toDry() {
1026
+
1027
+ let definition = this.definition,
1028
+ children = this[VALUE];
1029
+
1030
+ return {
1031
+ value: {
1032
+ group : definition,
1033
+ children,
1034
+ }
1035
+ };
1036
+ });
1037
+
1038
+ /**
1039
+ * Get this group as a simple object
1040
+ *
1041
+ * @author Jelle De Loecker <jelle@elevenways.be>
1042
+ * @since 1.4.0
1043
+ * @version 1.4.0
1044
+ */
1045
+ GroupValue.setMethod(function toObject() {
1046
+
1047
+ let result = {},
1048
+ entry,
1049
+ key;
1050
+
1051
+ for (key in this[VALUE]) {
1052
+ entry = this[VALUE][key];
1053
+
1054
+ if (!entry) {
1055
+ continue;
1056
+ }
1057
+
1058
+ result[key] = entry.toObject();
1059
+ }
1060
+
1061
+ return result;
1062
+ });
1063
+
1064
+ /**
1065
+ * Return the simple representation of this value.
1066
+ *
1067
+ * @author Jelle De Loecker <jelle@elevenways.be>
1068
+ * @since 1.4.0
1069
+ * @version 1.4.0
1070
+ */
1071
+ GroupValue.setMethod(function toSimple() {
1072
+ return this.toObject();
1073
+ });
1074
+
1075
+ /**
1076
+ * Get the configuration for the editor
1077
+ * (Including the current value)
1078
+ *
1079
+ * @author Jelle De Loecker <jelle@elevenways.be>
1080
+ * @since 1.4.0
1081
+ * @version 1.4.0
1082
+ *
1083
+ * @param {*} editor_context
1084
+ *
1085
+ * @return {Object}
1086
+ */
1087
+ GroupValue.setMethod(function getEditorConfiguration(editor_context) {
1088
+ return this.definition.getEditorConfiguration(this, editor_context);
1089
+ });
1090
+
1091
+ /**
1092
+ * Remove an entry
1093
+ *
1094
+ * @author Jelle De Loecker <jelle@elevenways.be>
1095
+ * @since 1.4.0
1096
+ * @version 1.4.0
1097
+ */
1098
+ GroupValue.setMethod(function remove(name) {
1099
+ this[VALUE][name] = undefined;
1100
+ });
1101
+
1102
+ /**
1103
+ * Get the proxy representation of this group value.
1104
+ *
1105
+ * @author Jelle De Loecker <jelle@elevenways.be>
1106
+ * @since 1.4.0
1107
+ * @version 1.4.0
1108
+ */
1109
+ GroupValue.setMethod(function toProxyObject() {
1110
+ return new MagicGroupValue(this);
1111
+ });
1112
+
1113
+ /**
1114
+ * Set the default value if no value has been set yet
1115
+ *
1116
+ * @author Jelle De Loecker <jelle@elevenways.be>
1117
+ * @since 1.4.0
1118
+ * @version 1.4.0
1119
+ */
1120
+ GroupValue.setMethod(function setDefaultValue(value) {
1121
+ this.definition.setDefaultValue(this, value);
1122
+ });
1123
+
1124
+ /**
1125
+ * Set the value
1126
+ *
1127
+ * @author Jelle De Loecker <jelle@elevenways.be>
1128
+ * @since 1.4.0
1129
+ * @version 1.4.0
1130
+ */
1131
+ GroupValue.setMethod(function setValue(value) {
1132
+ this.definition.setValue(this, value);
1133
+ });
1134
+
1135
+ /**
1136
+ * Set the value silently
1137
+ *
1138
+ * @author Jelle De Loecker <jelle@elevenways.be>
1139
+ * @since 1.4.0
1140
+ * @version 1.4.0
1141
+ */
1142
+ GroupValue.setMethod(function setValueSilently(value) {
1143
+
1144
+ if (Array.isArray(value)) {
1145
+ let entry;
1146
+
1147
+ for (entry of value) {
1148
+
1149
+ if (!entry.setting_id) {
1150
+ continue;
1151
+ }
1152
+
1153
+ this.setPathSilently(entry.setting_id, entry.configuration.value);
1154
+ }
1155
+
1156
+ return;
1157
+ }
1158
+
1159
+ this.definition.setValueSilently(this, value);
1160
+ });
1161
+
1162
+ /**
1163
+ * Inject the given group value
1164
+ *
1165
+ * @author Jelle De Loecker <jelle@elevenways.be>
1166
+ * @since 1.4.0
1167
+ * @version 1.4.0
1168
+ */
1169
+ GroupValue.setMethod(function injectSubGroupValue(name, value) {
1170
+ this[VALUE][name] = value;
1171
+ });
1172
+
1173
+ /**
1174
+ * Get all the setting values with executable actions in order.
1175
+ *
1176
+ * @author Jelle De Loecker <jelle@elevenways.be>
1177
+ * @since 1.4.0
1178
+ * @version 1.4.0
1179
+ *
1180
+ * @return {Alchemy.Setting.Value[]} sorted_values
1181
+ */
1182
+ GroupValue.setMethod(function getSortedValues() {
1183
+
1184
+ let result = this.getFlattenedValues();
1185
+
1186
+ // Try to sort the values by their dependencies,
1187
+ // aiming to maintain the current order as much as possible.
1188
+ // This is done by sorting the values by their dependencies,
1189
+ // and then sorting the dependencies by their dependencies.
1190
+ // This is done recursively.
1191
+ result.sortTopological('setting_id', 'all_dependencies');
1192
+
1193
+ return result;
1194
+ });
1195
+
1196
+ /**
1197
+ * Add all the values to the given array
1198
+ *
1199
+ * @author Jelle De Loecker <jelle@elevenways.be>
1200
+ * @since 1.4.0
1201
+ * @version 1.4.0
1202
+ *
1203
+ * @return {Alchemy.Setting.Value[]}
1204
+ */
1205
+ GroupValue.setMethod(function getFlattenedValues() {
1206
+
1207
+ let result = [],
1208
+ entry,
1209
+ key;
1210
+
1211
+ for (key in this[VALUE]) {
1212
+ entry = this[VALUE][key];
1213
+
1214
+ if (!entry) {
1215
+ continue;
1216
+ }
1217
+
1218
+ if (entry.is_group) {
1219
+ result.push(...entry.getFlattenedValues());
1220
+ } else {
1221
+ result.push(entry);
1222
+ }
1223
+ }
1224
+
1225
+ return result;
1226
+ });
1227
+
1228
+ /**
1229
+ * Get the value at the given path
1230
+ *
1231
+ * @author Jelle De Loecker <jelle@elevenways.be>
1232
+ * @since 1.4.0
1233
+ * @version 1.4.0
1234
+ *
1235
+ * @param {string|Array} path
1236
+ *
1237
+ * @return {Value}
1238
+ */
1239
+ GroupValue.setMethod(function get(path) {
1240
+
1241
+ if (typeof path == 'string') {
1242
+ path = path.split('.');
1243
+ }
1244
+
1245
+ if (path && this.definition.group == null && path[0] == this.definition.name) {
1246
+ path.shift();
1247
+ }
1248
+
1249
+ if (!path || path.length == 0) {
1250
+ return this;
1251
+ }
1252
+
1253
+ if (path.length == 1) {
1254
+ return this[VALUE][path[0]];
1255
+ }
1256
+
1257
+ let current = this,
1258
+ piece;
1259
+
1260
+ while (path.length) {
1261
+ piece = path.shift();
1262
+
1263
+ current = current.get(piece);
1264
+
1265
+ if (!current) {
1266
+ return null;
1267
+ }
1268
+ }
1269
+
1270
+ return current;
1271
+ });
1272
+
1273
+ /**
1274
+ * Set via a path (but silently).
1275
+ * Any non-existing group will be created.
1276
+ *
1277
+ * @author Jelle De Loecker <jelle@elevenways.be>
1278
+ * @since 1.4.0
1279
+ * @version 1.4.0
1280
+ *
1281
+ * @param {string|Array} path
1282
+ * @param {Mixed} raw_value
1283
+ *
1284
+ * @return {Value}
1285
+ */
1286
+ GroupValue.setMethod(function setPathSilently(path, raw_value) {
1287
+ return this._setPath(true, path, raw_value);
1288
+ });
1289
+
1290
+ /**
1291
+ * Set via a path.
1292
+ * Any non-existing group will be created.
1293
+ *
1294
+ * @author Jelle De Loecker <jelle@elevenways.be>
1295
+ * @since 1.4.0
1296
+ * @version 1.4.0
1297
+ *
1298
+ * @param {string|Array} path
1299
+ * @param {Mixed} raw_value
1300
+ *
1301
+ * @return {Value}
1302
+ */
1303
+ GroupValue.setMethod(function setPath(path, raw_value) {
1304
+ return this._setPath(false, path, raw_value);
1305
+ });
1306
+
1307
+ /**
1308
+ * Set via a path.
1309
+ * Any non-existing group will be created.
1310
+ *
1311
+ * @author Jelle De Loecker <jelle@elevenways.be>
1312
+ * @since 1.4.0
1313
+ * @version 1.4.0
1314
+ *
1315
+ * @param {boolean} silent
1316
+ * @param {string|Array} path
1317
+ * @param {Mixed} raw_value
1318
+ *
1319
+ * @return {Value}
1320
+ */
1321
+ GroupValue.setMethod(function _setPath(silent, path, raw_value) {
1322
+
1323
+ if (typeof path == 'string') {
1324
+ path = path.split('.');
1325
+ }
1326
+
1327
+ if (this.definition.group == null && path[0] == this.definition.name) {
1328
+ path.shift();
1329
+ }
1330
+
1331
+ let object = Object.setPath({}, path, raw_value);
1332
+
1333
+ if (silent) {
1334
+ this.setValueSilently(object);
1335
+ } else {
1336
+ this.setValue(object);
1337
+ }
1338
+
1339
+ return this.getPath(path);
1340
+ });
1341
+
1342
+ /**
1343
+ * Convert to a datasource array
1344
+ *
1345
+ * @author Jelle De Loecker <jelle@elevenways.be>
1346
+ * @since 1.4.0
1347
+ * @version 1.4.0
1348
+ */
1349
+ GroupValue.setMethod(function toDatasourceArray() {
1350
+
1351
+ let result = [],
1352
+ flattened = this.getFlattenedValues(),
1353
+ entry;
1354
+
1355
+ for (entry of flattened) {
1356
+
1357
+ if (entry.has_default_value) {
1358
+ continue;
1359
+ }
1360
+
1361
+ result.push({
1362
+ setting_id : entry.setting_id,
1363
+ configuration : {
1364
+ value : entry.get(),
1365
+ }
1366
+ });
1367
+ }
1368
+
1369
+ return result;
1370
+ });
1371
+
1372
+ /**
1373
+ * A value instance of a setting
1374
+ *
1375
+ * @author Jelle De Loecker <jelle@elevenways.be>
1376
+ * @since 1.4.0
1377
+ * @version 1.4.0
1378
+ *
1379
+ * @param {Alchemy.Setting.Definition} definition The definition of the setting
1380
+ */
1381
+ const SettingValue = Function.inherits('Alchemy.Setting.Value', function SettingValue(definition) {
1382
+
1383
+ SettingValue.super.call(this, definition);
1384
+
1385
+ // Is this the default value?
1386
+ // meaning: this value does not come from any manual override
1387
+ // (Groups do not have default values)
1388
+ this.has_default_value = true;
1389
+
1390
+ // The actual value
1391
+ this[VALUE] = null;
1392
+
1393
+ // How many times the action has been executed
1394
+ this.action_execution_count = 0;
1395
+ });
1396
+
1397
+ /**
1398
+ * unDry an object
1399
+ *
1400
+ * @author Jelle De Loecker <jelle@elevenways.be>
1401
+ * @since 1.4.0
1402
+ * @version 1.4.0
1403
+ *
1404
+ * @param {Object} obj
1405
+ * @param {boolean|string} cloned
1406
+ *
1407
+ * @return {SettingValue}
1408
+ */
1409
+ SettingValue.setStatic(function unDry(obj, cloned) {
1410
+
1411
+ let result = new this(obj.definition);
1412
+
1413
+ result[VALUE] = obj.value;
1414
+ result.has_default_value = obj.has_default_value;
1415
+
1416
+ return result;
1417
+ });
1418
+
1419
+ /**
1420
+ * Get all the dependency ids
1421
+ *
1422
+ * @author Jelle De Loecker <jelle@elevenways.be>
1423
+ * @since 1.4.0
1424
+ * @version 1.4.0
1425
+ *
1426
+ * @return {string[]}
1427
+ */
1428
+ SettingValue.setProperty(function all_dependencies() {
1429
+ return this.definition.all_dependencies;
1430
+ });
1431
+
1432
+ /**
1433
+ * Return the json-dry representation
1434
+ *
1435
+ * @author Jelle De Loecker <jelle@elevenways.be>
1436
+ * @since 1.4.0
1437
+ * @version 1.4.0
1438
+ */
1439
+ SettingValue.setMethod(function toDry() {
1440
+ return {
1441
+ value: {
1442
+ definition : this.definition,
1443
+ value : this[VALUE],
1444
+ has_default_value : this.has_default_value,
1445
+ }
1446
+ };
1447
+ });
1448
+
1449
+ /**
1450
+ * Get this group as a simple object
1451
+ *
1452
+ * @author Jelle De Loecker <jelle@elevenways.be>
1453
+ * @since 1.4.0
1454
+ * @version 1.4.0
1455
+ */
1456
+ SettingValue.setMethod(function toObject() {
1457
+ return this.get();
1458
+ });
1459
+
1460
+ /**
1461
+ * Return the simple representation of this value.
1462
+ *
1463
+ * @author Jelle De Loecker <jelle@elevenways.be>
1464
+ * @since 1.4.0
1465
+ * @version 1.4.0
1466
+ */
1467
+ SettingValue.setMethod(function toSimple() {
1468
+ return this.get();
1469
+ });
1470
+
1471
+ /**
1472
+ * Get the current value
1473
+ *
1474
+ * @author Jelle De Loecker <jelle@elevenways.be>
1475
+ * @since 1.4.0
1476
+ * @version 1.4.0
1477
+ */
1478
+ SettingValue.setMethod(function get(key) {
1479
+ let result = this[VALUE];
1480
+
1481
+ if (key != null) {
1482
+ result = result[key];
1483
+ }
1484
+
1485
+ return result;
1486
+ });
1487
+
1488
+ /**
1489
+ * Set the default value if no value has been set yet
1490
+ *
1491
+ * @author Jelle De Loecker <jelle@elevenways.be>
1492
+ * @since 1.4.0
1493
+ * @version 1.4.0
1494
+ */
1495
+ SettingValue.setMethod(function setDefaultValue(value) {
1496
+
1497
+ if (!this.has_default_value) {
1498
+ return;
1499
+ }
1500
+
1501
+ if (value != null && typeof value == 'object') {
1502
+
1503
+ if (value instanceof Value) {
1504
+ value = value[VALUE];
1505
+ }
1506
+
1507
+ if (value && typeof value == 'object') {
1508
+ value = JSON.clone(value);
1509
+ }
1510
+ }
1511
+
1512
+ this[VALUE] = value;
1513
+ });
1514
+
1515
+ /**
1516
+ * Set the value
1517
+ *
1518
+ * @author Jelle De Loecker <jelle@elevenways.be>
1519
+ * @since 1.4.0
1520
+ * @version 1.4.0
1521
+ */
1522
+ SettingValue.setMethod(function setValueSilently(value) {
1523
+
1524
+ if (value instanceof Value) {
1525
+ value = value[VALUE];
1526
+ }
1527
+
1528
+ // Even though the default value and this new value might be the same,
1529
+ // it is no longer considered to be the "default" value!
1530
+ this.has_default_value = false;
1531
+
1532
+ value = this.definition.cast(value);
1533
+
1534
+ this[VALUE] = value;
1535
+ });
1536
+
1537
+ /**
1538
+ * Test and set the given value
1539
+ *
1540
+ * @author Jelle De Loecker <jelle@elevenways.be>
1541
+ * @since 1.4.0
1542
+ * @version 1.4.0
1543
+ */
1544
+ SettingValue.setMethod(function setValue(value) {
1545
+ this.setValueSilently(value);
1546
+ return this.executeAction();
1547
+ });
1548
+
1549
+ /**
1550
+ * Get via a path
1551
+ *
1552
+ * @author Jelle De Loecker <jelle@elevenways.be>
1553
+ * @since 1.4.0
1554
+ * @version 1.4.0
1555
+ *
1556
+ * @param {string|Array} path
1557
+ *
1558
+ * @return {Value}
1559
+ */
1560
+ Value.setMethod(function getPath(path) {
1561
+
1562
+ if (typeof path == 'string') {
1563
+ path = path.split('.');
1564
+ }
1565
+
1566
+ let current = this,
1567
+ piece;
1568
+
1569
+ while (path.length) {
1570
+ piece = path.shift();
1571
+
1572
+ current = current.get(piece);
1573
+
1574
+ if (!current) {
1575
+ return null;
1576
+ }
1577
+ }
1578
+
1579
+ return current;
1580
+ });
1581
+
1582
+ if (Blast.isBrowser) {
1583
+ return;
1584
+ }
1585
+
1586
+ /**
1587
+ * Custom Janeway representation (left side)
1588
+ *
1589
+ * @author Jelle De Loecker <jelle@elevenways.be>
1590
+ * @since 1.4.0
1591
+ * @version 1.4.0
1592
+ *
1593
+ * @return {string}
1594
+ */
1595
+ Value.setMethod(Symbol.for('janeway_arg_left'), function janewayClassIdentifier() {
1596
+ return 'A.S.' + this.constructor.name;
1597
+ });
1598
+
1599
+ /**
1600
+ * Custom Janeway representation (right side)
1601
+ *
1602
+ * @author Jelle De Loecker <jelle@elevenways.be>
1603
+ * @since 1.4.0
1604
+ * @version 1.4.0
1605
+ *
1606
+ * @return {String}
1607
+ */
1608
+ Value.setMethod(Symbol.for('janeway_arg_right'), function janewayInstanceInfo() {
1609
+ return this.definition.setting_id;
1610
+ });
1611
+
1612
+ /**
1613
+ * Perform all the actions of this group and its children
1614
+ *
1615
+ * @author Jelle De Loecker <jelle@elevenways.be>
1616
+ * @since 1.4.0
1617
+ * @version 1.4.0
1618
+ */
1619
+ GroupValue.setMethod(async function performAllActions() {
1620
+
1621
+ let sorted = this.getSortedValues();
1622
+
1623
+ for (let value of sorted) {
1624
+ let result = value.executeAction();
1625
+
1626
+ if (result) {
1627
+ await result;
1628
+ }
1629
+ }
1630
+ });
1631
+
1632
+ /**
1633
+ * Execute the action (if any is linked)
1634
+ *
1635
+ * @author Jelle De Loecker <jelle@elevenways.be>
1636
+ * @since 1.4.0
1637
+ * @version 1.4.0
1638
+ */
1639
+ SettingValue.setMethod(function executeAction() {
1640
+ this.action_execution_count++;
1641
+ return this.definition.executeAction(this);
1642
+ });
1643
+
1644
+ /**
1645
+ * A magic value getter
1646
+ *
1647
+ * @author Jelle De Loecker <jelle@elevenways.be>
1648
+ * @since 1.4.0
1649
+ * @version 1.4.0
1650
+ *
1651
+ * @param {Alchemy.Setting.Value} value The value group
1652
+ */
1653
+ const MagicGroupValue = Function.inherits('Magic', 'Alchemy.Setting', function MagicGroupValue(group_value) {
1654
+
1655
+ if (!group_value) {
1656
+ throw new Error('Cannot create magic group value without group value');
1657
+ }
1658
+
1659
+ this[VALUE] = group_value;
1660
+ });
1661
+
1662
+ /**
1663
+ * The magic getter
1664
+ *
1665
+ * @author Jelle De Loecker <jelle@elevenways.be>
1666
+ * @since 1.4.0
1667
+ * @version 1.4.0
1668
+ *
1669
+ * @param {string} key
1670
+ */
1671
+ MagicGroupValue.setMethod(function __get(key) {
1672
+
1673
+ let result = this[VALUE].get(key);
1674
+
1675
+ if (!result) {
1676
+ return;
1677
+ }
1678
+
1679
+ if (result.is_group) {
1680
+ return result.toProxyObject();
1681
+ }
1682
+
1683
+ return result.get();
1684
+ });