alchemymvc 1.3.6 → 1.3.8

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.
@@ -290,16 +290,18 @@ Alchemy.setMethod(function group(name, id, callback) {
290
290
  /**
291
291
  * Print a segment
292
292
  *
293
- * @author Jelle De Loecker <jelle@develry.be>
293
+ * @author Jelle De Loecker <jelle@elevenways.be>
294
294
  * @since 0.5.0
295
- * @version 1.1.5
295
+ * @version 1.3.7
296
296
  *
297
297
  * @param {String|Object} options
298
298
  * @param {Object} data
299
+ *
300
+ * @return {HePlaceholder}
299
301
  */
300
302
  Alchemy.setMethod(function segment(options, data) {
301
303
 
302
- var that = this;
304
+ const that = this;
303
305
 
304
306
  if (typeof options == 'string') {
305
307
  options = {
@@ -307,10 +309,14 @@ Alchemy.setMethod(function segment(options, data) {
307
309
  };
308
310
  }
309
311
 
310
- let start = Date.now();
312
+ let print_placeholder = options.print;
313
+
314
+ if (print_placeholder == null) {
315
+ print_placeholder = true;
316
+ }
311
317
 
312
318
  // Prints a placeholder
313
- this.view.async(function doAsync(next) {
319
+ return this.view.async(function doAsync(next) {
314
320
 
315
321
  var conduit,
316
322
  route;
@@ -354,7 +360,7 @@ Alchemy.setMethod(function segment(options, data) {
354
360
 
355
361
  next(null, block);
356
362
  });
357
- });
363
+ }, {print: print_placeholder});
358
364
  });
359
365
 
360
366
  /**
@@ -0,0 +1,156 @@
1
+ /**
2
+ * The Breadcrumb class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 1.3.8
8
+ * @version 1.3.8
9
+ *
10
+ * @param {String} input
11
+ */
12
+ const Breadcrumb = Function.inherits('Alchemy.Base', function Breadcrumb(input) {
13
+
14
+ this.trails = [];
15
+
16
+ if (input) {
17
+ this.addTrail(input);
18
+ }
19
+ });
20
+
21
+ /**
22
+ * Get the number of trails
23
+ *
24
+ * @author Jelle De Loecker <jelle@elevenways.be>
25
+ * @since 1.3.8
26
+ * @version 1.3.8
27
+ *
28
+ * @return {Number}
29
+ */
30
+ Breadcrumb.setProperty(function length() {
31
+ return this.trails.length;
32
+ });
33
+
34
+ /**
35
+ * Add a breadcrumb trail
36
+ *
37
+ * @author Jelle De Loecker <jelle@elevenways.be>
38
+ * @since 1.3.8
39
+ * @version 1.3.8
40
+ *
41
+ * @param {String} input
42
+ */
43
+ Breadcrumb.setMethod(function addTrail(input) {
44
+
45
+ let trails;
46
+
47
+ if (!input) {
48
+ input = '';
49
+ } else if (typeof input == 'object') {
50
+ if (input instanceof Breadcrumb) {
51
+ trails = input.trails;
52
+ } else if (input.nodeType === 1) {
53
+ // Simple way to see if it's an HTMLElement
54
+
55
+ let element = input;
56
+ input = '';
57
+
58
+ let crumb = element.getAttribute('data-breadcrumb');
59
+
60
+ if (crumb) {
61
+ input = crumb;
62
+ }
63
+
64
+ crumb = element.getAttribute('data-breadcrumbs');
65
+
66
+ if (crumb) {
67
+ input += ' ' + crumb;
68
+ }
69
+ }
70
+ }
71
+
72
+ if (!trails) {
73
+ // Split the string why whitespaces
74
+ trails = input.trim().split(/\s+|\|+/);
75
+ }
76
+
77
+ if (trails?.length) {
78
+ for (let trail of trails) {
79
+
80
+ if (!trail) {
81
+ continue;
82
+ }
83
+
84
+ // The trail has to end with a dot
85
+ if (!trail.endsWith('.')) {
86
+ trail += '.';
87
+ }
88
+
89
+ this.trails.push(trail);
90
+ }
91
+ }
92
+ });
93
+
94
+ /**
95
+ * Do these 2 breadcrumbs match loosely?
96
+ *
97
+ * @author Jelle De Loecker <jelle@elevenways.be>
98
+ * @since 1.3.8
99
+ * @version 1.3.8
100
+ *
101
+ * @param {Alchemy.Breadcrumb} other
102
+ */
103
+ Breadcrumb.setMethod(function matches(other) {
104
+ return this.matchLevel(other) > 0;
105
+ });
106
+
107
+ /**
108
+ * What leven of match is there between these 2 breadcrumbs?
109
+ *
110
+ * @author Jelle De Loecker <jelle@elevenways.be>
111
+ * @since 1.3.8
112
+ * @version 1.3.8
113
+ *
114
+ * @param {Alchemy.Breadcrumb} other
115
+ *
116
+ * @return {Number} 1 for a strict match, 2 for a loose match
117
+ */
118
+ Breadcrumb.setMethod(function matchLevel(other) {
119
+
120
+ if (!other || !this.trails.length) {
121
+ return 0;
122
+ }
123
+
124
+ if (!(other instanceof Breadcrumb)) {
125
+ other = new Breadcrumb(other);
126
+ }
127
+
128
+ let their_trail,
129
+ our_trail;
130
+
131
+ for (our_trail of this.trails) {
132
+ for (their_trail of other.trails) {
133
+ // Strict matches are always true
134
+ if (our_trail == their_trail) {
135
+ return 1;
136
+ }
137
+
138
+ if (our_trail.startsWith(their_trail)) {
139
+ return 2;
140
+ }
141
+ }
142
+ }
143
+
144
+ return 0;
145
+ });
146
+
147
+ /**
148
+ * Turn it into a string
149
+ *
150
+ * @author Jelle De Loecker <jelle@elevenways.be>
151
+ * @since 1.3.8
152
+ * @version 1.3.8
153
+ */
154
+ Breadcrumb.setMethod(function toString() {
155
+ return this.trails.join(' ');
156
+ });
@@ -147,14 +147,24 @@ Router.setMethod(function applyDirective(element, name, options) {
147
147
  }
148
148
  }
149
149
 
150
- if (config.breadcrumb) {
151
- let link_breadcrumb = this.getTrail(name, params);
150
+ let breadcrumb = config.breadcrumb;
151
+
152
+ if (options.breadcrumb) {
153
+ if (!breadcrumb) {
154
+ breadcrumb = options.breadcrumb;
155
+ } else {
156
+ breadcrumb += ' ' + options.breadcrumb;
157
+ }
158
+ }
159
+
160
+ if (config.breadcrumb || options.breadcrumb) {
161
+ let link_breadcrumb = this.getTrail(name, {...params}, options);
152
162
  element.setAttribute('data-breadcrumb', link_breadcrumb);
153
163
 
154
164
  if (this.renderer && link_breadcrumb) {
155
- let page_breadcrumb = this.renderer.internal('breadcrumb');
165
+ let page_breadcrumb = new Classes.Alchemy.Breadcrumb(this.renderer.internal('breadcrumb'));
156
166
 
157
- if (page_breadcrumb && page_breadcrumb.startsWith(link_breadcrumb)) {
167
+ if (page_breadcrumb.matches(link_breadcrumb)) {
158
168
 
159
169
  if (element.parentElement) {
160
170
  // @TODO: We need to make sure the options (classnames & such)
@@ -223,7 +233,7 @@ Router.setMethod(function applyDirective(element, name, options) {
223
233
  *
224
234
  * @author Jelle De Loecker <jelle@elevenways.be>
225
235
  * @since 0.2.0
226
- * @version 1.3.6
236
+ * @version 1.3.7
227
237
  *
228
238
  * @param {String} name
229
239
  * @param {Boolean} socket_route Look in the socket routes
@@ -288,22 +298,17 @@ Router.setMethod(function routeConfig(name, socket_route) {
288
298
  if (routes[section] != null && routes[section][name] != null) {
289
299
  let route = routes[section][name];
290
300
 
291
- let result = {
292
- section : section,
293
- name : name
294
- };
301
+ let result;
295
302
 
296
303
  if (socket_route) {
297
- result.socket_route = true;
304
+ result = {
305
+ section : section,
306
+ name : name,
307
+ socket_route : true,
308
+ };
298
309
  } else {
310
+ result = Object.create(route);
299
311
  result.socket_route = false;
300
- result.paths = route.paths;
301
- result.breadcrumb = route.breadcrumb;
302
- result.has_breadcrumb_assignments = route.has_breadcrumb_assignments;
303
- result.keys = route.keys;
304
- result.methods = route.methods;
305
- result.permission = route.permission;
306
- result.has_permission_assignments = route.has_permission_assignments;
307
312
  }
308
313
 
309
314
  let router_options;
@@ -329,9 +334,9 @@ Router.setMethod(function routeConfig(name, socket_route) {
329
334
  /**
330
335
  * Return the plain url for the given url name & parameters object
331
336
  *
332
- * @author Jelle De Loecker <jelle@develry.be>
337
+ * @author Jelle De Loecker <jelle@elevenways.be>
333
338
  * @since 0.2.0
334
- * @version 1.3.0
339
+ * @version 1.3.7
335
340
  *
336
341
  * @param {String} name
337
342
  * @param {Object} parameters
@@ -392,6 +397,33 @@ Router.setMethod(function routeUrl(name, parameters, options) {
392
397
  return '#url_config_' + name + '_notfound';
393
398
  }
394
399
 
400
+ if (config.param_definitions) {
401
+
402
+ let documents = {},
403
+ definition,
404
+ key;
405
+
406
+ for (key in config.param_definitions) {
407
+ definition = config.param_definitions[key];
408
+
409
+ // If this definition has a specific model name,
410
+ // try to get it from the parameters (and also remove it from there)
411
+ if (definition.type_class_name) {
412
+ if (!documents[definition.type_class_name]) {
413
+ documents[definition.type_class_name] = parameters[definition.type_class_name];
414
+
415
+ if (documents[definition.type_class_name]) {
416
+ delete parameters[definition.type_class_name];
417
+ }
418
+ }
419
+ }
420
+
421
+ if (!parameters[definition.name] && documents[definition.type_class_name]) {
422
+ parameters[definition.name] = documents[definition.type_class_name][definition.type_field_name];
423
+ }
424
+ }
425
+ }
426
+
395
427
  // Remove [brackets]
396
428
  url = url.replace(/\[.*?\]/g, '');
397
429
  url = url.assign(parameters, true, RURL.encodeUriQuery);
@@ -470,28 +502,72 @@ Router.setMethod(function routeUrl(name, parameters, options) {
470
502
  *
471
503
  * @author Jelle De Loecker <jelle@develry.be>
472
504
  * @since 0.3.0
473
- * @version 0.3.0
505
+ * @version 1.3.8
474
506
  *
475
507
  * @param {String} name
476
508
  * @param {Object} parameters
509
+ * @param {Object} options
477
510
  *
478
- * @return {String}
511
+ * @return {Alchemy.Breadcrumb}
479
512
  */
480
- Router.setMethod(function getTrail(name, parameters) {
513
+ Router.setMethod(function getTrail(name, parameters, options) {
481
514
 
482
- var breadcrumb,
515
+ let can_have_assignments,
516
+ trails = [],
483
517
  config;
518
+
519
+ if (options?.breadcrumb) {
520
+ can_have_assignments = true;
521
+ trails.push(options.breadcrumb);
522
+ }
484
523
 
485
524
  config = this.routeConfig(name);
486
525
 
487
- if (!config || !config.breadcrumb) {
526
+ if (!trails.length && (!config || !config.breadcrumb)) {
488
527
  return;
489
528
  }
490
529
 
491
- breadcrumb = config.breadcrumb;
530
+ let breadcrumb = new Classes.Alchemy.Breadcrumb();
531
+
532
+ if (config?.breadcrumb) {
533
+ trails.push(config.breadcrumb);
534
+ }
492
535
 
493
- if (config.has_breadcrumb_assignments) {
494
- breadcrumb = Blast.Bound.String.assign(breadcrumb, parameters).toLowerCase();
536
+ if (!can_have_assignments) {
537
+ can_have_assignments = config?.has_breadcrumb_assignments;
538
+ }
539
+
540
+ if (can_have_assignments) {
541
+ let key;
542
+ parameters = {...parameters};
543
+
544
+ for (key in parameters) {
545
+ if (key[0] == key[0].toUpperCase()) {
546
+ let value = parameters[key];
547
+
548
+ if (value && typeof value == 'object') {
549
+ let subkey;
550
+
551
+ let context = value?.$main || value;
552
+
553
+ for (subkey in context) {
554
+ if (parameters[subkey] == null) {
555
+ parameters[subkey] = context[subkey];
556
+ }
557
+ }
558
+ }
559
+ }
560
+ }
561
+ }
562
+
563
+ let trail;
564
+
565
+ for (trail of trails) {
566
+ if (can_have_assignments) {
567
+ trail = Blast.Bound.String.assign(trail, parameters).toLowerCase();
568
+ }
569
+
570
+ breadcrumb.addTrail(trail);
495
571
  }
496
572
 
497
573
  return breadcrumb;
@@ -505,9 +581,6 @@ Router.setMethod(function getTrail(name, parameters) {
505
581
  * @version 0.3.0
506
582
  */
507
583
  Router.setMethod(function printBreadcrumb() {
508
-
509
- var that = this;
510
-
511
584
  this.view.print_partial('breadcrumb/wrapper');
512
585
  });
513
586
 
@@ -717,7 +790,7 @@ Router.setMethod(function updateLanguageSwitcher(element, variables) {
717
790
  *
718
791
  * @author Jelle De Loecker <jelle@elevenways.be>
719
792
  * @since 1.2.5
720
- * @version 1.3.0
793
+ * @version 1.3.8
721
794
  *
722
795
  * @param {String} prefix The prefix to use
723
796
  * @param {Object} variables
@@ -739,6 +812,15 @@ Router.setMethod(function translateCurrentRoute(prefix, variables) {
739
812
  return;
740
813
  }
741
814
 
815
+ // Don't just translate the last rendered route, make sure it's the current one
816
+ if (info.url && Blast.isBrowser) {
817
+ let url = RURL.parse(info.url);
818
+
819
+ if (url && !url.matchesPath(window.location)) {
820
+ return;
821
+ }
822
+ }
823
+
742
824
  let config = this.routeConfig(info.route);
743
825
 
744
826
  if (!config) {
@@ -762,6 +844,8 @@ Router.setMethod(function translateCurrentRoute(prefix, variables) {
762
844
 
763
845
  // Add the get queries
764
846
  if (info.url && info.url.search) {
847
+ let key;
848
+
765
849
  for (key in info.url.query) {
766
850
 
767
851
  if (key == 'hajax' || key == 'h_diversion' || key == 'htop') {
@@ -33,4 +33,24 @@ BooleanField.setDatatype('boolean');
33
33
  */
34
34
  BooleanField.setMethod(function cast(value) {
35
35
  return Boolean(value);
36
- });
36
+ });
37
+
38
+ /**
39
+ * See if the given vlaue is considered not-empty for this field
40
+ *
41
+ * @author Jelle De Loecker <jelle@elevenways.be>
42
+ * @since 1.3.7
43
+ * @version 1.3.7
44
+ *
45
+ * @param {Mixed} value
46
+ *
47
+ * @return {Boolean}
48
+ */
49
+ BooleanField.setMethod(function valueHasContent(value) {
50
+
51
+ if (value === undefined) {
52
+ return false;
53
+ }
54
+
55
+ return true;
56
+ });
@@ -89,13 +89,11 @@ SchemaField.setProperty(function requires_translating() {
89
89
  /**
90
90
  * Get the subschema of this field
91
91
  *
92
- * @constructor
93
- *
94
- * @author Jelle De Loecker <jelle@develry.be>
92
+ * @author Jelle De Loecker <jelle@elevenways.be>
95
93
  * @since 0.2.0
96
- * @version 1.3.1
94
+ * @version 1.3.7
97
95
  *
98
- * @param {Object} record
96
+ * @param {Object} record This *should* be the schema context (might not be the root)
99
97
  * @param {String} some_path Some path to a field in the wanted schema
100
98
  *
101
99
  * @return {Schema}
@@ -597,9 +597,9 @@ Model.setMethod(function getAliasModel(alias) {
597
597
  /**
598
598
  * Query the database
599
599
  *
600
- * @author Jelle De Loecker <jelle@develry.be>
600
+ * @author Jelle De Loecker <jelle@elevenways.be>
601
601
  * @since 0.0.1
602
- * @version 1.3.4
602
+ * @version 1.3.8
603
603
  *
604
604
  * @param {String} type The type of find (first, all)
605
605
  * @param {Criteria} criteria The criteria object
@@ -660,7 +660,7 @@ Model.setMethod(function find(type, criteria, callback) {
660
660
  }
661
661
 
662
662
  if (!criteria.options.locale && this.conduit) {
663
- criteria.setOption('locale', this.conduit.prefix);
663
+ criteria.setOption('locale', this.conduit.active_prefix);
664
664
  }
665
665
 
666
666
  let that = this,
@@ -1078,6 +1078,36 @@ Field.setMethod(function getRules() {
1078
1078
  return result;
1079
1079
  });
1080
1080
 
1081
+ /**
1082
+ * See if the given vlaue is considered not-empty for this field
1083
+ *
1084
+ * @author Jelle De Loecker <jelle@elevenways.be>
1085
+ * @since 1.3.7
1086
+ * @version 1.3.7
1087
+ *
1088
+ * @param {Mixed} value
1089
+ *
1090
+ * @return {Boolean}
1091
+ */
1092
+ Field.setMethod(function valueHasContent(value) {
1093
+
1094
+ if (value == null) {
1095
+ return false;
1096
+ }
1097
+
1098
+ if (value === '') {
1099
+ return false;
1100
+ }
1101
+
1102
+ if (Array.isArray(value)) {
1103
+ if (value.length === 0) {
1104
+ return false;
1105
+ }
1106
+ }
1107
+
1108
+ return true;
1109
+ });
1110
+
1081
1111
  /**
1082
1112
  * Translate the given value
1083
1113
  *
@@ -310,7 +310,7 @@ Model.prepareProperty('datasource', function datasource() {
310
310
  * @type {Object}
311
311
  */
312
312
  Model.prepareProperty('sort', function sort() {
313
- return {created: 1};
313
+ return {[this.primary_key]: -1};
314
314
  });
315
315
 
316
316
  /**
@@ -5,7 +5,7 @@ const PathDefinition = Classes.Alchemy.PathDefinition;
5
5
  *
6
6
  * @author Jelle De Loecker <jelle@elevenways.be>
7
7
  * @since 1.3.0
8
- * @version 1.3.0
8
+ * @version 1.3.7
9
9
  */
10
10
  const PathParamDefinition = Function.inherits('Alchemy.Base', function PathParamDefinition(config) {
11
11
 
@@ -41,6 +41,9 @@ const PathParamDefinition = Function.inherits('Alchemy.Base', function PathParam
41
41
  // The optional type class constructor
42
42
  this.type_class_constructor = null;
43
43
 
44
+ // Is this a model type?
45
+ this.is_model_type = false;
46
+
44
47
  // Has all the config been parsed?
45
48
  this.is_parsed = false;
46
49
  });
@@ -79,6 +82,30 @@ PathParamDefinition.setStatic(function from(input) {
79
82
  return new this(input);
80
83
  });
81
84
 
85
+ /**
86
+ * Get the model constructor
87
+ *
88
+ * @author Jelle De Loecker <jelle@elevenways.be>
89
+ * @since 1.3.7
90
+ * @version 1.3.7
91
+ */
92
+ PathParamDefinition.enforceProperty(function model_constructor(new_value) {
93
+
94
+ if (!this.is_parsed) {
95
+ this.parseTypeDefinition();
96
+ }
97
+
98
+ if (!new_value) {
99
+ const constructor = this.type_class_constructor;
100
+
101
+ if (constructor && this.is_model_type) {
102
+ new_value = constructor;
103
+ }
104
+ }
105
+
106
+ return new_value;
107
+ });
108
+
82
109
  /**
83
110
  * Parse the type definition
84
111
  *
@@ -131,7 +158,14 @@ PathParamDefinition.setMethod(function parseTypeDefinition() {
131
158
  this.type_field_name = field_name;
132
159
 
133
160
  const Model = Classes.Alchemy.Model || Classes.Alchemy.Client.Model;
134
- this.type_class_constructor = Object.path(Model, class_name) || Object.path(Classes.Alchemy, class_name) || Object.path(Classes, class_name);
161
+
162
+ this.type_class_constructor = Object.path(Model, class_name);
163
+
164
+ if (this.type_class_constructor) {
165
+ this.is_model_type = true;
166
+ } else {
167
+ this.type_class_constructor = Object.path(Classes.Alchemy, class_name) || Object.path(Classes, class_name);
168
+ }
135
169
  }
136
170
  });
137
171
 
@@ -129,6 +129,116 @@ Route.setProperty(function has_type_class_checks() {
129
129
  return result;
130
130
  });
131
131
 
132
+ /**
133
+ * Routes with parameters can have schemas
134
+ *
135
+ * @author Jelle De Loecker <jelle@elevenways.be>
136
+ * @since 1.3.7
137
+ * @version 1.3.7
138
+ *
139
+ * @return {Schema}
140
+ */
141
+ Route.enforceProperty(function schema(new_value) {
142
+
143
+ if (new_value) {
144
+ return new_value;
145
+ }
146
+
147
+ if (!this.has_path_assignments) {
148
+ return false;
149
+ }
150
+
151
+ let added_models = {},
152
+ added_fields = {},
153
+ prefix,
154
+ param,
155
+ model_constructor,
156
+ definition;
157
+
158
+ new_value = alchemy.createSchema();
159
+
160
+ for (prefix in this.paths) {
161
+ definition = this.paths[prefix];
162
+
163
+ if (!definition?.param_definitions?.length) {
164
+ continue;
165
+ }
166
+
167
+ for (param of definition.param_definitions) {
168
+ model_constructor = param.model_constructor;
169
+
170
+ if (model_constructor) {
171
+
172
+ if (added_models[model_constructor.name]) {
173
+ continue;
174
+ }
175
+
176
+ if (added_fields[param.name]) {
177
+ continue;
178
+ }
179
+
180
+ added_models[model_constructor.name] = true;
181
+
182
+ try {
183
+ new_value.belongsTo(model_constructor.name);
184
+ } catch (err) {
185
+ // Ignored
186
+ alchemy.distinctProblem('route-schema-' + this.name, 'Route schema error', {error: err});
187
+ }
188
+ } else {
189
+ new_value.addField(param.name, 'String');
190
+ added_fields[param.name] = true;
191
+ }
192
+ }
193
+ }
194
+
195
+ return new_value;
196
+ });
197
+
198
+ /**
199
+ * Get all the param definitions
200
+ *
201
+ * @author Jelle De Loecker <jelle@elevenways.be>
202
+ * @since 1.3.7
203
+ * @version 1.3.7
204
+ *
205
+ * @return {Object}
206
+ */
207
+ Route.enforceProperty(function param_definitions(new_value) {
208
+
209
+ if (new_value) {
210
+ return new_value;
211
+ }
212
+
213
+ let prefix,
214
+ param,
215
+ key,
216
+ definition;
217
+
218
+ new_value = {};
219
+
220
+ for (prefix in this.paths) {
221
+ definition = this.paths[prefix];
222
+
223
+ if (!definition.param_definitions?.length) {
224
+ continue;
225
+ }
226
+
227
+ for (param of definition.param_definitions) {
228
+ key = prefix + '_' + param.name;
229
+
230
+ new_value[key] = {
231
+ name : param.name,
232
+ type_class_name : param.type_class_name,
233
+ type_field_name : param.type_field_name,
234
+ is_model_type : param.is_model_type,
235
+ };
236
+ }
237
+ }
238
+
239
+ return new_value;
240
+ });
241
+
132
242
  /**
133
243
  * Set the breadcrumb for this route
134
244
  *
@@ -1163,7 +1163,7 @@ RouterClass.setMethod(function getOptions(result) {
1163
1163
  *
1164
1164
  * @author Jelle De Loecker <jelle@elevenways.be>
1165
1165
  * @since 0.2.0
1166
- * @version 1.3.0
1166
+ * @version 1.3.7
1167
1167
  *
1168
1168
  * @param {Object} result Optional object to store sectioned results in
1169
1169
  *
@@ -1212,6 +1212,8 @@ RouterClass.setMethod(function getRoutes(result) {
1212
1212
  has_permission_assignments : route.has_permission_assignments || undefined,
1213
1213
  title : route.title,
1214
1214
  visible_location : route.visible_location,
1215
+ schema : route.schema,
1216
+ param_definitions : route.param_definitions,
1215
1217
  };
1216
1218
  }
1217
1219
 
@@ -519,6 +519,59 @@ Alchemy.setMethod(function routeConfig(name) {
519
519
 
520
520
  const markLinkElement = Alchemy.prototype.markLinkElement;
521
521
 
522
+ /**
523
+ * Get a live map of all the routes
524
+ *
525
+ * @author Jelle De Loecker <jelle@elevenways.be>
526
+ * @since 1.3.8
527
+ * @version 1.3.8
528
+ *
529
+ * @return {Develry.BackedMap}
530
+ */
531
+ Alchemy.setMethod(function getRoutes() {
532
+ return new Classes.Develry.BackedMap(getRoutesDict);
533
+ });
534
+
535
+ /**
536
+ * The live-map dictionary creator
537
+ *
538
+ * @author Jelle De Loecker <jelle@elevenways.be>
539
+ * @since 1.3.8
540
+ * @version 1.3.8
541
+ *
542
+ * @return {Object}
543
+ */
544
+ function getRoutesDict() {
545
+ let routes;
546
+
547
+ if (Blast.isNode) {
548
+ routes = {};
549
+
550
+ let roots = Router.getRoutes();
551
+
552
+ for (let key in roots) {
553
+ let root = roots[key];
554
+
555
+ for (let name in root) {
556
+ routes[name] = root[name];
557
+ }
558
+ }
559
+
560
+ } else {
561
+ routes = {};
562
+
563
+ for (let root in hawkejs.scene.exposed.routes) {
564
+ let section = hawkejs.scene.exposed.routes[root];
565
+
566
+ for (let key in section) {
567
+ routes[key] = section[key];
568
+ }
569
+ }
570
+ }
571
+
572
+ return routes;
573
+ }
574
+
522
575
  // From here on, only client-side code is added
523
576
  if (Blast.isBrowser) {
524
577
  window.alchemy = new Alchemy();
@@ -582,21 +635,18 @@ Alchemy.setMethod(function linkup(type, data, cb) {
582
635
  *
583
636
  * @author Jelle De Loecker <jelle@develry.be>
584
637
  * @since 0.3.0
585
- * @version 1.3.0
638
+ * @version 1.3.8
586
639
  *
587
640
  * @param {Object} variables
588
641
  * @param {Object} render_data
589
642
  */
590
643
  Alchemy.setMethod(function markLinks(variables, render_data) {
591
644
 
592
- var page_breadcrumb,
593
- el_breadcrumbs,
594
- el_breadcrumb,
645
+ let page_breadcrumb,
595
646
  placeholder,
596
647
  href_val,
597
648
  elements,
598
649
  element,
599
- temp,
600
650
  url,
601
651
  i;
602
652
 
@@ -607,7 +657,7 @@ Alchemy.setMethod(function markLinks(variables, render_data) {
607
657
  }
608
658
 
609
659
  // Get the newly set breadcrumb
610
- page_breadcrumb = variables.__breadcrumb;
660
+ page_breadcrumb = new Classes.Alchemy.Breadcrumb(variables.__breadcrumb);
611
661
 
612
662
  // Get all the language switcher anchors
613
663
  elements = document.querySelectorAll('[data-alchemy-language-switch]');
@@ -621,30 +671,7 @@ Alchemy.setMethod(function markLinks(variables, render_data) {
621
671
 
622
672
  for (i = 0; i < elements.length; i++) {
623
673
  element = elements[i];
624
-
625
- // Create the breadcrumbs array
626
- el_breadcrumbs = [];
627
-
628
- // Get the element's set breadcrumb
629
- el_breadcrumb = element.getAttribute('data-breadcrumb');
630
-
631
- if (el_breadcrumb) {
632
- el_breadcrumbs.push(el_breadcrumb);
633
- }
634
-
635
- el_breadcrumb = element.getAttribute('data-breadcrumbs');
636
-
637
- if (el_breadcrumb) {
638
- el_breadcrumbs.include(el_breadcrumb.split('|'));
639
- }
640
-
641
- if (el_breadcrumbs.indexOf(page_breadcrumb) > -1) {
642
- markLinkElement(element, 1);
643
- } else if (page_breadcrumb && page_breadcrumb.startsWithAny(el_breadcrumbs)) {
644
- markLinkElement(element, 2);
645
- } else {
646
- markLinkElement(element, false);
647
- }
674
+ markLinkElement(element, page_breadcrumb.matchLevel(element));
648
675
  }
649
676
 
650
677
  // Get all anchors without breadcrumbs
@@ -702,9 +729,9 @@ Alchemy.setMethod(function markLinks(variables, render_data) {
702
729
  /**
703
730
  * Reload all stylesheets
704
731
  *
705
- * @author Jelle De Loecker <jelle@develry.be>
732
+ * @author Jelle De Loecker <jelle@elevenways.be>
706
733
  * @since 1.0.0
707
- * @version 1.2.5
734
+ * @version 1.3.8
708
735
  */
709
736
  Alchemy.setMethod(function reloadStylesheets() {
710
737
 
@@ -718,15 +745,15 @@ Alchemy.setMethod(function reloadStylesheets() {
718
745
  let url = new RURL(sheet.href);
719
746
  url.param('last_reload', Date.now());
720
747
 
721
- //sheet.ownerNode.href = String(url);
722
-
723
748
  let new_element = alchemy.hawkejs.createElement('link');
724
749
  new_element.setAttribute('rel', 'stylesheet');
725
750
  new_element.setAttribute('href', String(url));
726
751
  document.head.append(new_element);
727
752
 
728
753
  new_element.addEventListener('load', e => {
729
- sheet.ownerNode.remove();
754
+ if (sheet.ownerNode) {
755
+ sheet.ownerNode.remove();
756
+ }
730
757
  });
731
758
  }
732
759
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alchemymvc",
3
3
  "description": "MVC framework for Node.js",
4
- "version": "1.3.6",
4
+ "version": "1.3.8",
5
5
  "author": "Jelle De Loecker <jelle@elevenways.be>",
6
6
  "keywords": [
7
7
  "alchemy",