radiant-fabulator_exhibit-extension 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);