radiant-fabulator_exhibit-extension 0.0.1 → 0.0.2

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.
@@ -0,0 +1,160 @@
1
+ /*
2
+ * (c) Copyright Texas A&M University 2010. All rights reserved.
3
+ *
4
+ * Portions of this code are copied from The SIMILE Project:
5
+ * (c) Copyright The SIMILE Project 2006. All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions
9
+ * are met:
10
+ *
11
+ * 1. Redistributions of source code must retain the above copyright
12
+ * notice, this list of conditions and the following disclaimer.
13
+ *
14
+ * 2. Redistributions in binary form must reproduce the above copyright
15
+ * notice, this list of conditions and the following disclaimer in the
16
+ * documentation and/or other materials provided with the distribution.
17
+ *
18
+ * 3. The name of the author may not be used to endorse or promote products
19
+ * derived from this software without specific prior written permission.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ *
32
+ */
33
+
34
+ // We can assume the Infusion is loaded
35
+
36
+ /*
37
+ * An Exhibit consists of the presentation and an overlay or slide-out
38
+ * with facets
39
+ */
40
+
41
+ /* We look for <div class="fabulator-exhibit" /> sections */
42
+
43
+ /*
44
+ * A view offers a filtered set of data from a data store
45
+ * A data store manages the actual data from the server
46
+ * An Exhibit interacts with the view to manage the data shown
47
+ */
48
+
49
+ Fabulator.namespace('Exhibit');
50
+
51
+ (function($, Exhibit) {
52
+
53
+ var initDataView = function(that) {
54
+ that.dataView = Exhibit.DataView({ source: that.options.source });
55
+ that.dataView.events.onModelChange.addListener(that.eventModelChange);
56
+ that.registerFilter = function(filter) {
57
+ that.dataView.registerFilter(filter);
58
+ };
59
+ };
60
+
61
+ var initPresentationViews = function(that) {
62
+ $(that.container).find('.views').each(function(idx, el) {
63
+ $(el).addClass("ui-corner-all");
64
+ that.presentation = Exhibit.Presentations(el, { viewPanel: that });
65
+ });
66
+ };
67
+
68
+ var initFacetView = function(that, myid) {
69
+ $(that.container).find('.facets').each(function(idx, el) {
70
+ $(el).attr('id', myid+'-facets-' + idx);
71
+ that.facets = Exhibit.Facets(el, { viewPanel: that, trigger: '#' + myid + '-open-facets' });
72
+ });
73
+ };
74
+
75
+ Exhibit.ViewPanel = function(container, options) {
76
+ var that = fluid.initView("Fabulator.Exhibit.ViewPanel", container, options),
77
+ lenses = new Array(),
78
+ header, counters,
79
+ myid = $(container).attr('id');
80
+
81
+ options = that.options;
82
+
83
+ options.source = options.source || $(container).attr("source");
84
+
85
+ that.eventModelChange = function(model) {
86
+
87
+ that.setCount(model.size());
88
+
89
+ if(that.presentation) {
90
+ that.presentation.eventModelChange(model);
91
+ }
92
+
93
+ };
94
+
95
+ lenses.push(Exhibit.DefaultLens());
96
+
97
+ that.getLens = function(item) {
98
+ for(i = 0, n = lenses.length; i < n; i++) {
99
+ if(lenses[i].isForItem(item)) {
100
+ return lenses[i];
101
+ }
102
+ }
103
+ }
104
+
105
+
106
+ header = "<span class='title'>" + $(container).attr("ex:exhibitLabel") + "</span>";
107
+
108
+ counters = $(container).attr("ex:counters");
109
+
110
+ if( counters != null) {
111
+ counters = counters.split(':');
112
+ header += "<span class='counter' id='" + myid + "-counter'>0 " + counters[1] + "</span>";
113
+ }
114
+
115
+ if( $(that.container).find('.facets').size() > 0 ) {
116
+ header += "<span class='ui-icon ui-icon-gear' id='"+myid+"-open-facets'>facets</span>";
117
+ }
118
+ $("<div class='header ui-corner-all'>" + header + "</div>").prependTo($(container));
119
+ //$("<div class='header ui-corner-all'><span class='title'>" + $(container).attr("ex:exhibitLabel") + "</span><span class='counter' id='" + myid + "-counter'>0 Items</span><span class='ui-icon ui-icon-gear' id='"+myid+"-open-facets'>facets</span></div>").prependTo($(container));
120
+
121
+ that.setCount = function(count) {
122
+ if( counters != null ) {
123
+ $('#' + myid + '-counter').text(count + " " + (count == 1 ? counters[0] : counters[1]));
124
+ }
125
+ }
126
+
127
+ initDataView(that);
128
+
129
+ initPresentationViews(that);
130
+
131
+ initFacetView(that, myid);
132
+
133
+ that.dataView.dataSource.fetchData();
134
+
135
+
136
+ return that;
137
+ };
138
+ })(jQuery, Fabulator.Exhibit);
139
+
140
+ fluid.defaults("Fabulator.Exhibit.ViewPanel", {
141
+ Presentations: {
142
+ type: "Fabulator.Exhibit.Presentations",
143
+ },
144
+ Facets: {
145
+ type: "Fabulator.Exhibit.Facets",
146
+ },
147
+ events: {
148
+ onDataChange: null
149
+ }
150
+ });
151
+
152
+ /* The following is what goes into the HTML through the xslt */
153
+ jQuery(document).ready(function($){
154
+ $(".fabulator-exhibit").each(function(idx, el) {
155
+ var options = {
156
+ };
157
+
158
+ Fabulator.Exhibit.ViewPanel('#' + $(el).attr('id'), options);
159
+ });
160
+ });
@@ -0,0 +1,1028 @@
1
+ /*
2
+ * (c) Copyright Texas A&M University 2010. All rights reserved.
3
+ *
4
+ * Portions of this code are copied from The SIMILE Project:
5
+ * (c) Copyright The SIMILE Project 2006. All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions
9
+ * are met:
10
+ *
11
+ * 1. Redistributions of source code must retain the above copyright
12
+ * notice, this list of conditions and the following disclaimer.
13
+ *
14
+ * 2. Redistributions in binary form must reproduce the above copyright
15
+ * notice, this list of conditions and the following disclaimer in the
16
+ * documentation and/or other materials provided with the distribution.
17
+ *
18
+ * 3. The name of the author may not be used to endorse or promote products
19
+ * derived from this software without specific prior written permission.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ *
32
+ */
33
+
34
+ (function($, Exhibit) {
35
+ Exhibit.Controls = {
36
+ "if": {
37
+ f: function(args, roots, rootValueTypes, defaultRootName, database) {
38
+ var conditionCollection = args[0].evaluate(roots, rootValueTypes, defaultRootName, database),
39
+ condition = false;
40
+ conditionCollection.forEachValue(function(v) {
41
+ if(v) {
42
+ condition = true;
43
+ return true;
44
+ }
45
+ });
46
+
47
+ if(condition) {
48
+ return args[1].evaluate(roots, rootValueTypes, defaultRootName, database);
49
+ }
50
+ else {
51
+ return args[2].evaluate(roots, rootValueTypes, defaultRootName, database);
52
+ }
53
+ }
54
+ },
55
+ "foreach": {
56
+ f: function(args, roots, rootValueTypes, defaultRootName, database) {
57
+ var collection = args[0].evaluate(roots, rootValueTypes, defaultRootName, database),
58
+ oldValue = roots["value"],
59
+ oldValueType = rootValueTypes["value"],
60
+ results = [ ],
61
+ valueType = "text",
62
+ collection2;
63
+
64
+ rootValueTypes["value"] = collection.valueType;
65
+
66
+ collection.forEachValue(function(element) {
67
+ roots["value"] = element;
68
+ collection2 = args[1].evaluate(roots, rootValueTypes, defaultRootName, database);
69
+ valueType = collection2.valueType;
70
+
71
+ collection2.forEachValue(function(result) {
72
+ results.push(result);
73
+ });
74
+ });
75
+
76
+ roots["value"] = oldValue;
77
+ rootValueTypes["value"] = oldValueType;
78
+
79
+ return Exhibit.Expression.Collection(results, valueType);
80
+ }
81
+ },
82
+ "default": {
83
+ f: function(args, roots, rootValueTypes, defaultRootName, database) {
84
+ var i, n, collection;
85
+ for(i = 0, n = args.length; i < n; i++) {
86
+ collection = args[i].evaluate(roots, rootValueTypes, defaultRootName, database);
87
+ if( collection.size() > 0 ) {
88
+ return collection;
89
+ }
90
+ }
91
+ return Exhibit.Expression.Collection([], "text");
92
+ }
93
+ }
94
+ };
95
+
96
+ Exhibit.Expression = function(rootNode) {
97
+ var that = { };
98
+
99
+ that.evaluate = function(
100
+ roots,
101
+ rootValueTypes,
102
+ defaultRootName,
103
+ database
104
+ ) {
105
+ var collection = rootNode.evaluate(roots, rootValueTypes, defaultRootName, database);
106
+ return {
107
+ values: collection.getSet(),
108
+ valueType: collection.valueType,
109
+ size: collection.size()
110
+ };
111
+ };
112
+
113
+ that.evaluateOneItem = function( itemID, database ) {
114
+ return this.evaluate(
115
+ { "value" : itemID },
116
+ { "value" : "item" },
117
+ "value",
118
+ database
119
+ );
120
+ };
121
+
122
+ that.evaluateSingle = function(
123
+ roots,
124
+ rootValueTypes,
125
+ defaultRootName,
126
+ database
127
+ ) {
128
+ var collection = rootNode.evaluate(roots, rootValueTypes, defaultRootName, database),
129
+ result = { value: null, valueType: collection.valueType };
130
+
131
+ collection.forEachValue(function(v) {
132
+ result.value = v;
133
+ return true;
134
+ });
135
+
136
+ return result;
137
+ };
138
+
139
+ that.isPath = rootNode.isPath;
140
+
141
+ that.getPath = that.isPath ?
142
+ function() { return rootNode; } :
143
+ function() { return null; } ;
144
+
145
+ that.testExists = that.isPath ?
146
+ function(
147
+ roots,
148
+ rootValueTypes,
149
+ defaultRootName,
150
+ database
151
+ ) {
152
+ return rootNode.testExists(roots, rootValueTypes, defaultRootName, database);
153
+ } :
154
+ function(
155
+ roots,
156
+ rootValueTypes,
157
+ defaultRootName,
158
+ database
159
+ ) {
160
+ return that.evaluate(roots, rootValueTypes, defaultRootName, database).values.size() > 0;
161
+ };
162
+
163
+ that.evaluateBackward = function(
164
+ value,
165
+ valueType,
166
+ filter,
167
+ database
168
+ ) {
169
+ return rootNode.walkBackward([ value ], valueType, filter, database);
170
+ };
171
+
172
+ that.walkForward = function(
173
+ values,
174
+ valueType,
175
+ database
176
+ ) {
177
+ return rootNode.walkForward(values, valueType, database);
178
+ };
179
+
180
+ that.walkBackward = function(
181
+ values,
182
+ valueType,
183
+ filter,
184
+ database
185
+ ) {
186
+ return rootNode.walkBackward(values, valueType, filter, database);
187
+ };
188
+
189
+ return that;
190
+ };
191
+
192
+ Exhibit.Expression.Collection = function(values, valueType) {
193
+ var that = { };
194
+
195
+ if( values instanceof Array ) {
196
+
197
+ that.forEachValue = function(f) {
198
+ var a = values,
199
+ i, n;
200
+
201
+ for(i = 0, n = a.length; i < n; i++) {
202
+ if( f(a[i]) === true ) {
203
+ break;
204
+ }
205
+ }
206
+ };
207
+
208
+ that.getSet = function() {
209
+ return Exhibit.Set(values);
210
+ };
211
+
212
+ that.contains = function(v) {
213
+ var a = values,
214
+ i, n;
215
+
216
+ for(i = 0, n = a.length; i < n; i++) {
217
+ if( a[i] == v ) { return true; }
218
+ }
219
+ return false;
220
+ };
221
+
222
+ that.size = function() { values.length; };
223
+
224
+ }
225
+ else {
226
+
227
+ that.forEachValue = function(f) {
228
+ values.visit(f);
229
+ };
230
+
231
+ that.getSet = function() {
232
+ return values;
233
+ };
234
+
235
+ that.contains = function(v) {
236
+ return values.contains(v);
237
+ };
238
+
239
+ that.size = values.size;
240
+
241
+ }
242
+
243
+ that.isPath = false;
244
+
245
+ return that;
246
+ };
247
+
248
+ Exhibit.Expression.Constant = function(value, valueType) {
249
+ var that = { };
250
+
251
+ that.evaluate = function(
252
+ roots,
253
+ rootValueTypes,
254
+ defaultRootName,
255
+ database
256
+ ) {
257
+ return Exhibit.Expression.Collection([ value ], valueType);
258
+ };
259
+
260
+ that.isPath = false;
261
+
262
+ return that;
263
+ };
264
+
265
+ var _operators = {
266
+ "+" : {
267
+ argumentType: "number",
268
+ valueType: "number",
269
+ f: function(a,b) { return a+b; }
270
+ },
271
+ "-" : {
272
+ argumentType: "number",
273
+ valueType: "number",
274
+ f: function(a,b) { return a-b; }
275
+ },
276
+ "*" : {
277
+ argumentType: "number",
278
+ valueType: "number",
279
+ f: function(a,b) { return a*b; }
280
+ },
281
+ "/" : {
282
+ argumentType: "number",
283
+ valueType: "number",
284
+ f: function(a,b) { return a/b; }
285
+ },
286
+ "=" : {
287
+ valueType: "boolean",
288
+ f: function(a,b) { return a == b; }
289
+ },
290
+ "<>" : {
291
+ valueType: "boolean",
292
+ f: function(a,b) { return a != b; }
293
+ },
294
+ "><" : {
295
+ valueType: "boolean",
296
+ f: function(a,b) { return a != b; }
297
+ },
298
+ "<" : {
299
+ valueType: "boolean",
300
+ f: function(a,b) { return a < b; }
301
+ },
302
+ ">" : {
303
+ valueType: "boolean",
304
+ f: function(a,b) { return a > b; }
305
+ },
306
+ "<=" : {
307
+ valueType: "boolean",
308
+ f: function(a,b) { return a <= b; }
309
+ },
310
+ ">=" : {
311
+ valueType: "boolean",
312
+ f: function(a,b) { return a >= b; }
313
+ }
314
+ };
315
+
316
+ Exhibit.Expression.Operator = function(operator, args) {
317
+ var that = { },
318
+ _operator = operator,
319
+ _args = args;
320
+
321
+ that.evaluate = function(
322
+ roots,
323
+ rootValueTypes,
324
+ defaultRootName,
325
+ database
326
+ ) {
327
+ var values = [ ],
328
+ args = [],
329
+ i, n, operator, f;
330
+
331
+ for(i = 0, n = _args.length; i < n; i++) {
332
+ args.push(_args[i].evaluate(roots, rootValueTypes, defaultRootName, database));
333
+ }
334
+
335
+ operator = _operators[_operator];
336
+ f = operator.f;
337
+ if(operator.argumentType == "number") {
338
+ args[0].forEachValue(function(v1) {
339
+ if( !(typeof(v1) == "number") ) {
340
+ v1 = parseFloat(v1);
341
+ }
342
+
343
+ args[1].forEachValue(function(v2) {
344
+ if( !(typeof(v2) == "number")) {
345
+ v2 = parseFloat(v2);
346
+ }
347
+
348
+ values.push(f(v1, v2));
349
+ });
350
+ });
351
+ }
352
+ else {
353
+ args[0].forEachValue(function(v1) {
354
+ args[1].forEachValue(function(v2) {
355
+ values.push(f(v1, v2));
356
+ });
357
+ });
358
+ }
359
+
360
+ return Exhibit.Expression.Collection(values, operator.valueType);
361
+ };
362
+
363
+ that.isPath = false;
364
+
365
+ return that;
366
+ };
367
+
368
+ Exhibit.Expression.FunctionCall = function(name, args) {
369
+ var that = { },
370
+ _name = name,
371
+ _args = args;
372
+
373
+ that.evaluate = function(
374
+ roots,
375
+ rootValueTypes,
376
+ defaultRootName,
377
+ database
378
+ ) {
379
+ var args = [],
380
+ i, n;
381
+
382
+ for(i = 0, n = _args.length; i < n; i++) {
383
+ args.push(_args[i].evaluate(roots, rootValueTypes, defaultRootName, database));
384
+ }
385
+
386
+ if(_name in Exhibit.Functions) {
387
+ return Exhibit.Functions[_name].f(args);
388
+ }
389
+ else {
390
+ throw new Error("No such function named " + _name);
391
+ }
392
+ };
393
+
394
+ that.isPath = false;
395
+
396
+ return that;
397
+ };
398
+
399
+ Exhibit.Expression.ControlCall = function(name, args) {
400
+ var that = { },
401
+ _name = name,
402
+ _args = args;
403
+
404
+ that.evaluate = function(
405
+ roots,
406
+ rootValueTypes,
407
+ defaultRootName,
408
+ database
409
+ ) {
410
+ return Exhibit.Controls[_name].f(_args, roots, rootValueTypes, defaultRootName, database);
411
+ };
412
+
413
+ that.isPath = false;
414
+
415
+ return that;
416
+ };
417
+
418
+ Exhibit.Expression.Path = function(property, forward) {
419
+ var that = { },
420
+ _rootName = null,
421
+ _segments = [ ];
422
+
423
+ if( typeof(property) != "undefined" ) {
424
+ _segments.push({ property: property, forward: forward, isArray: false });
425
+ }
426
+
427
+ that.isPath = true;
428
+
429
+ that.setRootName = function(rootName) {
430
+ _rootName = rootName;
431
+ };
432
+
433
+ that.appendSegment = function(property, hopOperator) {
434
+ _segments.push({
435
+ property: property,
436
+ forward: hopOperator.charAt(0) == ".",
437
+ isArray: hopOperator.length > 1
438
+ });
439
+ };
440
+
441
+ that.getSegment = function(index) {
442
+ var segment;
443
+
444
+ if( index < _segments.length ) {
445
+ segment = _segments[index];
446
+ return {
447
+ property: segment.property,
448
+ forward: segment.forward,
449
+ isArray: segment.isArray
450
+ };
451
+ }
452
+ else {
453
+ return null;
454
+ }
455
+ };
456
+
457
+ that.getLastSegment = function() {
458
+ return that.getSegment(_segments.length - 1);
459
+ };
460
+
461
+ that.getSegmentCount = function() { return _segments.length };
462
+
463
+ var walkForward = function(collection, database) {
464
+ var i, n, segment, a, valueType, property, values;
465
+
466
+ for(i = 0, n = _segments.length; i < n; i++) {
467
+ segment = _segments[i];
468
+ if(segment.isArray) {
469
+ a = [ ];
470
+ if( segment.forward ) {
471
+ collection.forEachValue(function(v) {
472
+ database.getObjects(v, segment.property).visit(function(v2) { a.push(v2); });
473
+ });
474
+
475
+ property = database.getProperty(segment.property);
476
+ valueType = property != null ? property.getValueType() : "text";
477
+ }
478
+ else {
479
+ collection.forEachValue(function(v) {
480
+ database.getSubjects(v, segment.property).visit(function(v2) { a.push(v2); });
481
+ });
482
+ valueType = "item";
483
+ }
484
+ collection = Exhibit.Expression.Collection(a, valueType);
485
+ }
486
+ else {
487
+ if( segment.forward ) {
488
+ values = database.getObjectsUnion(collection.getSet(), segment.property);
489
+ property = database.getProperty(segment.property);
490
+ valueType = property != null ? property.getValueType() : "text";
491
+ collection = Exhibit.Expression.Collection(values, valueType);
492
+ }
493
+ else {
494
+ values = database.getSubjectsUnion(collection.getSet(), segment.property);
495
+ collection = Exhibit.Expression.Collection(values, "item");
496
+ }
497
+ }
498
+ }
499
+
500
+ return collection;
501
+ };
502
+
503
+ var walkBackward = function(collection, filter, database) {
504
+ var i, segment, a, valueType, property, values;
505
+
506
+ if(filter instanceof Array) {
507
+ filter = Exhibit.Set(filter);
508
+ }
509
+ for(i = _segments.length - 1; i >= 0; i--) {
510
+ segment = _segments[i];
511
+ if(segment.isArray) {
512
+ a = [];
513
+ if( segment.forward ) {
514
+ collection.forEachValue(function(v) {
515
+ database.getSubjects(v, segment.property).visit(function(v2) {
516
+ if( i > 0 || filter == null || filter.contains(v2) ) {
517
+ a.push(v2);
518
+ }
519
+ });
520
+ });
521
+
522
+ property = database.getProperty(segment.property);
523
+ valueType = property != null ? property.getValueType() : "text";
524
+ }
525
+ else {
526
+ collection.forEachValue(function(v) {
527
+ database.getObjects(v, segment.property).visit(function(v2) {
528
+ if( i > 0 || filter == null || filter.contains(v2) ) {
529
+ a.push(v2);
530
+ }
531
+ });
532
+ });
533
+ valueType = "item";
534
+ }
535
+ collection = Exhibit.Expression.Collection(a, valueType);
536
+ }
537
+ else {
538
+ if( segment.forward ) {
539
+ values = database.getSubjectsUnion(collection.getSet(), segment.property, null, i == 0 ? filter : null);
540
+ collection = Exhibit.Expression.Collection(values, "item");
541
+ }
542
+ else {
543
+ values = database.getObjectsUnion(collection.getSet(), segment.property, null, i == 0 ? filter : null);
544
+ property = database.getProperty(segment.property);
545
+ valueType = property != null ? property.getValueType() : "text";
546
+ collection = Exhibit.Expression.Collection(values, valueType);
547
+ }
548
+ }
549
+ }
550
+
551
+ return collection;
552
+ };
553
+
554
+ that.rangeBackward = function(
555
+ from,
556
+ to,
557
+ filter,
558
+ database
559
+ ) {
560
+ var set = Exhibit.Set(),
561
+ valueType = "item",
562
+ segment, i;
563
+
564
+ if(_segments.length > 0) {
565
+ segment = _segments[_segments.length - 1];
566
+ if(segment.forward) {
567
+ database.getSubjectsInRange(segment.property, from, to, false, set, _segments.length == 1 ? filter : null);
568
+ }
569
+ else {
570
+ throw new Error("Last path of segment must be forward");
571
+ }
572
+
573
+ for(i = _segments.length-2; i >= 0; i--) {
574
+ segment = _segments[i];
575
+ if( segment.forward ) {
576
+ set = database.getSubjectsUnion(set, segment.property, null, i == 0 ? filter : null);
577
+ valueType = "item";
578
+ }
579
+ else {
580
+ set = database.getObjectsUnion(set, segment.property, null, i == 0 ? filter : null);
581
+ property = database.getProperty(segment.property);
582
+ valueType = property != null ? property.getValueType() : "text";
583
+ }
584
+ }
585
+ }
586
+
587
+ return {
588
+ valueType: valueType,
589
+ values: set,
590
+ count: set.size()
591
+ };
592
+ };
593
+
594
+ that.evaluate = function(
595
+ roots,
596
+ rootValueTypes,
597
+ defaultRootName,
598
+ database
599
+ ) {
600
+ var rootName = _rootName != null ? _rootName : defaultRootName,
601
+ valueType = rootName in rootValueTypes ? rootValueTypes[rootName] : "text",
602
+ collection = null,
603
+ root;
604
+
605
+ if( rootName in roots ) {
606
+ root = roots[rootName];
607
+
608
+ if( root.isSet || root instanceof Array) {
609
+ collection = Exhibit.Expression.Collection(root, valueType);
610
+ }
611
+ else {
612
+ collection = Exhibit.Expression.Collection([ root ], valueType);
613
+ }
614
+
615
+ return walkForward(collection, database);
616
+ }
617
+ else {
618
+ throw new Error("No such variable called " + rootName);
619
+ }
620
+ };
621
+
622
+ that.testExists = function(
623
+ roots,
624
+ rootValueTypes,
625
+ defaultRootName,
626
+ database
627
+ ) {
628
+ return that.evaluate(roots, rootValueTypes, defaultRootName, database).size() > 0;
629
+ };
630
+
631
+ that.evaluateBackward = function(
632
+ value,
633
+ valueType,
634
+ filter,
635
+ database
636
+ ) {
637
+ var collection = Exhibit.Expression.Collection([ value ], valueType);
638
+ return walkBackward(collection, filter, database);
639
+ };
640
+
641
+ that.walkForward = function(
642
+ values,
643
+ valueType,
644
+ database
645
+ ) {
646
+ return walkForward(Exhibit.Expression.Collection(values, valueType), database);
647
+ };
648
+
649
+ that.walkBackward = function(
650
+ values,
651
+ valueType,
652
+ filter,
653
+ database
654
+ ) {
655
+ return walkBackward(Exhibit.Expression.Collection(values, valueType), filter, database);
656
+ };
657
+
658
+ return that;
659
+ };
660
+
661
+ Exhibit.ExpressionParser = function() {
662
+ var that = { };
663
+
664
+ var internalParse = function(scanner, several) {
665
+ var token = scanner.token(),
666
+ roots, expressions, r, n,
667
+ Scanner = Exhibit.ExpressionScanner,
668
+ next = function() { scanner.next(); token = scanner.token(); },
669
+ makePosition = function() { return token != null ? token.start : scanner.index(); };
670
+
671
+ var parsePath = function() {
672
+ var path = Exhibit.Expression.Path(),
673
+ hopOperator;
674
+ while( token != null && token.type == Scanner.PATH_OPERATOR ) {
675
+ hopOperator = token.value;
676
+ next();
677
+
678
+ if( token != null && token.type == Scanner.IDENTIFIER ) {
679
+ path.appendSegment(token.value, hopOperator);
680
+ next();
681
+ }
682
+ else {
683
+ throw new Error("Missing property ID at position " + makePosition());
684
+ }
685
+ }
686
+ return path;
687
+ };
688
+
689
+ var parseFactor = function() {
690
+ var result = null,
691
+ identifier;
692
+
693
+ if( token == null ) {
694
+ throw new Error("Missing factor at end of expression");
695
+ }
696
+
697
+ switch(token.type) {
698
+ case Scanner.NUMBER:
699
+ result = Exhibit.Expression.Constant(token.value, "number");
700
+ next();
701
+ break;
702
+ case Scanner.STRING:
703
+ result = Exhibit.Expression.Constant(token.value, "text");
704
+ next();
705
+ break;
706
+ case Scanner.PATH_OPERATOR:
707
+ result = parsePath();
708
+ break;
709
+ case Scanner.IDENTIFIER:
710
+ identifier = token.value;
711
+ next();
712
+
713
+ if( identifier in Exhibit.Controls ) {
714
+ if( token != null && token.type == Scanner.DELIMITER && token.value == "(") {
715
+ next();
716
+
717
+ args = (token != null && token.type == Scanner.DELIMITER && token.value == ")") ?
718
+ [] : parseExpressionList();
719
+ result = Exhibit.Expression.ControlCall(identifier, args);
720
+
721
+ if( token != null && token.type == Scanner.DELIMITER && token.value == ")") {
722
+ next();
723
+ }
724
+ else {
725
+ throw new Error("Missing ) to end " + identifier + " at position " + makePosition());
726
+ }
727
+ }
728
+ else {
729
+ throw new Error("Missing ( to start " + identifier + " at position " + makePosition());
730
+ }
731
+ }
732
+ else {
733
+ if( token != null && token.type == Scanner.DELIMITER && token.value == "(") {
734
+ next();
735
+
736
+ args = (token != null && token.type == Scanner.DELIMITER && token.value == ")") ?
737
+ [] : parseExpressionList();
738
+ result = Exhibit.Expression.FunctionCall(identifier, args);
739
+
740
+ if( token != null && token.type == Scanner.DELIMITER && token.value == ")") {
741
+ next();
742
+ }
743
+ else {
744
+ throw new Error("Missing ) after function call " + identifier + " at position " + makePosition());
745
+ }
746
+ }
747
+ else {
748
+ result = parsePath();
749
+ result.setRootName(identifier);
750
+ }
751
+ }
752
+ break;
753
+ case Scanner.DELIMITER:
754
+ if( token.value == "(" ) {
755
+ next();
756
+
757
+ result = parseExpression();
758
+ if( token != null && token.type == Scanner.DELIMITER && token.value == ")") {
759
+ next();
760
+ break;
761
+ }
762
+ else {
763
+ throw new Error("Missing ) at position " + makePosition());
764
+ }
765
+ } // else, fall through
766
+ default:
767
+ throw new Error("Unexpected text " + token.value + " at position " + makePosition());
768
+ }
769
+
770
+ return result;
771
+ };
772
+
773
+ var parseTerm = function() {
774
+ var term = parseFactor(),
775
+ operator;
776
+
777
+ while( token != null && token.type == Scanner.OPERATOR &&
778
+ ( token.value == "*" || token.value == "/" )) {
779
+ operator = token.value;
780
+ next();
781
+
782
+ term = Exhibit.Expression.Operator(operator, [ term, parseFactor() ]);
783
+ }
784
+ return term;
785
+ };
786
+
787
+ var parseSubExpression = function() {
788
+ var subExpression = parseTerm(),
789
+ operator;
790
+
791
+ while( token != null && token.type == Scanner.OPERATOR &&
792
+ ( token.value == "+" || token.value == "-" )) {
793
+ operator = token.value;
794
+ next();
795
+
796
+ subExpression = Exhibit.Expression.Operator(operator, [ subExpression, parseTerm() ]);
797
+ }
798
+ return subExpression;
799
+ };
800
+
801
+ var parseExpression = function() {
802
+ var expression = parseSubExpression(),
803
+ operator;
804
+
805
+ while( token != null && token.type == Scanner.OPERATOR &&
806
+ ( token.value == "=" || token.value == "<>" ||
807
+ token.value == "<" || token.value == "<=" ||
808
+ token.value == ">" || token.value == ">=" )) {
809
+
810
+ operator = token.value;
811
+ next();
812
+
813
+ expression = Exhibit.Expression.Operator(operator, [ expression, parseSubExpression ]);
814
+ }
815
+ return expression;
816
+ };
817
+
818
+ var parseExpressionList = function() {
819
+ var expressions = [ parseExpression() ];
820
+ while( token != null && token.type == Scanner.DELIMITER && token.value == ",") {
821
+ next();
822
+ expressions.push(parseExpression());
823
+ }
824
+ return expressions;
825
+ };
826
+
827
+ if(several) {
828
+ roots = parseExpressionList();
829
+ expressions = [ ];
830
+ for(r = 0, n = roots.length; r < n; r++) {
831
+ expressions.push(Exhibit.Expression(roots[r]));
832
+ }
833
+ return expressions;
834
+ }
835
+ else {
836
+ return Exhibit.Expression(parseExpression());
837
+ }
838
+ };
839
+
840
+ that.parse = function(s, startIndex, results) {
841
+ var scanner;
842
+
843
+ startIndex = startIndex || 0;
844
+ results = results || { };
845
+
846
+ scanner = Exhibit.ExpressionScanner(s, startIndex);
847
+ try {
848
+ return internalParse(scanner, false);
849
+ }
850
+ finally {
851
+ results.index = scanner.token() != null ? scanner.token().start : scanner.index();
852
+ }
853
+ };
854
+
855
+
856
+ return that;
857
+ };
858
+
859
+ Exhibit.ExpressionScanner = function(text, startIndex) {
860
+ var that = { },
861
+ _text = text + " ",
862
+ _maxIndex = text.length,
863
+ _index = startIndex,
864
+ _token = null;
865
+
866
+
867
+
868
+ that.token = function() { return _token; }
869
+
870
+ that.index = function() { return _index; }
871
+
872
+ var isDigit = function(c) {
873
+ return "0123456789".indexOf(c) >= 0;
874
+ };
875
+
876
+ that.next = function() {
877
+ var c1, c2, i, c;
878
+
879
+ _token = null;
880
+
881
+ while(_index < _maxIndex &&
882
+ " \t\r\n".indexOf(_text.charAt(_index)) >= 0) {
883
+ _index++;
884
+ };
885
+
886
+ if( _index < _maxIndex ) {
887
+ c1 = _text.charAt(_index);
888
+ c2 = _text.charAt(_index + 1);
889
+
890
+ if( ".!".indexOf(c1) >= 0 ) {
891
+ if( c2 == "@" ) {
892
+ _token = {
893
+ type: Exhibit.ExpressionScanner.PATH_OPERATOR,
894
+ value: c1+c2,
895
+ start: _index,
896
+ end: _index+2
897
+ };
898
+ _index += 2;
899
+ }
900
+ else {
901
+ _token = {
902
+ type: Exhibit.ExpressionScanner.PATH_OPERATOR,
903
+ value: c1,
904
+ start: _index,
905
+ end: _index+1
906
+ };
907
+ _index += 1;
908
+ }
909
+ }
910
+ else if( "<>".indexOf(c1) >= 0) {
911
+ if((c2 == "=") || ("<>".indexOf(c2) >= 0 && c1 != c2)) {
912
+ _token = {
913
+ type: Exhibit.ExpressionScanner.OPERATOR,
914
+ value: c1 + c2,
915
+ start: _index,
916
+ end: _index + 2
917
+ };
918
+ _index += 2;
919
+ }
920
+ else {
921
+ _token = {
922
+ type: Exhibit.ExpressionScanner.OPERATOR,
923
+ value: c1,
924
+ start: _index,
925
+ end: _index+1
926
+ };
927
+ _index += 1;
928
+ }
929
+ }
930
+ else if( "+-*/=".indexOf(c1) >= 0) {
931
+ _token = {
932
+ type: Exhibit.ExpressionScanner.OPERATOR,
933
+ value: c1,
934
+ start: _index,
935
+ end: _index+1
936
+ };
937
+ _index += 1;
938
+ }
939
+ else if( "()".indexOf(c1) >= 0) {
940
+ _token = {
941
+ type: Exhibit.ExpressionScanner.DELIMITER,
942
+ value: c1,
943
+ start: _index,
944
+ end: _index+1
945
+ };
946
+ _index += 1;
947
+ }
948
+ else if( "\"'".indexOf(c1) >= 0) { // quoted strings
949
+ i = _index + 1;
950
+ while( i < _maxIndex ) {
951
+ if( _text.charAt(i) == c1 && _text.charAt(i-1) != "\\") {
952
+ break;
953
+ }
954
+ i += 1;
955
+ }
956
+
957
+ if(i < _maxIndex) {
958
+ _token = {
959
+ type: Exhibit.ExpressionScanner.STRING,
960
+ value: _text.substring(_index+1, i).replace(/\\'/g, "'").replace(/\\"/g, '"'),
961
+ start: _index,
962
+ end: i+1
963
+ };
964
+ _index = i + 1;
965
+ }
966
+ else {
967
+ throw new Error("Unterminated string starting at " + _index);
968
+ }
969
+ }
970
+ else if(isDigit(c1)) { // number
971
+ i = _index;
972
+ while( i < _maxIndex && isDigit(_text.charAt(i))) {
973
+ i += 1;
974
+ }
975
+
976
+ if( i < _maxIndex && _text.charAt(i) == "." ) {
977
+ i += 1;
978
+ while( i < _maxIndex && isDigit(_text.charAt(i))) {
979
+ i += 1;
980
+ }
981
+ }
982
+
983
+ _token = {
984
+ type: Exhibit.ExpressionScanner.NUMBER,
985
+ value: parseFloat(_text.substring(_index, i)),
986
+ start: _index,
987
+ end: i
988
+ };
989
+
990
+ _index = i;
991
+ }
992
+ else { // identifier
993
+ i = _index;
994
+
995
+ while( i < _maxIndex ) {
996
+ c = _text.charAt(i);
997
+ if( "(),.!@ \t".indexOf(c) < 0 ) {
998
+ i += 1;
999
+ }
1000
+ else {
1001
+ break;
1002
+ }
1003
+ }
1004
+
1005
+ _token = {
1006
+ type: Exhibit.ExpressionScanner.IDENTIFIER,
1007
+ value: _text.substring(_index, i),
1008
+ start: _index,
1009
+ end: i
1010
+ };
1011
+ _index = i;
1012
+ }
1013
+ }
1014
+ };
1015
+
1016
+ that.next();
1017
+
1018
+ return that;
1019
+ };
1020
+
1021
+ Exhibit.ExpressionScanner.DELIMITER = 0;
1022
+ Exhibit.ExpressionScanner.NUMBER = 1;
1023
+ Exhibit.ExpressionScanner.STRING = 2;
1024
+ Exhibit.ExpressionScanner.IDENTIFIER = 3;
1025
+ Exhibit.ExpressionScanner.OPERATOR = 4;
1026
+ Exhibit.ExpressionScanner.PATH_OPERATOR = 5;
1027
+
1028
+ })(jQuery, Fabulator.Exhibit);