radiant-fabulator_exhibit-extension 0.0.3 → 0.0.4
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.
- data/History.txt +9 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/app/models/fabulator_exhibit.rb +8 -0
- data/app/models/fabulator_exhibit_item.rb +9 -0
- data/app/models/fabulator_exhibit_property.rb +8 -0
- data/app/models/fabulator_exhibit_type.rb +8 -0
- data/db/migrate/002_create_fabulator_exhibit_content_tables.rb +69 -0
- data/fabulator_exhibit_extension.rb +13 -28
- data/lib/fabulator_exhibit_extension/database.rb +274 -0
- data/public/javascripts/fabulator/exhibit/data.js +88 -10
- data/public/javascripts/fabulator/exhibit/exhibit.js +2 -1
- data/public/javascripts/fabulator/exhibit/expressions.js +1 -1
- data/public/javascripts/fabulator/exhibit/facets.js +7 -157
- data/public/javascripts/fabulator/exhibit/util.js +473 -0
- data/public/javascripts/fabulator/exhibit/views.js +184 -14
- metadata +13 -7
@@ -104,7 +104,87 @@ Fabulator.namespace('Exhibit');
|
|
104
104
|
return div.firstChild;
|
105
105
|
};
|
106
106
|
|
107
|
-
var
|
107
|
+
var itemList = function(view, parentElmt, templateNode, list) {
|
108
|
+
var separator = ", ",
|
109
|
+
last_separator = ", and ",
|
110
|
+
pair_separator = " and ",
|
111
|
+
values, value, lens,
|
112
|
+
valueType, lensElmt, lensRender,
|
113
|
+
i, n;
|
114
|
+
|
115
|
+
if( "separator" in templateNode ) {
|
116
|
+
separator = templateNode.separator;
|
117
|
+
}
|
118
|
+
if( "last_separator" in templateNode ) {
|
119
|
+
last_separator = templateNode.last_separator;
|
120
|
+
}
|
121
|
+
if( "pair_separator" in templateNode ) {
|
122
|
+
pair_separator = templateNode.pair_separator;
|
123
|
+
}
|
124
|
+
|
125
|
+
if(separator && separator.substr(0,1) != "<") {
|
126
|
+
separator = "<span>" + separator + "</span>";
|
127
|
+
}
|
128
|
+
if(last_separator && last_separator.substr(0,1) != "<") {
|
129
|
+
last_separator = "<span>" + last_separator + "</span>";
|
130
|
+
}
|
131
|
+
if(pair_separator && pair_separator.substr(0,1) != "<") {
|
132
|
+
pair_separator = "<span>" + pair_separator + "</span>";
|
133
|
+
}
|
134
|
+
|
135
|
+
valueType = list.valueType || "text";
|
136
|
+
values = list.values.items();
|
137
|
+
|
138
|
+
$(parentElmt).empty();
|
139
|
+
|
140
|
+
/* we want a popup that will show the lens for this item type */
|
141
|
+
for( i = 0, n = values.length; i < n; i += 1 ) {
|
142
|
+
if( valueType == 'item' ) {
|
143
|
+
value = that.getItem(values[i]);
|
144
|
+
lens = view.getLens(value);
|
145
|
+
if( lens == null ) {
|
146
|
+
value = value.label[0];
|
147
|
+
/* we should escape this value so it's just text */
|
148
|
+
$(value).appendTo($(parentElmt));
|
149
|
+
}
|
150
|
+
else {
|
151
|
+
/* construct a clickable link that will pop up the lens content */
|
152
|
+
lensElmt = $("<div></div>");
|
153
|
+
lensRender = lens.render(view, model, values[i]);
|
154
|
+
/* TODO: make id more universally unique */
|
155
|
+
$(lensRender).attr('id', 'facet-item-lens-' + values[i]);
|
156
|
+
$(lensRender).addClass('facets-overlay');
|
157
|
+
$("<a class='close ui-icon ui-icon-circle-close'></a>").prependTo($(lensRender));
|
158
|
+
$(lensRender).addClass('ui-corner-all');
|
159
|
+
trigger = $("<span rel='#facet-item-lens-'" + values[i] + "'>" + value.label[0] + "</span>")
|
160
|
+
trigger.appendTo(lensElmt);
|
161
|
+
$(lensRender).appendTo(lensElmt);
|
162
|
+
lensElmt.appendTo($(parentElmt));
|
163
|
+
$(lensRender).hide();
|
164
|
+
trigger.overlay();
|
165
|
+
}
|
166
|
+
}
|
167
|
+
else {
|
168
|
+
$("<span>" + values[i] + "</span>").appendTo($(parentElmt));
|
169
|
+
}
|
170
|
+
|
171
|
+
if( n > 1 ) {
|
172
|
+
if( i == n-2 ) {
|
173
|
+
if( n > 2 ) {
|
174
|
+
$(last_separator).appendTo($(parentElmt));
|
175
|
+
}
|
176
|
+
else {
|
177
|
+
$(pair_separator).appendTo($(parentElmt));
|
178
|
+
}
|
179
|
+
}
|
180
|
+
else if( i < n-1 ){
|
181
|
+
$(separator).appendTo($(parentElmt));
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
};
|
186
|
+
|
187
|
+
var createDOMFromTemplate = function(view, rootID, templateNode, result, parentElmt) {
|
108
188
|
var node, elmt, tag, attribute, value, v, n, i, items;
|
109
189
|
|
110
190
|
if(templateNode == null) {
|
@@ -199,12 +279,10 @@ Fabulator.namespace('Exhibit');
|
|
199
279
|
}
|
200
280
|
else if( attribute == "content" ) {
|
201
281
|
if( value != null ) {
|
202
|
-
items = value.
|
203
|
-
if( items.values.size() >
|
282
|
+
items = value.evaluateOnItem(rootID, that.dataSource);
|
283
|
+
if( items.values.size() > 0 ) {
|
204
284
|
// we have a list of items
|
205
|
-
|
206
|
-
else if( items.values.size() == 1 ) {
|
207
|
-
elmt.innerText = (items.values.items())[0];
|
285
|
+
itemList(view, elmt, templateNode, items);
|
208
286
|
}
|
209
287
|
}
|
210
288
|
}
|
@@ -219,7 +297,7 @@ Fabulator.namespace('Exhibit');
|
|
219
297
|
setting += e;
|
220
298
|
}
|
221
299
|
else {
|
222
|
-
r = e.
|
300
|
+
r = e.evaluateOnItem(rootID, that.dataSource);
|
223
301
|
setting += (r.values.items()).join("");
|
224
302
|
}
|
225
303
|
});
|
@@ -230,7 +308,7 @@ Fabulator.namespace('Exhibit');
|
|
230
308
|
else if( attribute == "children" ) {
|
231
309
|
if( value != null ) {
|
232
310
|
for(i = 0, n = value.length; i < n; i++) {
|
233
|
-
createDOMFromTemplate(rootID, value[i], result, elmt);
|
311
|
+
createDOMFromTemplate(view, rootID, value[i], result, elmt);
|
234
312
|
}
|
235
313
|
}
|
236
314
|
}
|
@@ -242,9 +320,9 @@ Fabulator.namespace('Exhibit');
|
|
242
320
|
}
|
243
321
|
};
|
244
322
|
|
245
|
-
that.renderTemplate = function(rootID, template) {
|
323
|
+
that.renderTemplate = function(view, rootID, template) {
|
246
324
|
var result = {};
|
247
|
-
result.elmt = createDOMFromTemplate(rootID, template, result, null);
|
325
|
+
result.elmt = createDOMFromTemplate(view, rootID, template, result, null);
|
248
326
|
|
249
327
|
return result;
|
250
328
|
};
|
@@ -155,6 +155,7 @@ jQuery(document).ready(function($){
|
|
155
155
|
var options = {
|
156
156
|
};
|
157
157
|
|
158
|
-
Fabulator.Exhibit.ViewPanel('#' + $(el).attr('id'), options);
|
158
|
+
//Fabulator.Exhibit.ViewPanel('#' + $(el).attr('id'), options);
|
159
|
+
Fabulator.Exhibit.ViewPanel(el, options);
|
159
160
|
});
|
160
161
|
});
|
@@ -156,159 +156,9 @@ Fabulator.namespace('Exhibit');
|
|
156
156
|
return that;
|
157
157
|
};
|
158
158
|
|
159
|
-
Exhibit.Facets.
|
159
|
+
Exhibit.Facets.initView = function(type, container, options) {
|
160
160
|
var that = fluid.initView("Fabulator.Exhibit.Facets." + type, container, options);
|
161
161
|
|
162
|
-
var parseSetting = function(s, type, spec) {
|
163
|
-
var sType = typeof s, f, i, n, choices;
|
164
|
-
|
165
|
-
if(type == "text") {
|
166
|
-
return s;
|
167
|
-
}
|
168
|
-
else if( type == "float" ) {
|
169
|
-
if( sType == "number" ) {
|
170
|
-
return s;
|
171
|
-
}
|
172
|
-
else if( sType == "string" ) {
|
173
|
-
f = parseFloat(s);
|
174
|
-
if( !isNaN(f) ) {
|
175
|
-
return f;
|
176
|
-
}
|
177
|
-
}
|
178
|
-
throw new Error("Expected a floating point number but got " + s);
|
179
|
-
}
|
180
|
-
else if( type == "int" ) {
|
181
|
-
if( sType == "number" ) {
|
182
|
-
return Math.round(s);
|
183
|
-
}
|
184
|
-
else if( sType == "string" ) {
|
185
|
-
n = parseInt(s);
|
186
|
-
if( !isNaN(n) ) {
|
187
|
-
return n;
|
188
|
-
}
|
189
|
-
}
|
190
|
-
throw new Error("Expected an integer but got " + s);
|
191
|
-
}
|
192
|
-
else if(type == "boolean" ) {
|
193
|
-
if( sType == "boolean" ) {
|
194
|
-
return s;
|
195
|
-
}
|
196
|
-
else if( sType == "string" ) {
|
197
|
-
s = s.toLowerCase();
|
198
|
-
if( s == "true" || s == "on" || s == "yes" ) {
|
199
|
-
return true;
|
200
|
-
}
|
201
|
-
else if( s == "false" || s == "off" || s == "no" ) {
|
202
|
-
return false;
|
203
|
-
}
|
204
|
-
}
|
205
|
-
throw new Error("Expected either 'true' or 'false' but got " + s);
|
206
|
-
}
|
207
|
-
else if( type == "function" ) {
|
208
|
-
if( sType == "function" ) {
|
209
|
-
return s;
|
210
|
-
}
|
211
|
-
else if( sType == "string" ) {
|
212
|
-
try {
|
213
|
-
f = eval(s);
|
214
|
-
if( typeof f == "function") {
|
215
|
-
return f;
|
216
|
-
}
|
217
|
-
}
|
218
|
-
catch(e) {
|
219
|
-
// silent
|
220
|
-
}
|
221
|
-
}
|
222
|
-
throw new Error("Expected a function or the name of a function but got " + s);
|
223
|
-
}
|
224
|
-
else if( type == "enum" ) {
|
225
|
-
choices = spec.choices;
|
226
|
-
for(i = 0, n = choices.length; i < n; i += 1 ) {
|
227
|
-
if(choices[i] == s) {
|
228
|
-
return s;
|
229
|
-
}
|
230
|
-
}
|
231
|
-
throw new Error("Expected one of " + choices.join(", ") + " but got " + s);
|
232
|
-
}
|
233
|
-
else if( type == "expression" ) {
|
234
|
-
return Exhibit.ExpressionParser().parse(s);
|
235
|
-
}
|
236
|
-
else {
|
237
|
-
throw new Error("Unknown setting type " + type);
|
238
|
-
}
|
239
|
-
};
|
240
|
-
|
241
|
-
that.collectSettingsFromDOM = function(specs) {
|
242
|
-
var field, spec, name, settings, type, value, dimensions,
|
243
|
-
separator, a, i, n;
|
244
|
-
|
245
|
-
that.options.facet = that.options.facet || { };
|
246
|
-
|
247
|
-
settings = that.options.facet;
|
248
|
-
|
249
|
-
for(field in specs) {
|
250
|
-
spec = specs[field];
|
251
|
-
name = field;
|
252
|
-
if( "name" in spec ) {
|
253
|
-
name = spec.name;
|
254
|
-
}
|
255
|
-
if( !(name in settings) && "defaultValue" in spec) {
|
256
|
-
settings[name] = spec.defaultValue;
|
257
|
-
}
|
258
|
-
|
259
|
-
value = $(container).attr("ex:" + field);
|
260
|
-
if( value == null ) {
|
261
|
-
continue;
|
262
|
-
}
|
263
|
-
|
264
|
-
if( typeof value == "string") {
|
265
|
-
value = value.trim();
|
266
|
-
if( value.length == 0 ) {
|
267
|
-
continue;
|
268
|
-
}
|
269
|
-
}
|
270
|
-
|
271
|
-
type = "text";
|
272
|
-
if( "type" in spec ) {
|
273
|
-
type = spec.type;
|
274
|
-
}
|
275
|
-
|
276
|
-
dimensions = 1;
|
277
|
-
if( "dimensions" in spec ) {
|
278
|
-
dimensions = spec.dimensions;
|
279
|
-
}
|
280
|
-
|
281
|
-
try {
|
282
|
-
if( dimensions > 1 || dimensions == '*') {
|
283
|
-
separator = ",";
|
284
|
-
if( "separator" in spec ) {
|
285
|
-
separator = spec.separator;
|
286
|
-
}
|
287
|
-
|
288
|
-
a = value.split(separator);
|
289
|
-
if( dimensions != '*' && a.length != dimensions ) {
|
290
|
-
throw new Error("Expected a tuple of " + dimensions + " dimensions separated with " + separator + " but got " + value);
|
291
|
-
}
|
292
|
-
else {
|
293
|
-
for(i = 0, n = a.length; i < n; i += 1 ) {
|
294
|
-
a[i] = parseSetting(a[i].trim(), type, spec);
|
295
|
-
}
|
296
|
-
|
297
|
-
settings[name] = a;
|
298
|
-
}
|
299
|
-
}
|
300
|
-
else {
|
301
|
-
settings[name] = parseSetting(value, type, spec);
|
302
|
-
}
|
303
|
-
}
|
304
|
-
catch(e) {
|
305
|
-
Exhibit.debug(e);
|
306
|
-
}
|
307
|
-
}
|
308
|
-
|
309
|
-
that.options.facet = settings;
|
310
|
-
};
|
311
|
-
|
312
162
|
options = that.options;
|
313
163
|
|
314
164
|
that.events = { };
|
@@ -317,14 +167,14 @@ Fabulator.namespace('Exhibit');
|
|
317
167
|
options.viewPanel.registerFilter(that);
|
318
168
|
|
319
169
|
if( "settingSpec" in options ) {
|
320
|
-
that.collectSettingsFromDOM(options.settingSpec);
|
170
|
+
that.options.facet = Exhibit.Util.collectSettingsFromDOM(container, options.settingSpec);
|
321
171
|
}
|
322
172
|
|
323
173
|
return that;
|
324
174
|
};
|
325
175
|
|
326
176
|
Exhibit.Facets.TextSearch = function(container, options) {
|
327
|
-
var that = Exhibit.Facets.
|
177
|
+
var that = Exhibit.Facets.initView("TextSearch", container, options),
|
328
178
|
dom, input_id;
|
329
179
|
options = that.options;
|
330
180
|
|
@@ -342,7 +192,7 @@ Fabulator.namespace('Exhibit');
|
|
342
192
|
if( that.text && that.options.facet.expression ) {
|
343
193
|
values = [ ];
|
344
194
|
$(that.options.facet.expression).each(function(idx, ex) {
|
345
|
-
var items = ex.
|
195
|
+
var items = ex.evaluateOnItem(id, dataSource);
|
346
196
|
values = values.concat(items.values.items());
|
347
197
|
});
|
348
198
|
n = values.length;
|
@@ -366,7 +216,7 @@ Fabulator.namespace('Exhibit');
|
|
366
216
|
$("<input type='text' id='" + input_id + "'>").appendTo($(dom.valuesContainer));
|
367
217
|
|
368
218
|
$("#" + input_id).keyup(function() {
|
369
|
-
that.text = $("#" + input_id).val().toLowerCase()
|
219
|
+
that.text = $.trim($("#" + input_id).val().toLowerCase());
|
370
220
|
that.events.onFilterChange.fire();
|
371
221
|
});
|
372
222
|
|
@@ -374,7 +224,7 @@ Fabulator.namespace('Exhibit');
|
|
374
224
|
};
|
375
225
|
|
376
226
|
Exhibit.Facets.List = function(container, options) {
|
377
|
-
var that = Exhibit.Facets.
|
227
|
+
var that = Exhibit.Facets.initView("List", container, options),
|
378
228
|
valueSet = Exhibit.Set(),
|
379
229
|
counts = { },
|
380
230
|
entries = [ ],
|
@@ -539,7 +389,7 @@ Fabulator.namespace('Exhibit');
|
|
539
389
|
// that.eventModelChange(that.viewPanel.dataView);
|
540
390
|
|
541
391
|
that.eventFilterItem = function(dataSource, id) {
|
542
|
-
var values = options.facet.expression.
|
392
|
+
var values = options.facet.expression.evaluateOnItem(id, dataSource),
|
543
393
|
i, n;
|
544
394
|
|
545
395
|
|
@@ -0,0 +1,473 @@
|
|
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
|
+
Fabulator.namespace('Exhibit');
|
35
|
+
|
36
|
+
(function($, Exhibit) {
|
37
|
+
|
38
|
+
Exhibit.Util = { };
|
39
|
+
|
40
|
+
var parseSetting = function(s, type, spec) {
|
41
|
+
var sType = typeof s, f, i, n, choices;
|
42
|
+
|
43
|
+
if(type == "text") {
|
44
|
+
return s;
|
45
|
+
}
|
46
|
+
else if( type == "float" ) {
|
47
|
+
if( sType == "number" ) {
|
48
|
+
return s;
|
49
|
+
}
|
50
|
+
else if( sType == "string" ) {
|
51
|
+
f = parseFloat(s);
|
52
|
+
if( !isNaN(f) ) {
|
53
|
+
return f;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
throw new Error("Expected a floating point number but got " + s);
|
57
|
+
}
|
58
|
+
else if( type == "int" ) {
|
59
|
+
if( sType == "number" ) {
|
60
|
+
return Math.round(s);
|
61
|
+
}
|
62
|
+
else if( sType == "string" ) {
|
63
|
+
n = parseInt(s);
|
64
|
+
if( !isNaN(n) ) {
|
65
|
+
return n;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
throw new Error("Expected an integer but got " + s);
|
69
|
+
}
|
70
|
+
else if(type == "boolean" ) {
|
71
|
+
if( sType == "boolean" ) {
|
72
|
+
return s;
|
73
|
+
}
|
74
|
+
else if( sType == "string" ) {
|
75
|
+
s = s.toLowerCase();
|
76
|
+
if( s == "true" || s == "on" || s == "yes" ) {
|
77
|
+
return true;
|
78
|
+
}
|
79
|
+
else if( s == "false" || s == "off" || s == "no" ) {
|
80
|
+
return false;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
throw new Error("Expected either 'true' or 'false' but got " + s);
|
84
|
+
}
|
85
|
+
else if( type == "function" ) {
|
86
|
+
if( sType == "function" ) {
|
87
|
+
return s;
|
88
|
+
}
|
89
|
+
else if( sType == "string" ) {
|
90
|
+
try {
|
91
|
+
f = eval(s);
|
92
|
+
if( typeof f == "function") {
|
93
|
+
return f;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
catch(e) {
|
97
|
+
// silent
|
98
|
+
}
|
99
|
+
}
|
100
|
+
throw new Error("Expected a function or the name of a function but got " + s);
|
101
|
+
}
|
102
|
+
else if( type == "enum" ) {
|
103
|
+
choices = spec.choices;
|
104
|
+
for(i = 0, n = choices.length; i < n; i += 1 ) {
|
105
|
+
if(choices[i] == s) {
|
106
|
+
return s;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
throw new Error("Expected one of " + choices.join(", ") + " but got " + s);
|
110
|
+
}
|
111
|
+
else if( type == "expression" ) {
|
112
|
+
return Exhibit.ExpressionParser().parse(s);
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
throw new Error("Unknown setting type " + type);
|
116
|
+
}
|
117
|
+
};
|
118
|
+
|
119
|
+
Exhibit.Util.collectSettingsFromDOM = function(container, specs) {
|
120
|
+
var field, spec, name, settings, type, value, dimensions,
|
121
|
+
separator, a, i, n;
|
122
|
+
|
123
|
+
settings = { };
|
124
|
+
|
125
|
+
for(field in specs) {
|
126
|
+
spec = specs[field];
|
127
|
+
name = field;
|
128
|
+
if( "name" in spec ) {
|
129
|
+
name = spec.name;
|
130
|
+
}
|
131
|
+
if( !(name in settings) && "defaultValue" in spec) {
|
132
|
+
settings[name] = spec.defaultValue;
|
133
|
+
}
|
134
|
+
|
135
|
+
value = $(container).attr("ex:" + field);
|
136
|
+
if( value == null ) {
|
137
|
+
continue;
|
138
|
+
}
|
139
|
+
|
140
|
+
if( typeof value == "string") {
|
141
|
+
value = $.trim(value);
|
142
|
+
if( value.length == 0 ) {
|
143
|
+
continue;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
type = "text";
|
148
|
+
if( "type" in spec ) {
|
149
|
+
type = spec.type;
|
150
|
+
}
|
151
|
+
|
152
|
+
dimensions = 1;
|
153
|
+
if( "dimensions" in spec ) {
|
154
|
+
dimensions = spec.dimensions;
|
155
|
+
}
|
156
|
+
|
157
|
+
try {
|
158
|
+
if( dimensions > 1 || dimensions == '*') {
|
159
|
+
separator = ",";
|
160
|
+
if( "separator" in spec ) {
|
161
|
+
separator = spec.separator;
|
162
|
+
}
|
163
|
+
|
164
|
+
a = value.split(separator);
|
165
|
+
if( dimensions != '*' && a.length != dimensions ) {
|
166
|
+
throw new Error("Expected a tuple of " + dimensions + " dimensions separated with " + separator + " but got " + value);
|
167
|
+
}
|
168
|
+
else {
|
169
|
+
for(i = 0, n = a.length; i < n; i += 1 ) {
|
170
|
+
a[i] = parseSetting($.trim(a[i]), type, spec);
|
171
|
+
}
|
172
|
+
|
173
|
+
settings[name] = a;
|
174
|
+
}
|
175
|
+
}
|
176
|
+
else {
|
177
|
+
settings[name] = parseSetting(value, type, spec);
|
178
|
+
}
|
179
|
+
}
|
180
|
+
catch(e) {
|
181
|
+
Exhibit.debug(e);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
return settings;
|
186
|
+
};
|
187
|
+
|
188
|
+
Exhibit.Util.TypeParsers = {
|
189
|
+
text: function(v, f) {
|
190
|
+
return f(v);
|
191
|
+
},
|
192
|
+
float: function(v, f) {
|
193
|
+
var n = parseFloat(v);
|
194
|
+
if(!isNaN(n)) {
|
195
|
+
return f(n);
|
196
|
+
}
|
197
|
+
return false;
|
198
|
+
},
|
199
|
+
|
200
|
+
int: function(v, f) {
|
201
|
+
var n = parseInt(v);
|
202
|
+
if(!isNaN(n)) {
|
203
|
+
return f(n);
|
204
|
+
}
|
205
|
+
return false;
|
206
|
+
},
|
207
|
+
|
208
|
+
date: function(v, f) {
|
209
|
+
var d;
|
210
|
+
|
211
|
+
if( v instanceof Date ) {
|
212
|
+
return f(v);
|
213
|
+
}
|
214
|
+
else if( typeof(v) == "number" ) {
|
215
|
+
d = new Date(0);
|
216
|
+
d.setUTCFullYear(v);
|
217
|
+
return f(d);
|
218
|
+
}
|
219
|
+
else {
|
220
|
+
d = Exhibit.DateTime.parseIso8601DateTime(v.toString());
|
221
|
+
if( d != null ) {
|
222
|
+
return f(d);
|
223
|
+
}
|
224
|
+
}
|
225
|
+
return false;
|
226
|
+
},
|
227
|
+
url: function(v, f) {
|
228
|
+
return f(Exhibit.Persistence.resolveURL(v.toString()));
|
229
|
+
},
|
230
|
+
boolean: function(v, f) {
|
231
|
+
v = v.toString().toLowerCase();
|
232
|
+
if( v == "true" ) {
|
233
|
+
return f(true);
|
234
|
+
}
|
235
|
+
else if( v == "false" ) {
|
236
|
+
return f(false);
|
237
|
+
}
|
238
|
+
return false;
|
239
|
+
}
|
240
|
+
};
|
241
|
+
|
242
|
+
var typeToParser = function(type) {
|
243
|
+
if( type in Exhibit.Util.TypeParsers ) {
|
244
|
+
return Exhibit.Util.TypeParsers[type];
|
245
|
+
}
|
246
|
+
throw new Error("Unknown setting type " + type);
|
247
|
+
};
|
248
|
+
|
249
|
+
var createTupleAccessor = function(f, spec) {
|
250
|
+
var value = f(spec.attributeName),
|
251
|
+
expression, parsers, i, n, bindingNames, separator;
|
252
|
+
|
253
|
+
if( value == null ) {
|
254
|
+
return null;
|
255
|
+
}
|
256
|
+
|
257
|
+
if( typeof(value) == "string" ) {
|
258
|
+
value = $.trim(value);
|
259
|
+
if( value.length == 0 ) {
|
260
|
+
return null;
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
264
|
+
try {
|
265
|
+
expression = Exhibit.ExpressionParser().parse(value);
|
266
|
+
parsers = [ ];
|
267
|
+
bindingTypes = spec.types;
|
268
|
+
|
269
|
+
for( i = 0, n = bindingTypes.length; i < n; i += 1 ) {
|
270
|
+
parsers.push(typeToParser(bindingTypes[i]));
|
271
|
+
}
|
272
|
+
|
273
|
+
bindingNames = spec.bindingNames;
|
274
|
+
separator = ",";
|
275
|
+
|
276
|
+
if( "separator" in spec ) {
|
277
|
+
separator = spec.separator;
|
278
|
+
}
|
279
|
+
|
280
|
+
return function(itemID, database, visitor, tuple) {
|
281
|
+
expression.evaluateOnItem(itemID, database).values.visit(
|
282
|
+
function(v) {
|
283
|
+
var a = v.split(separator),
|
284
|
+
tuple2 = { },
|
285
|
+
i, n;
|
286
|
+
|
287
|
+
if( a.length == parsers.length ) {
|
288
|
+
if( tuple ) {
|
289
|
+
for(n in tuple) {
|
290
|
+
tuple2[n] = tuple[n];
|
291
|
+
}
|
292
|
+
}
|
293
|
+
|
294
|
+
for(i = 0, n = bindingNames.length; i < n; i += 1) {
|
295
|
+
tuple2[bindingNames[i]] = null;
|
296
|
+
parsers[i](a[i], function(v) { tuple2[bindingNames[i]] = v; });
|
297
|
+
}
|
298
|
+
visitor(tuple2);
|
299
|
+
}
|
300
|
+
}
|
301
|
+
);
|
302
|
+
};
|
303
|
+
}
|
304
|
+
catch(e) {
|
305
|
+
Exhibit.debug(e);
|
306
|
+
return null;
|
307
|
+
}
|
308
|
+
};
|
309
|
+
|
310
|
+
var createElementalAccessor = function(f, spec) {
|
311
|
+
var value = f(spec.attributeName),
|
312
|
+
bindingType = "text",
|
313
|
+
expression, parser;
|
314
|
+
|
315
|
+
if( value == null ) {
|
316
|
+
return null;
|
317
|
+
}
|
318
|
+
|
319
|
+
if( typeof(value) == "string" ) {
|
320
|
+
value = $.trim(value);
|
321
|
+
if( value.length == 0 ) {
|
322
|
+
return null;
|
323
|
+
}
|
324
|
+
}
|
325
|
+
|
326
|
+
if( "type" in spec ) {
|
327
|
+
bindingType = spec.type;
|
328
|
+
}
|
329
|
+
|
330
|
+
try {
|
331
|
+
expression = Exhibit.ExpressionParser().parse(value);
|
332
|
+
parser = typeToParser(bindingType);
|
333
|
+
|
334
|
+
return function(itemID, database, visitor) {
|
335
|
+
expression.evaluateOnItem(itemID, database).values.visit(
|
336
|
+
function(v) { return parser(v, visitor); }
|
337
|
+
);
|
338
|
+
};
|
339
|
+
}
|
340
|
+
catch(e) {
|
341
|
+
Exhibit.debug(e);
|
342
|
+
return null;
|
343
|
+
}
|
344
|
+
};
|
345
|
+
|
346
|
+
var evaluateBindings = function(value, database, visitor, bindings) {
|
347
|
+
var maxIndex = bindings.length - 1,
|
348
|
+
f = function(tuple, index) {
|
349
|
+
var binding = bindings[index],
|
350
|
+
visited = false,
|
351
|
+
recurse = index == maxIndex ? function() { visitor(tuple); } : function() { f(tuple, index + 1); },
|
352
|
+
bindingName;
|
353
|
+
|
354
|
+
/*
|
355
|
+
The tuple accessor will copy existing fields out of "tuple" into a new
|
356
|
+
object and then injects new fields into it before calling the visitor.
|
357
|
+
This is so that the same tuple object is not reused for different
|
358
|
+
tuple values, which would cause old tuples to be overwritten by new ones.
|
359
|
+
*/
|
360
|
+
if( binding.isTuple ) {
|
361
|
+
binding.accessor(
|
362
|
+
value,
|
363
|
+
database,
|
364
|
+
function(tuple2) { visited = true; tuple = tuple2; recurse(); },
|
365
|
+
tuple
|
366
|
+
);
|
367
|
+
}
|
368
|
+
else {
|
369
|
+
bindingName = binding.bindingName;
|
370
|
+
binding.accessor(
|
371
|
+
value,
|
372
|
+
database,
|
373
|
+
function(v) { visited = true; tuple[bindingName] = v; recurse(); }
|
374
|
+
);
|
375
|
+
}
|
376
|
+
|
377
|
+
if( !visited ) { recurse(); }
|
378
|
+
};
|
379
|
+
|
380
|
+
f({}, 0);
|
381
|
+
};
|
382
|
+
|
383
|
+
var createBindingsAccessor = function(f, bineingSpecs) {
|
384
|
+
var bindings = [ ],
|
385
|
+
i, n, accessor, bindingSpec, accessor, isTuple;
|
386
|
+
|
387
|
+
for( i = 0, n = bindingSpecs.length; i < n; i += 1 ) {
|
388
|
+
bindingSpec = bindingSpecs[i];
|
389
|
+
accessor = null;
|
390
|
+
isTuple = false;
|
391
|
+
|
392
|
+
if( "bindingNames" in bindingSpec ) {
|
393
|
+
isTuple = true;
|
394
|
+
accessor = createTupleAccessor(f, bindingSpec);
|
395
|
+
}
|
396
|
+
else {
|
397
|
+
accessor = createElementalAccessor(f, bindingSpec);
|
398
|
+
}
|
399
|
+
|
400
|
+
if( accessor = null ) {
|
401
|
+
if( !("optional" in bindingSpec) || !bindingSpec.optional ) {
|
402
|
+
return null;
|
403
|
+
}
|
404
|
+
}
|
405
|
+
else {
|
406
|
+
bindings.push({
|
407
|
+
bindingName: bindingSpec.bindingName,
|
408
|
+
accessor: accessor,
|
409
|
+
isTuple: isTuple
|
410
|
+
});
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
return function(value, database, visitor) {
|
415
|
+
evaluateBindings(value, database, visitor, bindings);
|
416
|
+
};
|
417
|
+
};
|
418
|
+
|
419
|
+
var createAccessors = function(f, specs, accessors) {
|
420
|
+
var field, spec, accessorName, accessor, isTuple,
|
421
|
+
createOneAccessor, alternatives, i, n;
|
422
|
+
|
423
|
+
for(field in specs) {
|
424
|
+
spec = specs[field];
|
425
|
+
accessorName = spec.accessorName;
|
426
|
+
accessor = null;
|
427
|
+
isTuple = false;
|
428
|
+
|
429
|
+
createOneAccessor = function(spec2) {
|
430
|
+
isTuple = false;
|
431
|
+
if( "bindings" in specs ) {
|
432
|
+
return createBindingsAccessor(f, spec2.bindings);
|
433
|
+
}
|
434
|
+
else if( "bindingNames" in spec2 ) {
|
435
|
+
isTuple = true;
|
436
|
+
return createTupleAccessor(f, spec2);
|
437
|
+
}
|
438
|
+
else {
|
439
|
+
return createElementalAccessor(f, spec2);
|
440
|
+
}
|
441
|
+
};
|
442
|
+
|
443
|
+
if( "alternatives" in spec ) {
|
444
|
+
alternatives = spec.alternatives;
|
445
|
+
for( i = 0, n = alternatives.length; i < n; i += 1 ) {
|
446
|
+
accessor = createOneAccessor(alternatives[i]);
|
447
|
+
if( accessor != null ) {
|
448
|
+
break;
|
449
|
+
}
|
450
|
+
}
|
451
|
+
}
|
452
|
+
else {
|
453
|
+
accessor = createOneAccessor(spec);
|
454
|
+
}
|
455
|
+
|
456
|
+
if( accessor != null ) {
|
457
|
+
accessors[accessorName] = accessor;
|
458
|
+
}
|
459
|
+
else if( !(accessorName in accessors) ) {
|
460
|
+
accessors[accessorName] = function(value, database, visitor) {};
|
461
|
+
}
|
462
|
+
}
|
463
|
+
};
|
464
|
+
|
465
|
+
Exhibit.Util.createAccessorsFromDOM = function(container, specs, accessors) {
|
466
|
+
createAccessors(
|
467
|
+
function(field) { return $(container).attr("ex:" + field); },
|
468
|
+
specs,
|
469
|
+
accessors
|
470
|
+
);
|
471
|
+
};
|
472
|
+
|
473
|
+
})(jQuery, Fabulator.Exhibit);
|