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.
@@ -104,7 +104,87 @@ Fabulator.namespace('Exhibit');
104
104
  return div.firstChild;
105
105
  };
106
106
 
107
- var createDOMFromTemplate = function(rootID, templateNode, result, parentElmt) {
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.evaluateOneItem(rootID, that.dataSource);
203
- if( items.values.size() > 1 ) {
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.evaluateOneItem(rootID, that.dataSource);
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
  });
@@ -110,7 +110,7 @@
110
110
  };
111
111
  };
112
112
 
113
- that.evaluateOneItem = function( itemID, database ) {
113
+ that.evaluateOnItem = function( itemID, database ) {
114
114
  return this.evaluate(
115
115
  { "value" : itemID },
116
116
  { "value" : "item" },
@@ -156,159 +156,9 @@ Fabulator.namespace('Exhibit');
156
156
  return that;
157
157
  };
158
158
 
159
- Exhibit.Facets.Base = function(type, container, options) {
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.Base("TextSearch", container, options),
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.evaluateOneItem(id, dataSource);
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().trim();
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.Base("List", container, options),
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.evaluateOneItem(id, dataSource),
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);