1 /**
  2  * @fileOverview Semantic Web Widget Library
  3  * @author <a href="mailto:jambo@mit.edu">Jim Hollenbach</a>
  4  * @license MIT License
  5  * @license GPL v2
  6  */
  7 
  8 /**
  9  * @exports $ as jQuery
 10  */
 11 
 12 /**
 13  * @name jQuery
 14  * @namespace The jQuery Javascript toolkit.
 15  */
 16 
 17 /**
 18  * @name jQuery.rdfwidgets
 19  * @namespace Library functions for the widget library.
 20  */
 21 jQuery.rdfwidgets = function($) {
 22 
 23     var CONVERT_BASE = "http://feelings.xvm.mit.edu/"
 24 
 25     var langs = {
 26         "en": true,
 27         "en-US":true
 28     };
 29 
 30     var widgets = {};
 31     var matchers = [];
 32     var loadFilters = [];
 33     var pendingSources = [];
 34     /**
 35      * @description Define a new widget for use in the library.
 36      * @param {String} name the namespaced name of the widget, eg. ui.mywidgetname
 37      * @param {Object} prototype the prototype class of the new widget.
 38      */
 39     $.rdfwidget = function( name, prototype ) {
 40         var namespace = name.split( "." )[ 0 ], fullName;
 41         var sname = name.split( "." )[ 1 ];
 42         fullName = namespace + "-" + sname;
 43         widgets[fullName] = {s:(':'+fullName),n:sname};
 44 
 45         var w = $.extend({}, $.RdfWidget.prototype, prototype);
 46         $.widget(name,w);
 47     };
 48 
 49     /**
 50      * @class
 51      * @description The base class for all SW Widgets.
 52      */
 53     $.RdfWidget = function(options, element) {
 54 
 55 
 56     };
 57 
 58 
 59     $.RdfWidget.prototype = {
 60 
 61         /**
 62          * @description Get a valid term given a string input by the user, according to the options the user provided to the widget such as prefixes.
 63          * @param {String | Object} term The string or Term object provided by the user.
 64          * @returns {Object} A Literal or Symbol that matches the term supplied by the user.
 65          */
 66         _resource: function( term ) {
 67             return processResource( term, this.options );
 68         },
 69 
 70         /**
 71          * @description A function that is called whenever new data is loaded into the local store.
 72          * @param {Array} trips The new $rdf.Triples that were added to the store.
 73          */
 74         insertData: function( trips ) {
 75             if( !this.options.ignoreupdates ) {
 76                 this.refresh();
 77             }
 78         },
 79 
 80         /**
 81          * @description Redraw the widget.
 82          */
 83         refresh: function() {},
 84 
 85         /**
 86          * @description A function that is called whenever new data is removed from the local store.
 87          * @param {Array} trips The new $rdf.Triples that were removed from the store.
 88          */
 89         deleteData: function( trips ) {
 90             if( !this.options.ignoreupdates ) {
 91                 this.refresh();
 92             }
 93         },
 94 
 95         /**
 96          * @description Get a Matcher for the provided element.
 97          * @param {String | Object} [element=this.element] The element to draw into. Can be any valid argument to a jQuery constructor.
 98          * @param {Boolean} [norefresh=false] If true, this matcher will not refresh when new data is loaded.
 99          */
100         _matcher: function( element, norefresh ) {
101             return Matcher( (element ? element : this.element) , this.options, norefresh );
102         }
103     };
104 
105     /**
106      * @class
107      * @description a class for matching items.
108      */
109     var Matcher = function( element, opts, norefresh ) {
110         var bindings = [{}]; // {"varname":somesymbol, "varname2":someliteral}
111         var elt = $(element);
112         var filters = processSourceFilter( opts.sources, opts );
113         var history = [];
114         var cstring = "";
115         var wstring = "";
116         function processVar( s ) {
117             if( (typeof s) === "string" ) {
118                 if( s.length > 0 && s.charAt(0) == "?" ) {
119                     if( s.length > 1 ) {
120                         return s.substring(1);
121                     }
122                     throw "invalid variable name: "+str;
123                 }
124             }
125             return null;
126         }
127         var m = /** @lends jQuery.rdfwidgets-Matcher.prototype */{
128             /**
129              * @description foobarbaz
130              */
131             match: function( s,p,o,optional ) {
132                 var s_v = processVar( s );
133                 var p_v = processVar( p );
134                 var o_v = processVar( o );
135                 
136                 var newBindings = [];
137                 var matches;
138                 for( var i=0; i < bindings.length; i++ ) {
139                     var b = bindings[i]
140                     matches = $.rdfwidgets.statementsMatching( s_v ? (b[s_v] ? b[s_v] : undefined) : processResource( s, opts ),
141                                                                p_v ? (b[p_v] ? b[p_v] : undefined) : processResource( p, opts ),
142                                                                o_v ? (b[o_v] ? b[o_v] : undefined) : processObject( o, opts ),
143                                                                undefined, false, opts );
144                     if( opts.filterduplicates ) {
145                         matches = filterDuplicates( matches );
146                     }
147                     if( (!matches || (matches && matches.length === 0)) && optional ) {
148                         var newvals = {};
149                         if( s_v && !b[s_v] ) { newvals[s_v] = null }
150                         if( p_v && !b[p_v] ) { newvals[p_v] = null }
151                         if( o_v && !b[o_v] ) { newvals[o_v] = null }
152                         newBindings.push( $.extend( {}, b, newvals ) );
153                     } else {
154                         for( var j=0; j < matches.length; j++ ) {
155                             //use extend..
156                             var newvals = {};
157                             if( s_v && !b[s_v] ) { newvals[s_v] = matches[j].subject; }
158                             if( p_v && !b[p_v] ) { newvals[p_v] = matches[j].predicate; }
159                             if( o_v && !b[o_v] ) { newvals[o_v] = matches[j].object; }
160                             newBindings.push( $.extend( {}, b, newvals ) );
161                         }
162                     }
163                 }
164                 bindings = newBindings;
165 
166                 //build up query string..
167                 var pat = ""
168                 if( optional ) {
169                     pat += " optional { ";
170                 }
171                 if( s_v ) {
172                     pat += " " + s + " ";
173                 } else { pat += " " + processResource( s, opts ).toNT() + " ";
174                 }
175                 if( p_v ) {
176                     pat += " " + p + " ";
177                 } else { pat += " " + processResource( p, opts ).toNT() + " ";
178                 }
179                 if( o_v ) {
180                     pat += " " + o + " . ";
181                 } else {
182                     pat += " " + processObject( o, opts ).toNT() + " . ";
183                 }
184                 if( optional ) {
185                     pat += " } ";
186                 }
187                 if( !optional ) {
188                     cstring += pat;
189                 }
190                 wstring += pat;
191                 history.push({f:this.match, args:[s,p,o,optional]});
192                 return this;
193             },
194             
195             optional: function( s,p,o ) {
196                 return this.match( s,p,o,true );
197             },
198 
199             filter: function( f ) {
200                 bindings = $.grep( bindings, f );
201                 history.push({f:this.filter, args:[f]});
202                 return this;
203             },
204             
205             draw: function( template, element, justOne ) {
206                 var target = element ? $(element) : elt;
207                 var output = "";
208                 var temp;
209                 for( var i = 0; i < bindings.length && (!justOne || (justOne && i < 1 ) ); i++ ) {
210                     temp = template;
211                     for( x in bindings[i] ) {
212                         temp = temp.replace( new RegExp("\\?"+x,"g"), (bindings[i][x] ? bindings[i][x].value : "None" ));
213                         temp = temp.replace( new RegExp("\\@"+x,"g"), (bindings[i][x] ? doLabel( bindings[i][x] , opts ) : "None" )); 
214                     }
215                     output += temp;
216                 }
217                 target.html( output );
218                 processHTMLWidgets( target );
219                 history.push({f:this.draw, args:[template, element, justOne]});
220                 return this;
221             },
222             
223             page: function( template, perPage, element ) {
224                 //split bindings into ceil(bindings.length/perPage) pages.
225                 if( !perPage ) { perPage = 10 }
226                 //draw the first page, put listeners.
227                 var target = element ? $(element) : elt;
228                 var currentPage = 0;
229                 var area = $('<div class="rdfpagearea"></div>');
230                 var page = $('<div class="rdfpage"></div>');
231                 var left = $('<div style="float:left"><a href="#">< Previous</a></div>');
232                 var center = $('<div style="clear:both"> </div>');
233                 var right = $('<div style="float:right"><a href="#">Next ></a></div>');
234                 
235                 left.click( function( e ) {
236                     e.preventDefault();
237                     if( currentPage > 0 ) {
238                         currentPage--;
239                         drawPage( currentPage );
240                     }
241                 });
242                 
243                 right.click( function( e ) { 
244                     e.preventDefault();
245                     if( (currentPage+1)*perPage < bindings.length ) {
246                         currentPage++;
247                         drawPage( currentPage );
248                     }
249                 });
250                 
251                 area.append( page );
252                 area.append( left );
253                 area.append( right );
254                 area.append( center );
255                 var drawPage = function( pnumber ) {
256                     
257                     if( pnumber === 0 ) {
258                         left.hide();
259                         right.show();
260                     } else if ( (pnumber+1)*perPage >= bindings.length ) {
261                         left.show();
262                         right.hide();
263                     } else {
264                         left.show();
265                         right.show();
266                     }
267                     
268                     var output = "";
269                     for( var i = pnumber * perPage; ( i < (pnumber+1)*perPage && (i < bindings.length) ); i++ ) {
270                         temp = template;
271                         for( x in bindings[i] ) {
272                             temp = temp.replace( new RegExp("\\?"+x,"g"), (bindings[i][x] ? bindings[i][x].value : "None" ));
273                             temp = temp.replace( new RegExp("\\@"+x,"g"), (bindings[i][x] ? doLabel( bindings[i][x] , opts ) : "None" )); 
274                         }
275                         output += temp;
276                     }
277                     page.html( output );
278                     processHTMLWidgets( target );                    
279                 }
280                 drawPage( currentPage );
281                 target.empty().append(area);
282                 history.push({f:this.page, args:[template, perPage, element]});
283                 return this;
284             },
285             
286             replay: function() {
287                 bindings = [{}];
288                 cstring = "";
289                 wstring = "";
290                 var len = history.length;
291                 for( var i = 0; i < len; i++ ) {
292                     history[i].f.apply( this, history[i].args );
293                 }
294                 if( len > 0 ) {
295                     history = history.slice( len );
296                 }
297             },
298 
299             reset: function() {
300                 elt.empty();
301                 bindings = [{}];
302                 history = [];
303                 cstring = "";
304                 wstring = "";
305                 return this;
306             },
307 
308             query: function( uri ) {
309                 var q = "construct { " + cstring + " } where { "+ wstring +" }";
310                 q = escape( q );
311                 var u;
312                 if( uri.indexOf( "?" ) !== -1 ) {
313                     u = uri + "&query="+q;
314                 } else {
315                     u = uri + "?query="+q;
316                 }
317                 $.rdfwidgets.load( u );
318             },
319 
320             bindings: function() {
321                 return $.extend( true, [], bindings );
322             }
323         };
324         if( !norefresh ) {
325             matchers.push( m );
326         }
327         return m;
328     };
329 
330     function allSubClasses( uri, o ) {
331         var s = processResource( uri, o );
332         var c = {uri : s}
333         var q = [ s ];
334         while( q.length > 0 ) {
335             var more = $.rdfwidgets.each( undefined, db.sym("http://www.w3.org/2000/01/rdf-schema#subClassOf"), q[0] );
336             for( var i = 0; i < more.length; i++ ) {
337                 if( !c[more[i].uri] ) {
338                     c[more[i].uri] = more[i];
339                     q.push( more[i] );
340                 }
341             }
342             q = q.slice(1);
343         }
344         return c;
345     }
346 
347     function allSubProperties( uri, o ) {
348         var s = processResource( uri, o );
349         var c = {uri : s}
350         var q = [ s ];
351         while( q.length > 0 ) {
352             var more = $.rdfwidgets.each( undefined, db.sym("http://www.w3.org/2000/01/rdf-schema#subPropertyOf"), q[0] );
353             for( var i = 0; i < more.length; i++ ) {
354                 if( !c[more[i].uri] ) {
355                     c[more[i].uri] = more[i];
356                     q.push( more[i] );
357                 }
358             }
359             q = q.slice(1);
360         }
361         return c;
362     }
363 
364     function filterDuplicates( trips ) {
365         var result = [];
366         var used = {};
367         for( var i = 0; i < trips.length; i++ ) {
368             var hash = db.canon(trips[i].subject).toNT()+db.canon(trips[i].predicate).toNT()+(trips[i].object.termType==="literal" ? trips[i].object.value : db.canon(trips[i].object).toNT() );
369             if( !used[hash] ) {
370                 result.push( trips[i] );
371             }
372             used[hash] = true;
373         }
374         return result;
375     }
376 
377     function deleteData( trips ) {
378         labelCache = {};
379         imageCache = {};
380         var w;
381         for( x in widgets ) {
382             w = widgets[x];
383             //w holds two fields:
384             //w.s, a jquery selector for the widget, eg ":ui-rdflabel"
385             //w.n, the name of the widget, eg "rdflabel"
386             $(w.s)[w.n]("deleteData", trips);
387         }
388         for( var i = 0; i < matchers.length; i++ ) {
389             matchers[i].replay();
390         }
391     }
392 
393     function insertData( trips ) {
394         labelCache = {};
395         imageCache = {};
396         var w;
397         for( x in widgets ) {
398             w = widgets[x];
399             //the following line is explained in deleteData, above.
400             $(w.s)[w.n]("insertData", trips);
401         }
402         for( var i = 0; i < matchers.length; i++ ) {
403             matchers[i].replay();
404         }
405     }
406 
407     var uriRegex = /^(https?):\/\/((?:[a-z0-9.-]|%[0-9A-F]{2}){3,})(?::(\d+))?((?:\/(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})*)*)(?:\?((?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*))?$/i;
408     //var uriRegex = /^(([a-z][\-a-z0-9+\.]*):)?(\/\/([^\/?#]+))?([^?#]*)?(\?([^#]*))?(#(.*))?$/i;
409 
410     function validURI( str ) {
411         m = str.match(uriRegex);
412         if( m === null ) {
413             return false;
414         }
415         return true;
416     }
417 
418     var docpart = $rdf.Util.uri.docpart
419     var join = $rdf.Util.uri.join
420 
421     function randomID() {
422         return "rdfwidget"+Math.floor(Math.random()*100000000).toString();
423     }
424 
425     var SW_PREFIX = "http://dig.csail.mit.edu/2009/swjs#";
426 
427     var db = new $rdf.IndexedFormula();
428 
429     var requestedURIs = {};
430     var endpoints = {};
431     var selectedElement = null;
432     var labelCache = {};
433     var defaultEndpoint = window.location;
434     var defaultUser = null;
435     var defaultFriend = null;
436     var sourceNames = {};
437     var loadedSources = {};
438     function finishLoads() {
439         $.each( db.statementsMatching( undefined, db.sym( SW_PREFIX+"data" ), undefined ), function() {
440             if( !requestedURIs[this.object.uri] ) {
441                 loadDataSource( this.object.uri, undefined, finishLoads );
442             }
443         });
444     }
445 
446     function doneLoading( uri ) {
447         pendingSources = $.grep(pendingSources, function( element ) {
448             return element !== uri;
449         });
450         if( pendingSources.length === 0 ) {
451             $(document).trigger('rdfloaded');
452         }
453     }
454 
455     function loadDataSource( uri, t, callback, originalURI ) {
456         if( uri && !originalURI ) {
457             pendingSources.push(docpart( uri ) );
458         }
459         var refresh = false;
460         var decrement = false;
461         if( (originalURI && requestedURIs[originalURI]) || (!originalURI && requestedURIs[docpart( uri )]) ) {
462             refresh = true;
463         }
464 
465         if(originalURI && originalURI.indexOf('https') === 0 ) {
466             doneLoading( docpart( originalURI ) );
467             return;
468         }
469         var format = (t === "application/json" ? "json" :
470                       (t === "application/jsonp" ? "jsonp" : 
471                        (t === "application/rdf+xml" ? "xml" :
472                         ((t === "text/n3" || t === "text/rdf+n3") ? "text" : 
473                          "text") ) ) );
474         try {
475             requestedURIs[docpart( uri )] = true;
476             if( originalURI ) { requestedURIs[docpart( originalURI )] = true; }
477             $.ajax({
478                 url: uri, 
479                 beforeSend: function( xhr ) {
480                     if( xhr.withCredentials !== undefined ) {
481                         xhr.withCredentials = true;
482                     }
483                 },
484                 timeout:5000,
485                 success: function( data, text, xhr ) {
486                     var contenttype = t;
487                     if( xhr ) {
488                         contenttype = xhr.getResponseHeader("Content-Type").split(";")[0];
489                         if( !contenttype ) {
490                             contenttype = t;
491                         }
492                         format = (contenttype === "application/json" ? "json" :
493                                   (contenttype === "application/jsonp" ? "jsonp" : 
494                                    (contenttype === "application/rdf+xml" ? "xml" :
495                                     ((contenttype === "text/n3" || contenttype === "text/rdf+n3") ? "text" : 
496                                      null) ) ) );
497                     }
498                     if( xhr && xhr.status >= 400  || ( !xhr && format !== "jsonp" ) ) {
499                         doneLoading( originalURI ? docpart( originalURI ) : docpart( uri ) );
500                         return;
501                     }
502                     if( (xhr && xhr.status === 0) || !format || ( !text && !originalURI ) || (!contenttype && !originalURI ) ) {
503                         var converturi = CONVERT_BASE+"?data-uri[]="+escape(uri)+"&input=&output=jsonp";
504                         loadDataSource(converturi, "application/jsonp", callback, uri);
505                         return;
506                     }
507                     var source = originalURI ? originalURI : uri;
508                     if( refresh ) {
509                         db.removeMany( undefined, undefined, undefined, db.sym( source ) );
510                     }
511                     var triples = [];
512                     if( format === "json" || format === "jsonp" ) {
513                         var parser = $rdf.jsonParser;
514                         triples = parser.parseJSON( data, source, db );
515                         if( originalURI ) {
516                             loadedSources[originalURI] = originalURI;
517                         } else {
518                             loadedSources[uri] = uri;
519                         }
520                         if( !decrement ) { decrement = true; if( callback ) { callback(source, true); } }
521                     } else if ( format === "xml" ) {
522                         var p = new $rdf.RDFParser( db );
523                         triples = p.parse( xhr.responseXML, source, source );
524                         if( originalURI ) {
525                             loadedSources[originalURI] = originalURI;
526                         } else {
527                             loadedSources[uri] = uri;
528                         }
529                         if( !decrement ) { decrement = true; if( callback ) { callback(source, true); } }
530                     } else if ( format === "text" ) {
531                         var u = originalURI ? originalURI : uri;
532                         var p = new $rdf.N3Parser(db,db,u,u,null,null,"",null);
533                         p.loadBuf( xhr.responseText );
534                         loadedSources[u] = u;
535                     }
536                     doneLoading( originalURI ? docpart( originalURI ) : docpart( uri ) );
537                     insertData( triples );
538                 },
539                 error: function(xhr) {
540                     requestedURIs[docpart( uri )] = false;
541                     if( !originalURI ) {
542                         var converturi = CONVERT_BASE+"?data-uri[]="+escape(uri)+"&input=&output=jsonp";
543                         loadDataSource(converturi, "application/jsonp", callback, uri);
544                     } else {
545                         if( callback ) { callback( originalURI, false ); }
546                         doneLoading( docpart( originalURI ) );
547                     }
548                 },
549                 dataType: format
550             });
551         } catch(e) {
552             /*                if (jQuery.browser.msie && window.XDomainRequest && !originalURI && uri.indexOf('https://') === 0 && "xml" === format ) {
553             // Use Microsoft XDR
554             var xdr = new XDomainRequest();
555             xdr.open("get", uri);
556             xdr.onload = function() {
557             var source = originalURI ? originalURI : uri;
558             var p = new $rdf.RDFParser( db );
559             var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
560             xmlDoc.async="false";
561             xmlDoc.loadXML(this.responseText);
562             triples = p.parse( xmlDoc, source, source );
563             if( originalURI ) {
564             loadedSources[originalURI] = originalURI;
565             } else {
566             loadedSources[uri] = uri;
567             }
568             return;
569             };
570             xdr.send();
571             return;
572             } */
573             if( !decrement ) { decrement = true; }
574             if( !originalURI ) {
575                 requestedURIs[docpart( uri )] = false;
576                 var converturi = CONVERT_BASE+"?data-uri[]="+escape(uri)+"&input=&output=jsonp";
577                 loadDataSource(converturi, "application/jsonp", callback, uri);
578             } else {
579                 callback( originalURI, false );
580                 doneLoading( docpart( originalURI ) );
581             }
582         }
583     }
584 
585     function loadDataSources() {
586         $('link').each( function() {
587             if(this.href && "sw:data" === this.getAttribute("rel") ) {
588                 var uri = docpart( this.href );
589                 var name = this.getAttribute("name");
590                 if( name ) { sourceNames[name] = uri; }
591                 var endpoint = this.getAttribute("sw:endpoint");
592                 endpoints[uri] = endpoint;
593                 loadDataSource( uri, this.getAttribute("type"), finishLoads );
594             } else if( this.href && "sw:endpoint" === this.getAttribute("rel") ) {
595                 var name = this.getAttribute("name");
596                 if( name ) { sourceNames[name] = uri; }
597                 defaultEndpoint = docpart( this.href );
598             } else if( this.href && "sw:user" === this.getAttribute("rel") ) {
599                 defaultUser = this.href;
600             }
601         });
602         return true;
603     }
604 
605     //====================================
606     // Widgets.
607     //====================================
608 
609     var namespaces = {'foaf':'http://xmlns.com/foaf/0.1/',
610                       'sioc':'http://rdfs.org/sioc/ns#',
611                       'dc'  :'http://purl.org/dc/terms/',
612                       'rdf' :'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
613                       'rdfs':'http://www.w3.org/2000/01/rdf-schema#',
614                       ''    :window.location};
615 
616     function processResource( v, o ) {
617         if( v === null || v === undefined ) { return null; }
618         if( typeof( v ) === "object" && v.termType ) {
619             return v;
620         }
621         if( typeof( v ) === "string" && v.indexOf( '<' ) === 0 && v.lastIndexOf( '<' ) === 0 && v.indexOf( '>' ) === (v.length-1) ) {
622             return db.sym( v.substr( 1, v.length-2 ) );
623         }
624         var index = v.indexOf( ':' );
625         if( index !== -1 && v.indexOf( "://" ) !== index ) {
626             var pfx = v.substr( 0, index );
627             var base = o.namespaces ? ( (o.namespaces[pfx] !== undefined) ? o.namespaces[pfx] : namespaces[pfx] ) : namespaces[pfx];
628             if( base ) {
629                 return db.sym( base + v.substr(index+1) );
630             }
631         }
632         var sym = db.sym(v);
633         return sym;
634     }
635 
636     function processSourceFilter( f, o ) {
637         if( !f ) { return null; }
638         var r = {};
639         for( var i = 0; i < f.length; i++ ) {
640             var n = sourceNames[f[i]];
641             r[ ( n ? n : processResource( f[i], o ).uri ) ] = true;
642         }
643         return r;
644     }
645 
646     function processLiteral( v, o ) {
647         if( v === null || v === undefined ) { return null; }
648         if( typeof( v ) === "object" && "literal" === v.termType) {
649             return v;
650         }
651         var lit = db.literal( v );
652         return lit;
653     }
654     
655     function processObject( v, o ) {
656         if( v === null || v === undefined ) { return null; }
657         if( typeof( v ) === "object" && v.termType ) {
658             return v;
659         }
660         var object;
661         if( validURI ( v ) || (v.indexOf('"') !== 0 && v.indexOf(':') !== -1) ) {
662             object = processResource( v, o );
663         } else {
664             object = processLiteral( v, o );
665         }
666         return object;
667     }
668 
669     function processEditTerm( o ) {
670         if( o.editTerm ) {
671             return o.editTerm;
672         }
673         if( o.subject && o.predicate && o.object ) {
674             o.editTerm = "object";
675             return o.editTerm;
676         } else if ( o.object && o.predicate ) {
677             o.editTerm = "subject";
678             return o.editTerm;
679         }  else if ( o.subject && o.object ) {
680             o.editTerm = "predicate";
681             return o.editTerm;
682         }  else if ( o.subject && o.predicate ) { 
683             o.editTerm = "object";
684             return o.editTerm;
685         }
686         throw "Insufficient information provided to edit or view "+ o.editTerm +" term. S:"+o.subject+" P:"+o.predicate+" O:"+o.object;
687     }
688 
689     function processGraph( o, subject ) {
690         return o.graph ? o.graph : ( (o.acl && o.acl.graph) ? o.acl.graph : (subject ? subject : null ) );
691     }
692 
693     function processACL( o, subject ) {
694         var a = o.acl;
695         var ACL_URI_BASE = "http://jambo.xvm.mit.edu/acl/";
696 
697         if( "string" === typeof(a) && validURI( a ) ) {
698             return a;
699         }
700         if( "object" === typeof(a) ) {
701             var graph = processGraph( o, subject );
702             var user = a.user ? a.user : defaultUser;
703             var friend = a.friend ? a.friend : defaultFriend;
704             if( !graph ) { throw( "could not determine target graph for ACL." ); }
705             if( !a.type ) {
706                 throw "no acl type provided:"+a;
707             } else if( a.type === "permanent" ) {
708                 return ACL_URI_BASE+"permanent.n3?r="+escape(graph);
709             } else if( a.type === "private-readonly" ) {
710                 if( !user ) { throw ("could not determine user for ACL"); }
711                 if( !friend ) { throw ("could not determine friend or group to be given read access for ACL"); }
712                 return ACL_URI_BASE+"private-readonly.n3?r="+escape(graph)+"&u="+escape(user)+"&f="+escape(friend);
713             } else if( a.type === "private-editable" ) {
714                 if( !user ) { throw ("could not determine user for ACL"); }
715                 if( !friend ) { throw ("could not determine friend or group to be given read-write access for ACL"); }
716                 return ACL_URI_BASE+"private.n3?r="+escape(graph)+"&u="+escape(user)+"&f="+escape(friend);
717             } else if( a.type === "public-editable" ) {
718                 return ACL_URI_BASE+"public.n3?r="+escape(graph);
719             } else if( a.type === "public-readonly" ) {
720                 return ACL_URI_BASE+"public-readonly.n3?r="+escape(graph)+"&u="+escape(user);
721             }
722         }
723         throw "unrecognized acl format:"+a;
724     }
725 
726     function getMenuPredicateOptions( o ) {
727         var props = $.rdfwidgets.each( undefined, db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"), undefined, o );
728         var uriToValue = {};
729         var valueToURI = {};
730         var bindings = [];
731         for( x in props ) {
732             var binding = {};
733             bindings.push( binding );
734             var prop = props[x];
735             var comment = $.rdfwidgets.any( prop, db.sym("http://www.w3.org/2000/01/rdf-schema#comment"), undefined, undefined, o );
736             binding.uri = prop.uri;
737             binding.value = doLabel( prop, o );
738 
739             if( comment ) {
740                 binding.comment = comment.value;
741             } else  {
742                 binding.comment = prop.value;
743             }
744 
745             binding.label = "<span title='"+binding.comment+" "+binding.uri+"'>"+binding.value+"</span>";
746             valueToURI[binding.value] = prop;
747             uriToValue[prop] = binding.value;
748         }
749         return {bindings:bindings, valueToURI:valueToURI, uriToValue:uriToValue};
750     }
751 
752     function getMenuNoOptions() {
753         return {bindings:[],valueToURI:{},uriToValue:{}};
754     }
755 
756     function getMenuUsedOptions( o ) {
757         var used = new Array();
758         used = $.rdfwidgets.statementsMatching( undefined ,  db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), o.type ? processObject(o.type) : undefined, undefined, false, o);
759 
760         used = filterDuplicates( used );
761 
762         var uriToValue = {};
763         var valueToURI = {};
764         var bindings = [];
765 
766         for( x in used ) {
767             var st = used[x];
768             var prop;
769             if( o.editTerm ) {
770                 prop = st[o.editTerm];
771             }else if( !o.subject ) {
772                 prop = st.subject;
773             } else if( !o.object ) {
774                 prop = st.object;
775             } else if( !o.predicate ) {
776                 prop = st.predicate;
777             }
778             if( uriToValue[prop] || prop.termType === "bnode" ) {
779                 continue;
780             }
781             var binding = {};
782             bindings.push( binding );
783             var comment = $.rdfwidgets.any( prop, db.sym("http://www.w3.org/2000/01/rdf-schema#comment"), undefined, undefined, o );
784             binding.uri = prop.uri;
785             binding.value = doLabel( prop, o );
786 
787             if( comment ) {
788                 binding.comment = comment.value;
789             } else  {
790                 binding.comment = prop.value;
791             }
792 
793             binding.label = "<span title='"+binding.comment+" "+binding.uri+"'>"+binding.value+"</span>";
794             valueToURI[binding.value] = prop;
795             uriToValue[prop] = binding.value;
796         }
797 
798         return {bindings:bindings, valueToURI:valueToURI, uriToValue:uriToValue};
799     }
800 
801     function getMenuInstanceOptions( o ) {
802         var used = new Array();
803         if( o.predicate ) {
804             if( $.rdfwidgets.whether(processResource( o.predicate, o ), db.sym("http://www.w3.org/2000/01/rdf-schema#range" ), db.sym("http://www.w3.org/2000/01/rdf-schema#Literal"), undefined, o ) ) {
805                 return {bindings:[],valueToURI:{},uriToValue:{}};
806             }
807             var p = allSubProperties( o.predicate, o );
808             var used = [];
809             var ranges = [];
810             for( x in p ) {
811                 used = used.concat( $.rdfwidgets.statementsMatching( undefined, p[x], undefined, undefined, false, o ) );
812                 if( o.editTerm === "subject" ) {
813                     ranges = ranges.concat($.rdfwidgets.each( p[x], db.sym("http://www.w3.org/2000/01/rdf-schema#domain"), undefined, undefined, o ));
814                 }else if( o.editTerm === "object" ) {
815                     ranges = ranges.concat($.rdfwidgets.each( p[x], db.sym("http://www.w3.org/2000/01/rdf-schema#range"), undefined, undefined, o ));
816                 }
817             }
818         }
819         
820         var props = [];
821         if( ranges.length > 0 ) {
822             for( var i = 0; i < ranges.length; i++ ) {
823                 var r = allSubClasses( ranges[i], o );
824                 for( x in r ) {
825                     props = props.concat( $.rdfwidgets.statementsMatching( undefined, db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), r[x], undefined, false, o) );
826                 }
827             }
828         } else {
829             props = $.rdfwidgets.statementsMatching( undefined, db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), undefined, undefined, false, o );
830         }
831 
832 
833         var uriToValue = {};
834         var valueToURI = {};
835         var bindings = [];
836         for( x in props ) {
837             var st = props[x];
838             var prop = st.subject;
839             if( $.rdfwidgets.whether( st.subject, db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#Property" ), undefined, o ) || uriToValue[st.subject] || st.subject.termType === "bnode") {
840                 continue;
841             }
842             var binding = {};
843             bindings.push( binding );
844             var comment = $.rdfwidgets.any( prop, db.sym("http://www.w3.org/2000/01/rdf-schema#comment"), undefined, undefined, o );
845             binding.uri = prop.uri;
846             binding.value = doLabel( prop, o );
847 
848             if( comment ) {
849                 binding.comment = comment.value;
850             } else  {
851                 binding.comment = prop.value;
852             }
853 
854             binding.label = "<span title='"+binding.comment+" "+binding.uri+"'>"+binding.value+"</span>";
855             valueToURI[binding.value] = prop;
856             uriToValue[prop] = binding.value;
857         }
858 
859 
860         for( x in used ) {
861             var st = used[x];
862             var prop;
863             if( o.editTerm === "subject" ) {
864                 prop = st.subject;
865             } else {
866                 prop = st.object;
867             }
868             if( uriToValue[prop] || prop.termType === "bnode" ) {
869                 continue;
870             }
871             var binding = {};
872             bindings.push( binding );
873             var comment = $.rdfwidgets.any( prop, db.sym("http://www.w3.org/2000/01/rdf-schema#comment"), undefined, undefined, o );
874             binding.uri = prop.uri;
875             binding.value = doLabel( prop, o );
876 
877             if( comment ) {
878                 binding.comment = comment.value;
879             } else  {
880                 binding.comment = prop.value;
881             }
882 
883             binding.label = "<span title='"+binding.comment+" "+binding.uri+"'>"+binding.value+"</span>";
884             valueToURI[binding.value] = prop;
885             uriToValue[prop] = binding.value;
886         }
887 
888         return {bindings:bindings, valueToURI:valueToURI, uriToValue:uriToValue};
889     }
890 
891 
892     /* find the endpoint that you should write to for a given data source */
893     function getEndpoint( why, o ) {
894         if( o.endpoint ) {
895             return o.endpoint;
896         } else if ( why && why.uri && endpoints[why] ) {
897             return endpoints[why];
898         } else if ( defaultEndpoint ) {
899             return defaultEndpoint;
900         } else {
901             return window.location;
902         }
903     }
904 
905     function doInsert( triples, callback, o ){ 
906         if( !triples || triples.length == 0 ) { callback( true );return; }
907         var endpoint = getEndpoint( triples[0], o );
908         var queryString = "INSERT ";
909         var postData = {};
910 
911         var graph;
912         if( o.acl ) {
913             postData['acl'] = processACL( o, triples[0].why.uri );
914             graph = processGraph( o, triples[0].why.uri );
915         } else {
916             graph = processGraph( o );
917         }
918         if( graph ) {
919             queryString += "INTO <" + graph + "> ";
920         }
921 
922         queryString += " { ";
923         for( x in triples ) { queryString += triples[x].toString(); }
924         queryString += " } ";
925         postData['query']=queryString;
926         $.ajax({
927             type:"POST",
928             data:postData,
929             url :endpoint,
930             beforeSend: function( xhr ) {
931                 if( xhr.withCredentials !== undefined ) {
932                     xhr.withCredentials = true;
933                 }
934                 if( o.beforeSubmit ) {
935                     o.beforeSubmit( triples, xhr );
936                 }
937             },
938             success: function(data, stat, xhr) {
939                 var sp, d;
940                 for( var i = 0; i < data.childNodes.length; i++ ) {
941                     var sp = data.childNodes[i];
942                     if( sp.nodeName === "sparql" ) {
943                         for( var j = 0; j < sp.childNodes.length; j++ ) {
944                             var d = sp.childNodes[j];
945                             if( d.nodeName === "inserted" ) {
946                                 var val =$(d).text();
947                                 for( var t = 0; t < triples.length; t++ ) {
948                                     db.add( triples[t].subject, triples[t].predicate, triples[t].object, triples[t].why );
949                                 }
950                                 callback( true, val );
951                                 insertData( triples );
952                                 if( o.afterSubmit ) {
953                                     o.afterSubmit( true, triples, xhr );
954                                 }
955                                 return;
956                             }
957                         }
958                     }
959                 }
960                 callback( false );
961                 if( o.afterSubmit ) {
962                     o.afterSubmit( false, triples, xhr );
963                 }
964             },
965             error: function(xhr, t, err) {
966                 callback( false );
967                 if( o.afterSubmit ) {
968                     o.afterSubmit( false, null, xhr );
969                 }
970             },
971             dataType:"xml"
972         });
973     }
974 
975     function doDelete( triples, callback, o ){ 
976         if( !triples || triples.length == 0 ) { callback( true );return; }
977         var endpoint = getEndpoint( triples[0], o );
978         var queryString = "DELETE ";
979         var postData = {};
980         var graph;
981         if( o.acl ) {
982             postData['acl'] = processACL( o, triples[0].why.uri );
983             graph = processGraph( o, triples[0].why.uri );
984         } else {
985             graph = processGraph( o );
986         }
987         if( graph ) {
988             queryString += "FROM <" + graph + "> ";
989         }
990 
991         queryString += " { ";
992         for( x in triples ) { queryString += triples[x].toString(); }
993         queryString += " } ";
994         postData['query']=queryString;
995 
996         $.ajax({
997             type:"POST",
998             data:postData,
999             url :endpoint,
1000             beforeSend: function( xhr ) {
1001                 if( xhr.withCredentials !== undefined ) {
1002                     xhr.withCredentials = true;
1003                 }
1004                 if( o.beforeSubmit ) {
1005                     o.beforeSubmit( triples, xhr );
1006                 }
1007             },
1008             success: function(data, stat, xhr) {
1009                 var sp, d;
1010                 for( var i = 0; i < data.childNodes.length; i++ ) {
1011                     var sp = data.childNodes[i];
1012                     if( sp.nodeName === "sparql" ) {
1013                         for( var j = 0; j < sp.childNodes.length; j++ ) {
1014                             var d = sp.childNodes[j];
1015                             if( d.nodeName === "deleted" ) {
1016                                 var val =$(d).text();
1017                                 for( var t = 0; t < triples.length; t++ ) {
1018                                     db.remove( triples[t] );
1019                                 }
1020                                 callback( true, val );
1021                                 deleteData( triples );
1022                                 if( o.afterSubmit ) {
1023                                     o.afterSubmit( true, triples, xhr );
1024                                 }
1025                                 return;
1026                             }
1027                         }
1028                     }
1029                 }
1030                 callback( false );
1031                 if( o.afterSubmit ) {
1032                     o.afterSubmit( false, triples, xhr );
1033                 }
1034             },
1035             error: function(xhr, stat, err) {
1036                 callback( false );
1037                 if( o.afterSubmit ) {
1038                     o.afterSubmit( false, null, xhr );
1039                 }
1040             },
1041             dataType:"xml"
1042         });
1043     }
1044     
1045     //when i feel confident that its being implemented, this will use the w3c spec which allows multiple update actions in one query
1046     function doUpdate( triples, callback,  o ) {
1047         doDelete( triples['delete_triples'], function( success ) {
1048                 if( success ) {
1049                     doInsert( triples['insert_triples'], callback, o );
1050                 } else {
1051                     callback( false );
1052                 }
1053             }, o );
1054     }
1055 
1056     function doLabel( term, o ) {
1057         if( labelCache[term] ) {
1058             return labelCache[term];
1059         }
1060         if( term.termType === "literal" ) {
1061             return term.value;
1062         }
1063         var label = null;
1064         var t = term.value;
1065         if( !t ) { return ""; }
1066         var p = db.sym( "http://www.w3.org/2000/01/rdf-schema#label" );
1067         //var pat = $.rdf.pattern( s,p,o );
1068         var r = $.rdfwidgets.statementsMatching( term, p, undefined, undefined, false );
1069         if( r.length > 0 ) {
1070             for( var i = 0; i < r.length; i++ ) {
1071                 if( !r[i].lang || (r[i].lang && langs[r[i].lang]) ) {
1072                     label = r[i].object.toString();
1073                     break;
1074                 }
1075             }
1076         }
1077         if( !label ) {
1078             p = db.sym("http://purl.org/dc/terms/title");
1079             r = $.rdfwidgets.statementsMatching( term, p, undefined, undefined, false );
1080             if( r.length > 0 ) {
1081                 for( var i = 0; i < r.length; i++ ) {
1082                     if( !r[i].lang || (r[i].lang && langs[r[i].lang]) ) {
1083                         label = r[i].object.toString();
1084                         break;
1085                     }
1086                 }
1087             }
1088         }
1089         if( !label ) {
1090             p = db.sym("http://xmlns.com/foaf/0.1/name");
1091             r = $.rdfwidgets.statementsMatching( term, p, undefined, undefined, false );
1092             if( r.length > 0 ) {
1093                 for( var i = 0; i < r.length; i++ ) {
1094                     if( !r[i].lang || (r[i].lang && langs[r[i].lang]) ) {
1095                         label = r[i].object.toString();
1096                         break;
1097                     }
1098                 }
1099             }
1100         }
1101         if( !label ) {
1102             var li = t.lastIndexOf( "#" );
1103             if( li !== -1 && li < t.length-2 ) {
1104                     label = t.substr(li+1);
1105             } else {
1106                 li = t.lastIndexOf( "/" );
1107                 if( (li > 0 && t[li-1] !== "/") && li < t.length-2 ) {
1108                     label = t.substr(li+1);
1109                 } else {
1110                     label = t;
1111                 }
1112             }
1113         }
1114         labelCache[term]=label;
1115         return label;
1116     }
1117 
1118     var imageProperties = null;
1119     var imageCache = {};
1120 
1121     function findImageProperties() {
1122         q = [db.sym("http://xmlns.com/foaf/0.1/depiction"),db.sym("http://dbpedia.org/property/img")/*,db.sym("http://xmlns.com/foaf/0.1/img")*/];
1123         imageProperties = {};
1124         var current = null;
1125         var sp = db.sym( "http://www.w3.org/2000/01/rdf-schema#subPropertyOf" );
1126         while( q.length > 0 ) {
1127             current = q[0];
1128             q = q.slice(1);
1129             if( imageProperties[current.value] ) {
1130                 continue;
1131             } else {
1132                 var extras = $.rdfwidgets.each( undefined, sp, current );
1133                 for( var i = 0; i < extras.length; i++ ) {
1134                     if( !imageProperties[extras[i].value] ) {
1135                         q.push( extras[i] );
1136                     }
1137                 }
1138                 imageProperties[current.value] = 1;
1139             }
1140         }
1141     }
1142 
1143 
1144 
1145     function doImage( term, o ) {
1146         if( !imageProperties ) {
1147             findImageProperties();
1148         }
1149 
1150         var sts = $.rdfwidgets.statementsMatching( term,undefined,undefined,undefined,false,o );
1151         for( var i = 0; i < sts.length; i++ ) {
1152             if( imageProperties[sts[i].predicate.value] && sts[i].object.termType === "symbol" ) {
1153                 imageCache[term.value] = sts[i].object;
1154                 return sts[i].object;
1155             }
1156         }
1157         return null;
1158     }
1159 
1160     function isImage( term, o ) {
1161         if( !imageProperties ) {
1162             findImageProperties();
1163         }
1164 
1165         if( $.rdfwidgets.anyStatementMatching( term, db.sym( "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ), db.sym( "http://xmlns.com/foaf/0.1/Image" ), undefined, o ) ) {
1166             return term;
1167         }
1168 
1169         var sts = $.rdfwidgets.statementsMatching( undefined, undefined, term, undefined, false, o );
1170         for( var i = 0; i < sts.length; i++ ) {
1171             if( imageProperties[sts[i].predicate.value] ) {
1172                 return sts[i].object;
1173             }
1174         }
1175         return null;
1176     }
1177 
1178     function doAutocomplete( jq, o ) {
1179         var options;
1180         
1181         if( o.menuOptions !== null && o.menuOptions !== undefined ) {
1182             options = o.menuOptions;
1183         } else if( o.editTerm === "object" && $.rdfwidgets.whether(processResource( o.predicate, o ), db.sym("http://www.w3.org/2000/01/rdf-schema#range" ), db.sym("http://www.w3.org/2000/01/rdf-schema#Literal"), undefined, o ) ) {
1184             jq.bind("blur", function() { jq.trigger("autocompletechoice") } );
1185             return;
1186         } else if ( (o.editTerm === "object" || o.editTerm === "subject") ) {
1187             options = getMenuInstanceOptions( o );
1188         } else if ( o.editTerm === "predicate" ) {
1189             options = getMenuPredicateOptions( o );
1190         }  else {
1191             options = {bindings:[],valueToURI:{},uriToValue:{}};
1192         }
1193         var preds = options.bindings;
1194         var open = false;
1195         var passedUp = false;
1196         var selected = false;
1197         var lastSelect = null;
1198 
1199         function getEvent( val ) {
1200             //perform label-> uri OR uri -> label.
1201             if( lastSelect && lastSelect.value === jq.val() ) {
1202                 return { type: "autocompletechoice", uri:lastSelect.uri,  input: jq.val(), label:lastSelect.value };                
1203             } else if ( validURI( jq.val() ) ) {
1204                 return { type: "autocompletechoice", uri:jq.val(), input: jq.val(), label:( options.uriToValue[ jq.val() ] ? options.uriToValue[ jq.val() ] : jq.val() ) };
1205             } else if ( options.valueToURI[jq.val()] ) {
1206                 return { type: "autocompletechoice", uri: options.valueToURI[jq.val()],input: jq.val(),  label:jq.val() };
1207             } else if ( validURI( jq.val() ) ){
1208                 return { type: "autocompletechoice", uri: jq.val(),input: jq.val(),  label:jq.val() };
1209             } else {
1210                 return { type: "autocompletechoice", uri: null,input: jq.val(),  label:jq.val() };
1211             }
1212         }
1213 
1214         return jq.autocomplete("destroy").autocomplete({
1215                     source: preds,
1216                     open: function() {
1217                       open = true; 
1218                       passedUp = false;
1219                       selected = false;
1220                     },
1221                     close: function() { 
1222                       open = false;
1223                       if( passedUp && !selected ) {
1224                           var evt = getEvent( jq.val() );
1225                           if( evt.uri ) {
1226                               jq.data( "uri", evt.uri );
1227                               jq.data( "val", evt.input );
1228                           } else {
1229                               jq.data( "uri", null );
1230                               jq.data( "val", evt.input );
1231                           }
1232                           $(this).trigger( evt ); 
1233                       }
1234                     },
1235                     focus: function( e, ui ) {
1236                       lastSelect = ui.item;
1237                     },
1238                     select: function( e, ui ) {
1239                       selected = true;
1240                       lastSelect = ui.item;
1241                     }
1242                }).blur( function() { 
1243                     if( !open ) {
1244                         var evt = getEvent( jq.val() );
1245                         if( evt.uri ) {
1246                             jq.data( "uri", evt.uri );
1247                             jq.data( "val", evt.input );
1248                         } else {
1249                             jq.data( "uri", null );
1250                             jq.data( "val", evt.input );
1251                         }
1252                         $(this).trigger( evt ); 
1253                     } else {
1254                         passedUp = true; 
1255                     } 
1256                });
1257     }
1258 
1259     function processTripleUpdate( term, original, o ) {
1260         var delete_triples = null;
1261         var insert_triples = null;
1262         if( o.editTerm === "subject" ) {
1263             var pred = processResource( o.predicate, o );
1264             var obj = processObject( o.object, o );
1265             if( original ) { delete_triples = $.rdfwidgets.statementsMatching( original, pred, obj,undefined,false,o ); }
1266             if( term ) {
1267                 insert_triples = [ new $rdf.Statement( term, pred, obj ) ];
1268                 if( delete_triples && delete_triples[0] && delete_triples[0].why ) { insert_triples[0].why = delete_triples[0].why; }
1269             }
1270         } else if ( o.editTerm === "predicate" ) {
1271             var subj = processResource( o.subject, o );
1272             var obj = processObject( o.object, o );
1273             if( original ) { delete_triples = $.rdfwidgets.statementsMatching( subj, original, obj,undefined,false,o ); }
1274             if( term ) {
1275                 insert_triples = [ new $rdf.Statement( subj, term, obj ) ]; 
1276                 if( delete_triples && delete_triples[0] && delete_triples[0].why ) { insert_triples[0].why = delete_triples[0].why; }
1277             }
1278         } else if ( o.editTerm === "object" ) {
1279             var subj = processResource( o.subject, o );
1280             var pred = processResource( o.predicate, o );
1281             if( original ) { delete_triples = $.rdfwidgets.statementsMatching( subj, pred, original,undefined,false,o ); }
1282             if( term ) {
1283                 insert_triples = [ new $rdf.Statement( subj, pred, term ) ];
1284                 if( delete_triples && delete_triples[0] && delete_triples[0].why ) { insert_triples[0].why = delete_triples[0].why; }
1285             }
1286         }
1287         if( !delete_triples ) { delete_triples = new Array(); }
1288         if( !insert_triples ) { insert_triples = new Array(); }
1289         return { delete_triples: delete_triples, insert_triples:insert_triples }
1290     }
1291 
1292     function setUnselected( element ) {
1293         if( element && element === selectedElement ) {
1294             selectedElement = null;
1295             element.removeClass( 'selected' );
1296         }
1297     }
1298 
1299     function setSelected( element, term ) {
1300         setUnselected( selectedElement );
1301         if( element ) {
1302             selectedElement = element;
1303             element.addClass( 'selected' );
1304             if( term ) {
1305                 element.trigger( 'rdfselect', [term] );
1306             }
1307         }
1308     }
1309 
1310     function checkSelected( element ) {
1311         return element === selectedElement;
1312     }
1313 
1314 
1315 
1316     function addDelayedClickEvent( element  ) {
1317         var waiting = false;
1318         function firstClick( e ) {
1319             e.stopPropagation();
1320             if( !waiting ) {
1321                 waiting = true;
1322                 setSelected( element, element.label('getRDFValue'));
1323                 setTimeout( function() {
1324                         waiting = false;
1325                         element.unbind( "click", firstClick );
1326                         element.bind( "click", secondClick );
1327                     }, 1000);
1328             }
1329         }
1330         function secondClick( e ) {
1331             e.stopPropagation();
1332             if( checkSelected( element ) ) {
1333                 element.trigger( "delayedclick" );
1334             } else {
1335                 element.bind( "click", firstClick );
1336                 element.unbind( "click", secondClick );
1337                 firstClick( e );
1338             }
1339         }
1340         element.bind( "click", firstClick );
1341     }
1342                                                                                                                                                                                                   /*    function addDelayedClickEvent( element  ) {
1343         function firstClick( e ) {
1344             e.stopPropagation();
1345             setSelected( element);
1346             element.trigger( "rdfselect",[element.rdflabel('getRDFValue')] );
1347             setTimeout( function() {
1348                     element.one( "click", secondClick );
1349                 }, 1000);
1350         }
1351         function secondClick( e ) {
1352             e.stopPropagation();
1353             if( checkSelected( element ) ) {
1354                 element.one( "click", firstClick );
1355                 element.trigger( "delayedclick" );
1356             } else {
1357                 firstClick( e );
1358             }
1359         }
1360         element.one( "click", firstClick );
1361         }*/
1362 
1363 
1364     /**
1365      * @name jQuery.prototype.edit
1366      * @description Draw a label that, when clicked twice, transforms into an editable text box for modifying a value in a triple.
1367      * @function
1368      * @param {String | Object} [options.subject] The resource to display an edit for OR if predicate or object is defined, the subject of a statement to be matched.
1369      * @param {String | Object} [options.predicate] The predicate of a statement to be matched.
1370      * @param {String | Object} [options.object] The object of a statement to be matched.
1371      * @param {Boolean} [options.autocomplete=true] If false, no autocomplete menu will be displayed when the user is editing.
1372      * @param {Object} [options] @see CommonOptions
1373      */
1374     $.rdfwidget("ui.edit", {
1375         options: {
1376             namespaces:namespaces,
1377             showlinks:false,
1378             autocomplete:true,
1379             usetextarea:false,
1380             editable:"delayedclick"
1381         },
1382         _create: function() {
1383             this.originalSubject = this.options.subject;
1384             this.originalPredicate = this.options.predicate;
1385             this.originalObject = this.options.object;
1386             this.refresh();
1387         },
1388         reset: function() {
1389             this.element.edit('option','subject',this.originalSubject);
1390             this.element.edit('option','predicate',this.originalPredicate);
1391             this.element.edit('option','object',this.originalObject);
1392             refresh();
1393         },
1394         getTriple: function() {
1395             var o = this.options;
1396             var t = this.element.label( "getTriple" );
1397             if( t ) {
1398                 return t;
1399             }
1400             if( o.subject && o.predicate && o.object ) {
1401                 var trip = $.rdfwidgets.anyStatementMatching( processResource(o.subject,o),processResource(o.predicate,o),processObject(o.object,o), undefined, o );
1402                 return trip ? trip : null;
1403             }
1404             return null;
1405         },
1406 
1407         refresh: function() {
1408             var acdone = false;
1409             var o = this.options;
1410             var element = this.element;
1411             processEditTerm( o );
1412             element.label(o);
1413             addDelayedClickEvent( element );
1414             if( false !== o.editable && "never" !== o.editable ) {
1415                 var original = element.label("getRDFValue");
1416                 var inputbox;
1417                 function insertAC( e ) {
1418                     if( !inputbox ) {
1419                         if( o.usetextarea ) {
1420                             inputbox = $("<textarea class='rdfedit'></textarea>");
1421                         } else {
1422                             inputbox = $("<input style='margin:0; padding:0; border: 1px solid black;' class='rdfedit' type='text'></input>");
1423                         }
1424                     }
1425                     if( !acdone ) {
1426                         doAutocomplete( inputbox, o );
1427                     }
1428                     inputbox.width( element.width()-2 );
1429 //                    if( o.usetextarea ) {
1430                     inputbox.height( element.height()-2 );
1431 //                    }
1432                     if( element.edit('option','disabled')) {
1433                         element.one('delayedclick', insertAC );
1434                         return;
1435                     }
1436                     var oldText;
1437                     if( original ) {
1438                         inputbox.val(element.text());
1439                         oldText = element.text();
1440                     } else {
1441                         oldText = "";
1442                     }
1443                     element.empty().append( inputbox );
1444                     inputbox.select();
1445                     function insertLabel(e) {
1446                         var i = inputbox.val();
1447                         var choice;
1448                         var label;
1449                         if( i === "" || i === oldText ) {
1450                             element.empty().label(o).one('delayedclick', insertAC );
1451                             return;
1452                         }
1453                         if( !e.uri ) {
1454                             if( !o.autocomplete ) {
1455                                 if( validURI( i ) ) {
1456                                     choice = processResource( i, o );
1457                                 } else if ( o.editTerm === "object" ) {
1458                                     choice = processLiteral( i, o );
1459                                 }
1460                             } else if (o.editTerm === "object" ) {
1461                                 choice = processLiteral( i, o );
1462                             }
1463                             label = i;
1464                         } else {
1465                             label = e.label;
1466                             choice = processResource( e.uri, o );
1467                         }
1468                         if( choice ) {
1469                             var data = processTripleUpdate( choice, original, o );
1470                             doUpdate( data, function( success ) {
1471                                     if( success ) {
1472                                         element.edit("option",o.editTerm,choice.value);
1473                                         element.empty().label(o).one('delayedclick', insertAC );
1474                                         original = choice;
1475                                     } else {
1476                                         alert( "Sorry, the server responded to your edit request with an error." );
1477                                         element.empty().label(o).one('delayedclick', insertAC );
1478                                     }
1479                                 }, o);
1480                         } else {
1481                             alert( "Sorry, the input you provided was not valid. Please choose a menu option or enter a valid URL.");
1482                             inputbox.one("autocompletechoice", insertLabel );
1483                             inputbox.focus();
1484                         }
1485                     }
1486                     inputbox.one("autocompletechoice", insertLabel );
1487                     if( !o.autocomplete ) {
1488                         o.menuOptions = [];
1489                     }
1490                     inputbox.focus();
1491                 }
1492                 element.one('delayedclick', insertAC );
1493             }
1494         }
1495     });
1496 
1497     $.ui.edit.getter = "getTriple";
1498 
1499     /**
1500      * @name jQuery.prototype.tripleedit
1501      * @description Draw a series of editable boxes for editing each term in a single triple.
1502      * @function
1503      * @param {String | Object} [options.subject] The subject of a statement to be matched.
1504      * @param {String | Object} [options.predicate] The predicate of a statement to be matched.
1505      * @param {String | Object} [options.object] The object of a statement to be matched.
1506      * @param {Boolean} [options.autocomplete=true] If false, no autocomplete menu will be displayed.
1507      * @param {Object} [options] @see CommonOptions
1508      */
1509     $.rdfwidget("ui.tripleedit", {
1510         options: {
1511             namespaces:namespaces,
1512             showlinks:false,
1513             autocomplete:true
1514         },
1515         _create: function() {
1516             this.refresh();
1517         },
1518 
1519         disable: function() {
1520             var o = this.options;
1521             if( o.subjectElement ) {
1522                 $(o.subjectElement).edit('disable');
1523             }
1524             if( o.predicateElement ) {
1525                 $(o.predicateElement).edit('disable');
1526             }
1527             if( o.objectElement ) {
1528                 $(o.objectElement).edit('disable');
1529             }
1530         },
1531 
1532         enable: function() {
1533             var o = this.options;
1534             if( o.subjectElement ) {
1535                 $(o.subjectElement).edit('enable');
1536             }
1537             if( o.predicateElement ) {
1538                 $(o.predicateElement).edit('enable');
1539             }
1540             if( o.objectElement ) {
1541                 $(o.objectElement).edit('enable');
1542             }
1543         },
1544         getTriple: function() {
1545             var o = this.options;
1546             if( o.subjectElement ) {
1547                 return $(o.subjectElement).edit('getTriple');
1548             } else if( o.predicateElement ) {
1549                 return $(o.predicateElement).edit('getTriple');
1550             } else if( o.objectElement) {
1551                 return $(o.objectElement).edit('getTriple');                
1552             }
1553             return null;
1554         },
1555 
1556         refresh: function() {
1557             var o = this.options;
1558             var element = this.element;
1559             var that = this;
1560             if( ! (o.subjectElement || o.predicateElement || o.objectElement)) {
1561                 element.empty();
1562             }
1563 
1564             var beforeSubmit = function(data, xhr) {
1565                 that.disable();
1566                 if( o.beforeSubmit ) {
1567                     o.beforeSubmit( data, xhr );
1568                 }
1569             }
1570 
1571             var afterSubmit = function(success, data, xhr) {
1572                 if( success && data.insert_triples && data.insert_triples[0] ) {
1573                     element.tripleedit('option','subject',data.insert_triples[0].subject);
1574                     element.tripleedit('option','predicate',data.insert_triples[0].predicate);
1575                     element.tripleedit('option','object',data.insert_triples[0].object);
1576                 }
1577                 if( o.afterSubmit ) {
1578                     o.afterSubmit( success, data, xhr );
1579                 }
1580                 that.enable();
1581             }
1582 
1583             var subjectopts = {};
1584             var newopts = {editTerm:'subject',beforeSubmit:beforeSubmit, afterSubmit:afterSubmit}
1585             $.extend( subjectopts, o, newopts );
1586             if( o.subjectElement === undefined ) {
1587                 element.tripleedit('option','subjectElement',$('<div class="rdftripleedit"></div>'));
1588                 element.append( o.subjectElement );
1589             }
1590 
1591             var predopts = {};
1592             newopts = {editTerm:'predicate',beforeSubmit:beforeSubmit, afterSubmit:afterSubmit}
1593             $.extend( predopts, o, newopts );
1594             if( o.predicateElement === undefined ) {
1595                 element.tripleedit('option','predicateElement',$('<div class="rdftripleedit"></div>'));
1596                 element.append( o.predicateElement );
1597             }
1598             var objectopts = {};
1599             newopts = {editTerm:'object',beforeSubmit:beforeSubmit, afterSubmit:afterSubmit}
1600             $.extend( objectopts, o, newopts );
1601             if( o.objectElement === undefined ) {
1602                 element.tripleedit('option','objectElement',$('<div class="rdftripleedit"></div>'));
1603                 element.append( o.objectElement );
1604             }
1605             if( o.subjectElement ) {
1606                 $(o.subjectElement).edit(subjectopts);
1607             }
1608             if( o.predicateElement ) {
1609                 $(o.predicateElement).edit(predopts);
1610             }
1611             if( o.objectElement ) {
1612                 $(o.objectElement).edit(objectopts);
1613             }
1614 
1615         }
1616 
1617     });
1618 
1619     $.ui.tripleedit.getter = "getTriple";
1620 
1621     /**
1622      * @name jQuery.prototype.selecttable
1623      * @description Perform a SPARQL SELECT query with a set of properties and display the results in a table.
1624      * @function
1625      * @param {String | Object} [options.type] If specified, the required rdf:type of each result in the list.
1626      * @param {Array} [options.properties] If specified, the set of properties to be SELECTed for.
1627      * @param {Array} [options.requireall=true] If false, matches that bind at least one item in options.properties will be displayed.
1628      * @param {Object} [options] @see CommonOptions
1629      */
1630     $.rdfwidget("ui.selecttable", {
1631         options: {
1632             namespaces:namespaces,
1633             showlinks:false,
1634             editable:true,
1635             autocomplete:true,
1636             subject:window.location,
1637             requireall:true,
1638             properties:[]
1639         },
1640         _create: function() {
1641             this.refresh();
1642         },
1643 
1644         disable: function() {
1645         },
1646 
1647         enable: function() {
1648         },
1649 
1650         refresh: function() {
1651             var o = this.options;
1652             var element = this.element;
1653             var that = this;
1654 
1655             var optsArray = {};
1656             var properties = [];
1657             for( var i = 0; i < o.properties.length; i++ ) {
1658                 properties.push( processResource( o.properties[i], o ) );
1659             }
1660 
1661             function doHeader( ) {
1662                 var str = "<thead><tr class='rdfeditheader'><th></th>";
1663                 for( var i = 0; i < properties.length; i++ ) {
1664                     var id = randomID();
1665                     var cell = "<div class='rdfeditheader' id='"+id+"'></div>";
1666                     var cellopts = {};
1667                     var tripopts = {subject: properties[i], predicate:null, object:null, selectable:false, editable:false};
1668                     $.extend( cellopts, o, tripopts );
1669                     optsArray[id]= cellopts;
1670                     td+=cell;
1671                     str += "<th class='rdfeditheader'>"+cell+"</th>";
1672                 }
1673                 str += "</tr></thead>";
1674                 return str;
1675             }
1676 
1677             var html = "<table class='rdfselecttable'><tbody>";
1678             html += doHeader();
1679             if( o.type ) {
1680                 var t = processResource( o.type, o );
1681                 var subjects = $.rdfwidgets.each( undefined, db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), t,undefined, o );
1682                 for( var i = 0; i < subjects.length; i++ ) {
1683                     var row = "<tr>";
1684                     var subject = subjects[i];
1685                     //todo: row header.
1686                     var id = randomID();
1687                     var td = "<td class='rdfeditheader'>";
1688                     var cell = "<div  class='rdfeditheader' id='"+id+"'></div>";
1689                     var cellopts = {};
1690                     var tripopts = {subject: subject, predicate:null, object:null, selectable:false, editable:false};
1691                     $.extend( cellopts, o, tripopts );
1692                     optsArray[id]= cellopts;
1693                     td += cell + "</td>";
1694                     row += td;
1695                     var fail = false;
1696                     for( var j = 0; j < properties.length; j++ ) {
1697                         td = "<td>";
1698                         var objects = $.rdfwidgets.each( subject, properties[j],undefined,undefined,o );
1699                         if( (!objects || objects.length == 0)) {
1700                             if( o.requireall ) {
1701                                 fail = true;
1702                                 break;
1703                             } else {
1704                                 id = randomID();
1705                                 cell = "<div id='"+id+"'></div>";
1706                                 cellopts = {};
1707                                 tripopts = {subject: subject, predicate:properties[j]};
1708                                 $.extend( cellopts, o, tripopts );
1709                                 optsArray[id] = cellopts;
1710                                 td += cell;
1711                             }
1712                         }
1713                         for( var k = 0; k < objects.length; k++ ) {
1714                             id = randomID();
1715                             cell = "<div id='"+id+"'></div>";
1716                             cellopts = {};
1717                             tripopts = {subject: subject, predicate:properties[j], object:objects[k]};
1718                             $.extend( cellopts, o, tripopts );
1719                             optsArray[id] = cellopts;
1720                             td += cell;
1721                         }
1722                         td+="</td>";
1723                         row += td;
1724                     }
1725                     if( !o.requireall || !fail ) {
1726                         row += "</tr>";
1727                         html += row;
1728                     }
1729                 }
1730             } else {
1731                 var done = {};
1732                 var total = 0;
1733                 for( var h = 0; ((h < properties.length && !o.requireall) || (o.requireall && h < 1)) ; h++ ) {
1734                     var tempsubjects = $.rdfwidgets.statementsMatching( undefined, properties[h],undefined,undefined,false,o);
1735                     var subjects = [];
1736                     var count = total;
1737                     for( var y= 0; y < tempsubjects.length; y++ ) {
1738                         if( !done[tempsubjects[y].subject] ) {
1739                             subjects.push(tempsubjects[y].subject);
1740                             done[tempsubjects[y].subject]=true;
1741                             total++;
1742                         }
1743                     }
1744                     for( var i = 0; i < subjects.length; i++ ) {
1745                         var fail = false;
1746                         var row = "<tr>";
1747                         var subject = subjects[i];
1748                         //todo: row header.
1749                         var id = randomID();
1750                         var td = "<td class='rdfeditheader'>";
1751                         var cell = "<div class='rdfeditheader' id='"+id+"'></div>";
1752                         var cellopts = {};
1753                         var tripopts = {subject: subject, predicate:null, object:null, selectable:false, editable:false};
1754                         $.extend( cellopts, o, tripopts );
1755                         optsArray[id]= cellopts;
1756                         td += cell + "</td>";
1757                         row += td;
1758                         for( var j = 0; j < h; j++ ) {
1759                             row += "<td>";
1760                             id = randomID();
1761                             cell = "<div id='"+id+"'></div>";
1762                             cellopts = {};
1763                             tripopts = {subject: subject, predicate:properties[j]};
1764                             $.extend( cellopts, o, tripopts );
1765                             row+=cell+ "</td>";
1766                             optsArray[id] = cellopts;
1767                         }
1768                         for( var j = h; j < properties.length; j++ ) {
1769                             td = "<td>";
1770                             var objects = $.rdfwidgets.each( subject, properties[j],undefined,undefined,o );
1771                             if( (!objects || objects.length == 0)) {
1772                                 if( o.requireall ) {
1773                                     fail = true;
1774                                     break;
1775                                 } else {
1776                                     id = randomID();
1777                                     cell = "<div id='"+id+"'></div>";
1778                                     cellopts = {};
1779                                     tripopts = {subject: subject, predicate:properties[j]};
1780                                     $.extend( cellopts, o, tripopts );
1781                                     optsArray[id] = cellopts;
1782                                     td += cell;
1783                                 }
1784                             }
1785                             for( var k = 0; k < objects.length; k++ ) {
1786                                 id = randomID();
1787                                 cell = "<div id='"+id+"'></div>";
1788                                 cellopts = {};
1789                                 tripopts = {subject: subject, predicate:properties[j], object:objects[k]};
1790                                 $.extend( cellopts, o, tripopts );
1791                                 optsArray[id] = cellopts;
1792                                 td += cell;
1793                             }
1794                             td+="</td>";
1795                             row += td;
1796                         }
1797                         if( !o.requireall || !fail ) {
1798                             row += "</tr>";
1799                             html += row;
1800                         }
1801                     }
1802                 }
1803             }
1804             html+= "</tbody></table>";
1805             element.bind( "rdfdelete", function( e ) {
1806                 var triple = $(e.target).edit("getTriple");
1807                 
1808                 if( triple ) {
1809                     doDelete( [ triple ] , function( success ) { 
1810                         if( success ) {
1811                             var parent = $(e.target).parent();
1812                             $(e.target).remove();
1813                             if( parent.children().size() == 0 ) {
1814                                 if( !o.requireall ) {
1815                                             id = randomID();
1816                                     cell = "<div id='"+id+"'></div>";
1817                                     cellopts = {};
1818                                     tripopts = {subject: triple.subject, predicate:triple.predicate};
1819                                     $.extend( cellopts, o, tripopts );
1820                                     optsArray[id] = cellopts;
1821                                     $(parent).append(cell);
1822                                     $('#'+id).edit( cellopts );
1823                                 } else {
1824                                     parent.parent().remove();
1825                                 }
1826                             }
1827                         } else {
1828                             alert( "Sorry, couldn't delete that." );
1829                         }
1830                     }, o );
1831                 } else {
1832                     alert( "Sorry, that triple can't be deleted yet for some reason." );
1833                 }
1834             });
1835             element.html(html);
1836             
1837             $(element).find('div').each( function() {
1838                     var p = optsArray[this.id]
1839                         if( p ) {
1840                             if( p.editable ) {
1841                                 $(this).edit( optsArray[this.id] );
1842                             } else {
1843                                 $(this).label( optsArray[this.id] );
1844                             }
1845                         }
1846                 });           
1847             }
1848         });
1849 
1850     /**
1851      * @name jQuery.prototype.instancedropdown
1852      * @description Provide an autocomplete input that contains all instances of a given type.
1853      * @function
1854      * @param {Object} [options] @see CommonOptions
1855      * @param {String | Object} [options.type] If provided, only instances of the given type will be displayed in the dropdown.  Otherwise, all instances in the local store are options.
1856      */
1857     $.rdfwidget("ui.instancedropdown", {
1858         options: {
1859             namespaces:namespaces,
1860             showlinks:false,
1861             autocomplete:true
1862         },
1863         _create: function() {
1864             this.refresh();
1865         },
1866 
1867         disable: function() {
1868         },
1869 
1870         enable: function() {
1871         },
1872 
1873         refresh: function() {
1874             var o = this.options;
1875             var element = this.element;
1876             var that = this;
1877             var t = processResource( o.type, o );
1878             this.triples = $.rdfwidgets.statementsMatching( undefined, db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), t, undefined, false, o );
1879             if( o.filterduplicates ) {
1880                 this.triples = filterDuplicates( this.triples );
1881             }
1882             var input = $("<input type='text'></input>");
1883             doAutocomplete( input, {menuOptions: getMenuUsedOptions( { type: o.type, sources:o.sources } ) } );
1884             input.bind( "autocompletechoice", function(e) {
1885                 if( e.uri ) {
1886                     element.trigger( "rdfselect", [db.sym(e.uri)] ); 
1887                 } else {
1888                     alert( "Please enter a valid menu selection." );
1889                 }
1890             });
1891             element.empty().append( input );
1892         }
1893     });
1894 
1895     /**
1896      * @name jQuery.prototype.instancelist
1897      * @description Provide an list that contains all instances of a given type.
1898      * @function
1899      * @param {Object} [options] @see CommonOptions
1900      * @param {String | Object} [options.type] The rdf:type to filter instances by.
1901      */
1902     $.rdfwidget("ui.instancelist", {
1903         options: {
1904             namespaces:namespaces,
1905             showlinks:false,
1906             autocomplete:true,
1907             subject:window.location
1908         },
1909         _create: function() {
1910             this.refresh();
1911         },
1912 
1913         disable: function() {
1914         },
1915 
1916         enable: function() {
1917         },
1918 
1919         refresh: function() {
1920             var o = this.options;
1921             var element = this.element;
1922             var that = this;
1923             var t = processResource( o.type, o );
1924             this.triples = $.rdfwidgets.statementsMatching( undefined, db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), t, undefined, false, o);
1925             if( o.filterduplicates ) {
1926                 this.triples = filterDuplicates( this.triples );
1927             }
1928             var html = "<table class='rdfinstancelist'><tbody>";
1929             var ids = [];
1930             var id;
1931             for( var i = 0; i < this.triples.length; i++ ) {
1932                 id = randomID();
1933                 ids.push( id );
1934                 html += "<tr><td id='"+id+"'></td></tr>";
1935             }
1936             html += "</tbody></table>";
1937             element.html(html);
1938             var opts,tripopts;
1939             for( var i = 0; i < ids.length; i++ ) {
1940                 opts = {};
1941                 tripopts = {subject: that.triples[i].subject, selectable:true};
1942                 $.extend( opts, o, tripopts );
1943                 $('#'+ids[i]).label(opts);
1944             }
1945         }
1946     });
1947 
1948     /**
1949      * @name jQuery.prototype.checkbox
1950      * @description Let the user edit a triple by selecting which value(s) it takes on from a set of checkboxes.
1951      * @function
1952      * @param {Object} [options] @see CommonOptions
1953      * @param {Array} [options.choices] The set of Terms that are made available to choose from as checkboxes.
1954      * @param {String | Object} [options.subject] The subject of a statement to be matched.
1955      * @param {String | Object} [options.predicate] The predicate of a statement to be matched.
1956      * @param {String | Object} [options.object] The object of a statement to be matched.
1957      */
1958     $.rdfwidget("ui.checkbox", {
1959         options: {
1960             namespaces:namespaces,
1961             showlinks:true,
1962             choices:[]
1963         },
1964         _create: function() {
1965             this.groupname = randomID();
1966             this.refresh();
1967         },
1968 
1969         disable: function() {
1970             this.element.find('input').attr('disabled','disabled');
1971         },
1972 
1973         enable: function() {
1974             this.element.find('input').attr('disabled',false);
1975         },
1976 
1977         _setOption: function( key ) {
1978             $.Widget.prototype._setOption.apply( this, arguments );
1979             if ( key === "subject" || key === "predicate" || key === "object") {
1980                 this.refresh();
1981             }
1982         },
1983 
1984         refresh: function() {
1985             var o = this.options;
1986             var element = this.element;
1987             var that = this;
1988             processEditTerm( o );
1989             var choices = [];
1990             var used = {};
1991             var useda = [];
1992             var span = $('<span class="rdfcheckbox"></span>');
1993             if( o.editTerm === "object" ) {
1994                 for( var i = 0; i < o.choices.length; i++ ) { choices.push( processObject( o.choices[i], o ) ); }
1995             } else {
1996                 for( var i = 0; i < o.choices.length; i++ ) { choices.push( processResource( o.choices[i], o ) ); }
1997             }
1998             var exist = $.rdfwidgets.each( (o.editTerm!=="subject" ? processResource(o.subject,o) : undefined ), (o.editTerm!=="predicate" ? processResource(o.predicate,o) : undefined ), (o.editTerm!=="object" ? processObject(o.object,o) : undefined ), undefined, o );
1999             for( var i = 0; i < exist.length; i++ ) {
2000                 choices.push( exist[i] );
2001             }
2002             function handleCheck(e) {
2003                 e.preventDefault();
2004                 var t = $(e.target);
2005                 var selection = choices[Number($(this).val())];
2006                 t.attr("disabled","disabled");
2007                 var update = {insert_triples:[],delete_triples:[]};
2008                 if( !t.attr("checked") ) {
2009                     var st = new $rdf.Statement( o.subject ? processResource( o.subject,o ) : undefined,
2010                                                  o.predicate ? processResource( o.predicate,o ) : undefined,
2011                                                  o.object ? processObject( o.object,o ) : undefined );
2012                     st[o.editTerm] = selection;
2013                     var st2 = $.rdfwidgets.anyStatementMatching( st.subject, st.predicate, st.object,undefined,o );
2014                     st = st2 ? st2 : st;
2015                     update.delete_triples.push( st );
2016                 } else {
2017                     var st = new $rdf.Statement( o.subject ? processResource( o.subject,o ) : undefined,
2018                                                  o.predicate ? processResource( o.predicate,o ) : undefined,
2019                                                  o.object ? processObject( o.object,o ) : undefined );
2020                     st[o.editTerm] = selection;
2021                     update.insert_triples.push( st );
2022                 }
2023                 doUpdate( update, function( success ) {
2024                         if( success ) {
2025                             t.attr("checked",!t.attr("checked"));
2026                             t.attr("disabled",false);
2027                         } else {
2028                             t.attr("disabled",false);
2029                             alert( "Sorry, the server responded to your request with an error." );
2030                         }
2031                     }, o);
2032             }
2033 
2034             element.empty();
2035             for( var i = 0; i < choices.length; i++ ) {
2036                 if( !used[choices[i].value] ) {
2037                     var id = randomID();
2038                     var div = $('<div class="rdfcheckbox" ></div>');
2039                     var cb = $('<input type="checkbox" name="'+this.groupname+'" id="'+id+'" value="'+useda.length+'"></input>');
2040                     var label = $('<label for="'+id+'">'+doLabel(choices[i],o)+'</label>');
2041                     span.append(div);
2042                     div.append(cb);
2043                     div.append(label);
2044                     var st = new $rdf.Statement( o.subject ? processResource( o.subject,o ) : undefined,
2045                                                  o.predicate ? processResource( o.predicate,o ) : undefined,
2046                                                  o.object ? processObject( o.object,o ) : undefined );
2047                     st[o.editTerm] = choices[i];
2048                     if( $.rdfwidgets.anyStatementMatching( st.subject, st.predicate, st.object, undefined, o ) ) {
2049                         cb.attr( "checked", "checked" );
2050                     }
2051                     cb.bind("click", handleCheck);
2052                     used[choices[i].value] = true;
2053                     useda.push(choices[i].value);
2054                 }
2055             }
2056             element.append(span);
2057         }
2058 
2059     });
2060 
2061     /**
2062      * @name jQuery.prototype.radio
2063      * @description Let the user edit a triple by selecting which single value it takes on from a set of radio buttons.
2064      * @function
2065      * @param {Object} [options] @see CommonOptions
2066      * @param {Array} [options.choices] The set of choices the user may choose from as a set of radio buttons.
2067      * @param {String | Object} [options.subject] The subject of a statement to be matched.
2068      * @param {String | Object} [options.predicate] The predicate of a statement to be matched.
2069      * @param {String | Object} [options.object] The object of a statement to be matched.
2070      */
2071     $.rdfwidget("ui.radio", {
2072         options: {
2073             namespaces:namespaces,
2074 
2075             showlinks:true,
2076             choices:[]
2077         },
2078         _create: function() {
2079             this.groupname = randomID();
2080             this.refresh();
2081         },
2082 
2083         disable: function() {
2084             this.element.find('input').attr('disabled','disabled');
2085         },
2086 
2087         enable: function() {
2088             this.element.find('input').attr('disabled',false);
2089         },
2090 
2091         _setOption: function( key ) {
2092             $.Widget.prototype._setOption.apply( this, arguments );
2093             if ( key === "subject" || key === "predicate" || key === "object") {
2094                 this.refresh();
2095             }
2096         },
2097 
2098         refresh: function() {
2099             var o = this.options;
2100             var element = this.element;
2101             var that = this;
2102             that.currentChoice = null;
2103             processEditTerm( o );
2104             var initialValue;
2105             var choices = [];
2106             var used = {};
2107             var useda = [];
2108             var span = $('<span class="rdfradio"></span>');
2109             var temp = $.rdfwidgets.each( o.subject ? processResource( o.subject,o ) : undefined,
2110                                           o.predicate ? processResource( o.predicate,o ) : undefined,
2111                                           o.object ? processObject( o.object,o ) : undefined, undefined, o );
2112             if( temp && temp.length > 1 ) {
2113                 element.checkbox( o );
2114                 return;
2115             }
2116             if( o.editTerm === "object" ) {
2117                 for( var i = 0; i < o.choices.length; i++ ) { choices.push( processObject( o.choices[i], o ) ); }
2118             } else {
2119                 for( var i = 0; i < o.choices.length; i++ ) { choices.push( processResource( o.choices[i], o ) ); }
2120             }
2121 
2122             if( temp && temp.length === 1 ) {
2123                 initialValue = temp[0];
2124                 that.currentChoice = initialValue;
2125                 choices.push( temp[0] );
2126             } else {
2127                 initialValue = null;
2128             }
2129 
2130             function handleCheck(e) {
2131                 e.preventDefault();
2132                 var selection = choices[Number($(this).val())];
2133                 if( that.currentChoice && selection.value === that.currentChoice.value ) {
2134                     return;
2135                 }
2136                 element.find( "input" ).attr("disabled","disabled");
2137                 var update = {insert_triples:[],delete_triples:[]};
2138                 if( that.currentChoice ) {
2139                     var st = new $rdf.Statement( o.subject ? processResource( o.subject,o ) : undefined,
2140                                                  o.predicate ? processResource( o.predicate,o ) : undefined,
2141                                                  o.object ? processObject( o.object,o ) : undefined );
2142                     st[o.editTerm] = that.currentChoice;
2143                     var st2 = $.rdfwidgets.anyStatementMatching( st.subject, st.predicate, st.object, undefined, o );
2144                     st = st2 ? st2 : st;
2145                     update.delete_triples.push( st );
2146                 }
2147                 var st = new $rdf.Statement( o.subject ? processResource( o.subject,o ) : undefined,
2148                                              o.predicate ? processResource( o.predicate,o ) : undefined,
2149                                              o.object ? processObject( o.object,o ) : undefined );
2150                 st[o.editTerm] = selection;
2151                 update.insert_triples.push( st );
2152                 doUpdate( update, function( success ) {
2153                         if( success ) {
2154                             $(e.target).attr("checked","checked");
2155                             that.currentChoice = selection;
2156                             element.find( "input" ).attr("disabled",false);
2157                         } else {
2158                             element.find( "input" ).attr("disabled",false);
2159                             alert( "Sorry, the server responded to your request with an error." );
2160                         }
2161                     }, o);
2162             }
2163 
2164             element.empty();
2165             for( var i = 0; i < choices.length; i++ ) {
2166                 if( !used[choices[i].value]) {
2167                     var id = randomID();
2168                     var div = $('<div class="rdfradio"></div>');
2169                     var radio = $('<input class="rdfradio" type="radio" name="'+this.groupname+'" id="'+id+'" value="'+useda.length+'"></input>');
2170                     var label = $('<label for="'+id+'">'+doLabel(choices[i],o)+'</label>');
2171                     span.append(div);
2172                     div.append(radio);
2173                     div.append(label);
2174                     if( initialValue && choices[i].value === initialValue.value ) {
2175                         radio.attr("checked","checked");
2176                     }
2177                     used[choices[i].value] = true;
2178                     useda.push( choices[i].value );
2179                     radio.bind("click", handleCheck);
2180                 }
2181             }
2182             element.append(span);
2183         },
2184         getTriple: function() {
2185             var o = this.options;
2186             if( this.currentChoice ) {
2187                 var st = new $rdf.Statement( o.subject ? processResource( o.subject,o ) : undefined,
2188                                              o.predicate ? processResource( o.predicate,o ) : undefined,
2189                                              o.object ? processObject( o.object,o ) : undefined );
2190                 st[o.editTerm] = this.currentChoice;
2191                 return $.rdfwidgets.anyStatementMatching( st.subject, st.predicate, st.object, undefined, o );
2192             }
2193         }
2194 
2195     });
2196 
2197     $.ui.radio.getter = "getTriple";
2198 
2199     /**
2200      * @name jQuery.prototype.combobox
2201      * @description Let the user edit a triple by selecting which single value it takes on from a combobox.
2202      * @function
2203      * @param {Object} [options] @see CommonOptions
2204      * @param {Array} [options.choices] The set of choices the user may choose from as dropdown items in a combo box.
2205      * @param {String | Object} [options.subject] The subject of a statement to be matched.
2206      * @param {String | Object} [options.predicate] The predicate of a statement to be matched.
2207      * @param {String | Object} [options.object] The object of a statement to be matched.
2208      */
2209     $.rdfwidget("ui.combobox", {
2210         options: {
2211             namespaces:namespaces,
2212             choices:[]
2213         },
2214         _create: function() {
2215             this.refresh();
2216         },
2217 
2218         disable: function() {
2219             this.combo.attr( "disabled", "disabled" );
2220         },
2221 
2222         enable: function() {
2223             this.combo.attr( "disabled", false );
2224         },
2225 
2226         getTriple: function() {
2227             var o = this.options;
2228             var term = this.ids[this.current.val()];
2229             if( term && o.editTerm ) {
2230                 var st = new $rdf.Statement( o.subject ? processResource( o.subject,o ) : undefined,
2231                                              o.predicate ? processResource( o.predicate,o ) : undefined,
2232                                              o.object ? processObject( o.object,o ) : undefined );
2233                 st[o.editTerm] = term;
2234                 return $.rdfwidgets.anyStatementMatching( st.subject, st.predicate, st.object, undefined, o );
2235             }
2236         },
2237 
2238         _setOption: function( key ) {
2239             $.Widget.prototype._setOption.apply( this, arguments );
2240             if ( key === "subject" || key === "predicate" || key === "object" || key === "editTerm" ) {
2241                 this.refresh();
2242             }
2243         },
2244 
2245         refresh: function() {
2246             var o = this.options;
2247             var element = this.element;
2248             var that = this;
2249             processEditTerm( o );
2250             var ids = {};
2251             var opt, term, id;
2252             var process = (o.editTerm === "object") ? processObject : processResource;
2253             var combo = $('<select class="rdfcombobox"></select>');
2254             this.combo = combo;
2255             var initial = null;
2256             this.current = null;
2257             function addOption( str,hidden ) {
2258                 term = process( str,o );
2259                 id = randomID();
2260                 if( !hidden ) {
2261                     ids[id] = term;
2262                 }
2263                 opt = $('<option value="'+id+'" title="'+term.value.toString()+'"'+(hidden?' style="display:none"':'')+'>'+doLabel(term, o)+'</option>');
2264                 combo.append( opt );
2265                 return opt;
2266             }
2267             this.current = addOption( "", true );
2268             var subject = ((o.editTerm === "subject") ? undefined : processResource(o.subject,o));
2269             var predicate = ((o.editTerm === "predicate") ? undefined : processResource(o.predicate,o));
2270             var object = ((o.editTerm === "object") ? undefined : processObject(o.object,o));
2271             var existing = $.rdfwidgets.each( subject,predicate,object,undefined,o );
2272 
2273             var used = {};
2274             var choices = [];
2275             for( var i = 0; i < o.choices.length; i++ ) {
2276                 choices.push( process( o.choices[i], o ) );
2277             }
2278 
2279             if( existing ) {
2280                 if( existing.length === 1 ) {
2281                     initial = existing[0].value;
2282                     choices.push( existing[0] );
2283                 } else if (existing.length > 1) {
2284                     throw "Combobox functionality is only provided for patterns with a single value right now:"+existing;
2285                 }
2286             }
2287             for( var i = 0; i < choices.length; i++ ) {
2288                 if( !used[ choices[i].value ] ) {
2289                     used[choices[i].value] = true;
2290                     var opt = addOption( choices[i] );
2291                     if( choices[i].value === initial ) {
2292                         opt.attr("selected","selected");
2293                         this.current = opt;
2294                     }
2295                 }
2296             }
2297 
2298             element.empty().append(combo);
2299             combo.change( function( e ) {
2300                 old = ids[that.current.val()];
2301                 var term = ids[e.target.value];
2302                 var d = processTripleUpdate( term, old, o );
2303                 combo.attr("disabled","disabled");
2304                 doUpdate( d, function( success ) {
2305                     if( success ) {
2306                         that.current = $(e.target).find(":selected");
2307                     } else {
2308                         alert( "Sorry, the server responded to your request with an error." );
2309                         that.current.attr("selected","selected");
2310                     }
2311                     combo.attr( "disabled", false );
2312                 }, o );
2313             });
2314             this.ids = ids;
2315         }
2316     });
2317 
2318     $.ui.combobox.getter = "getTriple";
2319 
2320     /**
2321      * @name jQuery.prototype.triplelist
2322      * @description Display the list of triples that match a given pattern.
2323      * @function
2324      * @param {Object} [options] @see CommonOptions
2325      * @param {String | Object} [options.subject] The subject of the statements to be matched.
2326      * @param {String | Object} [options.predicate] The predicate of the statements to be matched.
2327      * @param {String | Object} [options.object] The object of the statements to be matched.
2328      * @param {String | Object} [options.filterduplicates=false] Remove duplicate statements from multiple different data sources.
2329      */
2330     $.rdfwidget("ui.triplelist", {
2331         options: {
2332             namespaces:namespaces,
2333             showlinks:false,
2334             autocomplete:true,
2335             filterduplicates:false
2336         },
2337         _create: function() {
2338             this.refresh();
2339         },
2340 
2341         _setOption: function( key ) {
2342             $.Widget.prototype._setOption.apply( this, arguments );
2343             if ( key === "subject" || key === "predicate" || key === "object" ) {
2344                 this.refresh();
2345             }
2346         },
2347 
2348         refresh: function() {
2349             var o = this.options;
2350             var element = this.element;
2351             var that = this;
2352             var table = $('<table class="triplelist"></table>');
2353             table.bind( "rdfdelete", function( e ) {
2354                 var row = $(e.target).parent();
2355                 var triple = row.tripleedit( "getTriple" );
2356                 if( triple ) {
2357                     doDelete( [ triple ] , function( success ) { 
2358                         if( success ) {
2359                             row.remove();
2360                         } else {
2361                             alert( "Sorry, the server responded to your edit request with an error." );
2362                         }
2363                     }, o );
2364                 } else {
2365                     alert( "Sorry, that triple can't be deleted yet for some reason." );
2366                 }
2367             });
2368             element.empty().append(table);
2369             var subject = o.subject ? processResource( o.subject,o ) : undefined;
2370             var predicate = o.predicate ? processResource( o.predicate,o) : undefined;
2371             var object = o.object ? processObject( o.object, o) : undefined;
2372             this.triples = $.rdfwidgets.statementsMatching( subject,predicate,object, undefined, false, o );
2373             if( o.filterduplicates ) {
2374                 this.triples = filterDuplicates( this.triples );
2375             }
2376             var rows = [];
2377             var html= "<tbody>";
2378             for( var i = 0; i < this.triples.length; i++ ) {
2379                 var t = this.triples[i];
2380                 var rid = randomID(), sid = null, pid = null, oid = null;
2381                 html += "<tr id='"+rid+"'>";
2382                 if( !o.subject ) {
2383                     sid = randomID();
2384                     html += "<td id='"+sid+"'></td>";
2385                 }
2386                 if( !o.predicate ) {
2387                     pid = randomID();
2388                     html += "<td id='"+pid+"'></td>";
2389                 }
2390                 if( !o.object ) {
2391                     oid = randomID();
2392                     html += "<td id='"+oid+"'></td>";
2393                 }
2394                 html += "</tr>";
2395                 var opts = {};
2396                 var tripopts = {subject: t.subject, predicate: t.predicate, object: t.object, subjectElement: '#'+sid, predicateElement: '#'+pid, objectElement: '#'+oid};
2397                 $.extend( opts, o, tripopts );
2398                 rows.push({r:rid,o:opts});
2399             }
2400             html += "</tbody>";
2401             table.append( html );
2402 
2403             for( var i = 0; i < rows.length; i++ ) {
2404                 $('#'+rows[i].r).tripleedit(rows[i].o);
2405             }
2406         }
2407     });
2408 
2409     /**
2410      * @name jQuery.prototype.toolbar
2411      * @description Display an image for each resource of a certain type in a toolbar layout.
2412      * @function
2413      * @param {Object} [options] @see CommonOptions
2414      * @param {String | Object} [options.type] The rdf:type that should be used to decide which resources will appear in the toolbar.
2415      */
2416     $.rdfwidget("ui.toolbar", {
2417         options: {
2418             namespaces:namespaces,
2419             width:32,
2420             height:32
2421         },
2422         _create: function() {
2423             this.refresh();
2424         },
2425 
2426         disable: function() {
2427             element.find( "td" ).each( function() { 
2428                 $(this).image("disable");
2429             });
2430         },
2431 
2432         enable: function() {
2433             element.find( "td" ).each( function() { 
2434                 $(this).image("enable");
2435             });
2436         },
2437 
2438         refresh: function() {
2439             var o = this.options;
2440             var element = this.element;
2441             var that = this;
2442             var t = processResource( o.type, o );
2443 
2444             if( o.choices ) {
2445                 this.choices = o.choices;
2446             } else {
2447                 this.choices = $.rdfwidgets.each( undefined, db.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), t, undefined, o );
2448             }
2449             var html = "<table class='rdftoolbar'><tr>";
2450             var actual = [];
2451 
2452             for( var i = 0; i < this.choices.length; i++ ) {
2453                 if( (o.mappings && o.mappings[this.choices[i]]) || doImage( processResource(this.choices[i],o),o) ) {
2454                     actual.push( this.choices[i] );
2455                     html += "<td width="+o.width+" height="+o.height+" class='rdftoolbar' style='margin:1px'></td>";
2456                 }
2457             }
2458             html += "</tr></table>";
2459             element.html(html);
2460             element.find( "td" ).each( function(i) { 
2461                     var opts = {};
2462                     var tripopts = {subject: actual[i], selectable:true};
2463                     $.extend( opts, o, tripopts );
2464                     $(this).image(opts);
2465                 });
2466         }
2467     });
2468 
2469     /**
2470      * @name jQuery.prototype.resource
2471      * @description Display a table containing all of the properties of a given resource.
2472      * @function
2473      * @param {Object} [options] @see CommonOptions
2474      * @param {String | Object} [options.subject] the URI of the resource to be described.
2475      */
2476     $.rdfwidget("ui.resource", {
2477         options: {
2478             namespaces:namespaces,
2479             showlinks:false,
2480             autocomplete:true,
2481             subject:window.location
2482         },
2483         _create: function() {
2484             this.refresh();
2485         },
2486 
2487         disable: function() {
2488         },
2489 
2490         enable: function() {
2491         },
2492 
2493         _setOption: function( key ) {
2494             $.Widget.prototype._setOption.apply( this, arguments );
2495             if ( key === "subject" ) {
2496                 this.refresh();
2497             }
2498         },
2499 
2500         refresh: function() {
2501             var o = this.options;
2502             var element = this.element;
2503             var that = this;
2504             var div = $('<div></div>');
2505             var table = $('<table class="rdfresourceedit"></table>');
2506             table.bind( "rdfdelete", function( e ) {
2507                 var row = $(e.target).parent();
2508                 var triple = row.tripleedit( "getTriple" );
2509                 if( triple ) {
2510                     doDelete( [ triple ] , function( success ) { 
2511                         if( success ) {
2512                             row.remove();
2513                         } else {
2514                             alert( "Sorry, couldn't delete that." );
2515                         }
2516                     }, o );
2517                 } else {
2518                     alert( "Sorry, that triple can't be deleted yet for some reason." );
2519                 }
2520             });
2521             div.append(table);
2522             element.empty().append(div);
2523             var subject = processResource( o.subject,o );
2524             this.triples = $.rdfwidgets.statementsMatching( subject,undefined,undefined,undefined,false,o );
2525             var hid = randomID();
2526             var thead = "<thead><tr class='rdfeditheader'><td id='"+hid+"' colspan=2></td></tr></thead>";
2527             table.append(thead);
2528             $('#'+hid).label( {showlinks:o.showlinks,subject:o.subject, selectable:true } );
2529             var tbody = "<tbody>";
2530             var id, ids = [], opts, tripopts;
2531             var sopts = [];
2532             for( var i = 0; i < this.triples.length; i++ ) {
2533                 id = { p:randomID(),r:randomID(),o:randomID() };
2534                 ids.push(id);
2535                 var tr = '<tr id="'+id.r+'"><td id="'+id.p+'"></td><td id="'+id.o+'"></td></tr>';
2536                 var opts = {};
2537                 var tripopts = {subject: this.triples[i].subject, predicate: this.triples[i].predicate, object: this.triples[i].object, subjectElement: null, predicateElement: '#'+id.p, objectElement: '#'+id.o };
2538                 $.extend( opts, o, tripopts );
2539                 sopts.push( opts );
2540                 tbody+=tr;
2541             }
2542             tbody += "</tbody>";
2543             table.append( tbody );
2544             
2545             for( var i = 0; i < this.triples.length; i++ ) {
2546                 $('#'+ids[i].r).tripleedit( sopts[i] );
2547             }
2548             var addButton = $('<input type="button" value="Add..."></input>');
2549             div.append(addButton);
2550             addButton.click( function() {
2551                 var addTable = $('<table class="rdfaddtriple"></table');
2552                 addTable.append('<tr><th colspan="2">Add Data</th></tr>');
2553                 var rowid1 = randomID();
2554                 var rowid2 = randomID();
2555                 var firstRow = $('<tr><td><label for="'+rowid1+'">Property: </label></td></tr>');
2556                 var secondRow = $('<tr><td><label for="'+rowid2+'">Value: </label></td></tr>');
2557                 var firstInput = $('<input name="'+rowid1+'" type="text"></input>');
2558                 var secondInput = $('<input name="'+rowid2+'" type="text"></input>');
2559                 
2560                 doAutocomplete( firstInput, { menuOptions:getMenuPredicateOptions( {} ) } );
2561                 doAutocomplete( secondInput, { menuOptions:getMenuInstanceOptions( {} ) } );
2562                 
2563                 var lastPred, lastPredInput, lastObj, lastObjInput;
2564                 
2565                 firstInput.bind( "autocompletechoice", function(e) {
2566                     lastPred = e.uri;
2567                     lastPredInput = e.input;
2568                 });
2569                 
2570                 secondInput.bind( "autocompletechoice", function(e) {
2571                     lastObj = e.uri;
2572                     lastObjInput = e.input;
2573                 });
2574                 secondInput.focus( function() {
2575                     doAutocomplete( secondInput, { menuOptions:getMenuInstanceOptions( {subject:o.subject, predicate:lastPred, editTerm:"object"} ) } );
2576                 })
2577                 firstRow.append(firstInput);
2578                 secondRow.append(secondInput);
2579                 addTable.append(firstRow).append(secondRow);
2580                 var addPopup = $('<div style="position:absolute; display:none" class="rdfaddpropertypopup"></div>');
2581                 var closeButton = $('<a style="position:absolute" href="#">X</a>');
2582                 var submitButton = $('<input type="button" value="Submit"></input>');
2583                 submitButton.click( function() {
2584                     if( !lastPred ) {
2585                         alert( "You must enter a valid property.");
2586                         return;
2587                     }else if( !lastObjInput ) {
2588                         alert( "You must enter a value." );
2589                         return;
2590                     } else {
2591                         var pred = processResource( lastPred, o );
2592                         var obj;
2593                         if( lastObj ) {
2594                             obj = processResource( lastObj, o );
2595                         } else {
2596                             obj = processObject( lastObjInput, o );
2597                         }
2598                         var triple = new $rdf.Statement( subject, pred, obj );
2599                         if( !$.rdfwidgets.anyStatementMatching( subject, pred, obj,undefined, o ) ) {
2600                             doInsert( [ triple ], function( success ) {
2601                                 if( success ) {
2602                                     var predicateTD = $('<td class="'+((2%2===0) ? 'rdfediteven' : 'rdfeditodd')+'"></td>');
2603                                     var objectTD = $('<td class="'+((2%2===0) ? 'rdfediteven' : 'rdfeditodd')+'"></td>');
2604                                     var TR = $('<tr class="'+((2%2===0) ? 'rdfediteven' : 'rdfeditodd')+'"></tr>');
2605                                     
2606                                     table.append(TR);
2607                                     TR.append(predicateTD);
2608                                     TR.append(objectTD);
2609                                     var opts = {};
2610                                     var tripopts = {subject: subject, predicate: pred, object: obj, subjectElement: null, predicateElement: predicateTD, objectElement: objectTD };
2611                                     $.extend( opts, o, tripopts );
2612                                     TR.tripleedit(opts);
2613                                     addPopup.hide("fast");
2614                                 } else {
2615                                     alert( "Sorry, the triple could not be inserted at this time." );
2616                                 }
2617                             }, o );
2618                         } else {
2619                             alert( "Good news: the triple you created already exists!" );
2620                         }
2621                     }
2622                 });
2623                 closeButton.click( function() {
2624                     addPopup.hide("fast");
2625                 });
2626                 addPopup.append(closeButton);
2627                 addPopup.append( addTable );
2628                 addPopup.append( submitButton );
2629                 var pos = addButton.offset();
2630                 var width = addButton.outerWidth();
2631                 var height = addButton.outerHeight();
2632                 div.append( addPopup );
2633                 closeButton.css( { "right": "2px", "top": "2px" } );
2634                 addPopup.css( {"left": (pos.left + width) + "px", "top":(pos.top+height) + "px" } );
2635                 
2636                 addPopup.show("fast");
2637             });
2638         }
2639     });
2640 
2641     /**
2642      * @name jQuery.prototype.createinstance
2643      * @description Create a form for creating new information about an as-yet undescribed object.
2644      * @function
2645      * @param {Object} [options] @see CommonOptions
2646      * @param {Object} [options.properties] An array of create instance options.  Each object must at least have a property field, which defines which predicate the form input will obtain an object for. More coming soon.
2647      */
2648     $.rdfwidget("ui.createinstance", {
2649         options: {
2650             namespaces:namespaces,
2651             showlinks:false,
2652             autocomplete:true
2653         },
2654         _create: function() {
2655             this.refresh();
2656         },
2657 
2658         disable: function() {
2659             element.find("input").attr("disabled","disabled");
2660             element.find("textarea").attr("disabled","disabled");
2661         },
2662 
2663         enable: function() {
2664             element.find("input").attr("disabled",false);
2665             element.find("textarea").attr("disabled",false);
2666         },
2667 
2668         refresh: function() {
2669             var o = this.options;
2670             var element = this.element;
2671             var that = this;
2672             var items = o.items;
2673             var itemIDs = [];
2674             var map = {};
2675             var form = $('<form class="rdfcreateinstance"></form>');
2676             var table = $('<table class="rdfcreateinstance"></table>');
2677             var staticTriples = [];
2678             var why = db.sym(getEndpoint( null, o ));
2679             o.editTerm="object";
2680 
2681             if( o.items ) {
2682                 for( var i=0; i < items.length; i++ ) {
2683                     var item = items[i];
2684                     if ( item.type == "hidden" ) {
2685                         if( item.predicate && item.object ) {
2686                             staticTriples.push(item);
2687                         }
2688                         continue;
2689                     }
2690 
2691                     var id = randomID();
2692                     if( !item.predicate ) {
2693                         continue;
2694                     }
2695                     itemIDs.push( id );
2696                     map[id] = item;
2697                     var row = $("<tr></tr>");
2698                     var p = processResource( item.predicate, o );
2699                     if( !item.type || item.type == "text" ) {
2700                         var td = $("<td></td>");
2701                         var label = $("<td><label for='"+id+"'>"+doLabel( p, o )+"</label></td>");
2702                         var input = $("<input id='"+id+"' name='"+id+"' type='text'></input>");
2703                         td.append(input);
2704                         row.append(label).append(td);
2705                         var opts = {};
2706                         //todo: inputopts used to be {predicate:p}
2707                         var inputopts = item;
2708                         $.extend( opts, o, item, inputopts );
2709                         doAutocomplete( input, opts );
2710                         table.append(row);
2711                     } else if ( item.type == "textarea" ) {
2712                         var td = $("<td></td>");
2713                         var label = $("<td><label for='"+id+"'>"+doLabel( p, o )+"</label></td>");
2714                         var input = $("<textarea id='"+id+"' name='"+id+"' ></textarea>");
2715                         td.append(input);
2716                         row.append(label).append(td);
2717                         var opts = {};
2718                         var inputopts = {subject:(o.subject ? processResource(o.subject) : (o.baseURI ? db.sym(o.baseURI + '#' + randomID()) : db.sym( window.location + '#' + randomID() ) ) ), predicate: p };
2719                         $.extend( opts, o, item, inputopts );
2720                         doAutocomplete( input, opts );
2721                         table.append(row);
2722                     }
2723                 }
2724             }
2725 
2726             form.append(table);
2727             var submit = $('<input type="submit" value="Submit"></input>');
2728             form.append(submit);
2729             var disableAll = function() {
2730                 for( var i=0; i<itemIDs.length; i++ ) { $('#'+itemIDs[i]).attr("disabled","disabled"); }
2731                 submit.attr("disabled","disabled");
2732             };
2733             var enableAll = function() {
2734                 for( var i=0; i<itemIDs.length; i++ ) { $('#'+itemIDs[i]).attr("disabled",false); }
2735                 submit.attr("disabled",false);
2736             };
2737             form.submit( function(e) { 
2738                 e.preventDefault();
2739                 disableAll();
2740                 var subject = o.subject ? processResource(o.subject) : (o.baseURI ? db.sym(docpart(o.baseURI) + '#' + randomID()) : db.sym( docpart(window.location.toString()) + '#' + randomID() ) );
2741                 var triples = [];
2742                 for( var i=0; i<itemIDs.length; i++ ) {
2743                     var id = itemIDs[i];
2744                     var item = map[id];
2745                     var elt = $('#'+id);
2746                     var uri = elt.data("uri");
2747                     var lastAC = elt.data("val");
2748                     var val = elt.val();
2749                     if( val === lastAC ) {
2750                         if( uri ) {
2751                             triples.push( new $rdf.Statement( subject, processResource( item.predicate ), processResource( uri ), subject ) );
2752                         } else {
2753                             triples.push( new $rdf.Statement( subject, processResource( item.predicate ), processLiteral( val ), subject ) );
2754                         }
2755                     } else {
2756                         triples.push( new $rdf.Statement( subject, processResource( item.predicate ), processLiteral( val ), subject ) );
2757                     }
2758                 }
2759                 for( var i = 0; i < staticTriples.length; i++ ) {
2760                     triples.push( new $rdf.Statement( subject, processResource( staticTriples[i].predicate ), processObject( staticTriples[i].object ), subject ) );
2761                 }
2762 
2763                 doInsert( triples , function(success) { 
2764                     if( success ) {
2765                     } else {
2766                         alert( "Sorry, an error occurred during the submission of your information." );
2767                     }
2768                     enableAll();
2769                 },o);
2770                 return false;
2771             });
2772             element.empty().append(form);
2773         }
2774     });
2775 
2776 
2777     /**
2778      * @name jQuery.prototype.sourcelist
2779      * @description Display a list of all sources currently in use by the widget library.
2780      * @function
2781      */
2782     $.rdfwidget("ui.sourcelist", {
2783         options: {
2784         },
2785         _create: function() {
2786             this.refresh();
2787         },
2788 
2789         disable: function() {
2790         },
2791 
2792         enable: function() {
2793         },
2794 
2795         refresh: function() {
2796             var o = this.options;
2797             var element = this.element;
2798             element.empty();
2799             var span = $('<span class="rdfsourcelist"></span>');
2800 
2801             var html = "<div class='rdfsourcelisttitle'>Sources</div>";
2802             for( var x in loadedSources ) {
2803                 html += "<div class='rdfsourcelist'><a href='"+x+"' title='"+doLabel(db.sym(x),o)+"'>"+x+"</a></div>";
2804             }
2805             span.html(html);
2806             element.append(span);
2807         },
2808 
2809         insertData: function(d) {
2810             this.refresh();
2811         },
2812 
2813         deleteData: function(d) {
2814             this.refresh();
2815         }
2816     });
2817 
2818 
2819     getNodeSubject = function( node ) {
2820         var current = $(node);
2821         var subject;
2822         while( current.length > 0 ) {
2823             about = current.attr("about");
2824             var nn = current.get(0).nodeName;
2825             if( nn && nn.toLowerCase() === "a" ) {
2826                 subject = join( current.get(0).href, window.location.toString() );
2827                 break;
2828             }
2829             if( about ) {
2830                 if( about.indexOf("[") === 0 ) {
2831                     //TODO: support safe curies..
2832                     continue;
2833                 } else {
2834                     subject = join( about, window.location.toString() );
2835                     break;
2836                 }
2837             }
2838             current = current.parent();
2839         }
2840         if( !subject ) {
2841             subject = window.location.toString();
2842         }
2843         return subject;
2844     }
2845 
2846     /**
2847      * @name jQuery.prototype.annotator
2848      * @description Display an annotation box when any element inside of a certain element is shift+clicked.
2849      * @function
2850      * @param {Object} [options] @see CommonOptions
2851      * @param {String | Object} [options.displayElement] A valid jQuery constructor argument that determines where the annotations will be displayed when an item is selected.  If not provided, the annotations will be displayed in a floating popup.
2852      */
2853     $.rdfwidget("ui.annotator", {
2854         options: {
2855         },
2856 
2857         _findSubject: function( current ) {
2858             var subject;
2859             while( current.length > 0 ) {
2860                 about = current.attr("about");
2861                 var nn = current.get(0).nodeName;
2862                 if( nn && nn.toLowerCase() === "a" ) {
2863                     subject = join( current.get(0).href, window.location.toString() );
2864                     break;
2865                 }
2866                 if( about ) {
2867                     if( about.indexOf("[") === 0 ) {
2868                         //TODO: support safe curies..
2869                         continue;
2870                     } else {
2871                         subject =  join( about, window.location.toString() );
2872                         break;
2873                     }
2874                 }
2875                 current = current.parent();
2876             }
2877             if( !subject ) {
2878                 subject = window.location.toString();
2879             }
2880             return subject;
2881         },
2882 
2883         _create: function() {
2884             var annotator;
2885             var o = this.options;
2886             if( o.displayElement ) {
2887                 annotator = $("<div class='rdfannotator'></div>");
2888             } else {
2889                 annotator = $("<div class='rdfannotator' style='display:none; position:absolute'></div>");
2890             }
2891             var that=this;
2892             var filters = processSourceFilter( o.sources, o );
2893             function doCommentBox( e ) {
2894                 if( !e.target || !e.shiftKey ) {
2895                     return;
2896                 }
2897                 if( e.stopPropagation ) {
2898                     e.stopPropagation();
2899                     e.preventDefault();
2900                 }
2901                 var current = $(e.target);
2902                 that.current = current;
2903                 var about;
2904                 var subject = that._findSubject( current );
2905                 annotator.empty();
2906                 var listdiv = $("<div class='rdfannotatorlist'></div>");
2907                 that.listdiv = listdiv;
2908                 that._matcher( listdiv )
2909                     .match( "?comment", "http://rdfs.org/sioc/ns#topic", processResource( subject ) )
2910                     .match( "?comment", "http://rdfs.org/sioc/ns#content", "?content" )
2911                     .match( "?comment", "http://rdfs.org/sioc/ns#has_creator", "?user" )
2912                     .page("<div class='rdfannotatorcomment'><div><a href='?user'>@user</a> wrote...</div><div><p>?content</p></div></div>", 4);
2913 
2914                 var user = $.rdfwidgets.getDefaultUser();
2915                 if( !user ) { user = SW_PREFIX + "Anonymous"; }
2916                 annotator.append(listdiv);
2917                 var lid = randomID();
2918                 annotator.append( "<div class='rdfannotatortitle'>Add a comment about <span id='"+lid+"' title='"+subject+"'>"+subject+"</span></div>" );
2919                 $('#'+lid).label({subject: db.sym(subject), selectable:false})
2920                 var formdiv = $("<div class='rdfannotatorform'></div>");
2921                 var opts = $.extend({},that.options,{afterSubmit:function(s) { if(s && !o.displayElement) { annotator.hide("fast"); } },
2922                                                      items:[{type:"textarea",
2923                                                              predicate:"http://rdfs.org/sioc/ns#content",
2924                                                              menuOptions:getMenuNoOptions()},
2925                                                             {type:"hidden",
2926                                                              predicate:"http://rdfs.org/sioc/ns#has_creator",
2927                                                              object:db.sym( user )
2928                                                             },
2929                                                             {type:"hidden",
2930                                                              predicate:"http://rdfs.org/sioc/ns#topic",
2931                                                              object:subject
2932                                                             },
2933                                                             {type:"hidden",
2934                                                              predicate:"http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
2935                                                              object:"http://rdfs.org/sioc/types#Comment"
2936                                                             }],
2937                                                      acl:( o.acl ? o.acl : {
2938                                                          type:"permanent"
2939                                                      } )});
2940                 formdiv.createinstance(opts);
2941                 annotator.append(formdiv);
2942                 if( !o.displayElement ) {
2943                     var closeButton = $('<a style="position:absolute" href="#">X</a>');
2944                     annotator.css({left:e.pageX+"px", top:e.pageY+"px"});
2945                     closeButton.click( function() {
2946                         annotator.hide("fast");
2947                     });
2948                     closeButton.css( { "right": "2px", "top": "2px" } );
2949                     annotator.append( closeButton );
2950                     annotator.show("fast");
2951                 }
2952             }
2953 
2954             this.element.bind( "click", doCommentBox );
2955             if( o.displayElement ) {
2956                 $(o.displayElement).append( annotator );
2957                 doCommentBox({target:this.element, shiftKey:true});
2958             } else {
2959                 $(document.body).append(annotator);
2960             }
2961         },
2962 
2963         disable: function() {
2964         },
2965 
2966         enable: function() {
2967         },
2968 
2969         refresh: function() {
2970             this._matcher( this.listdiv )
2971                 .match( "?comment", "http://rdfs.org/sioc/ns#topic", processResource( this._findSubject( this.current ) ) )
2972                 .match( "?comment", "http://rdfs.org/sioc/ns#content", "?content" )
2973                 .match( "?comment", "http://rdfs.org/sioc/ns#has_creator", "?user" )
2974                 .page("<div class='rdfannotatorcomment'><div><a href='?user'>@user</a> wrote...</div><div><p>?content</p></div></div>", 4);
2975         }
2976     });
2977 
2978     /**
2979      * @name jQuery.prototype.sourceview
2980      * @description Serialize the entire local store as N3 and display it in a text box.
2981      * @function
2982      */
2983     $.rdfwidget("ui.sourceview", {
2984         options: {
2985           format:"n3"
2986         },
2987         _create: function() {
2988             this.refresh();
2989         },
2990 
2991         disable: function() {
2992         },
2993 
2994         enable: function() {
2995         },
2996 
2997         refresh: function() {
2998             var o = this.options;
2999             var element = this.element;
3000             element.empty();
3001             var pre = $('<pre class="rdfsourceview"></pre>');
3002             var s = $rdf.Serializer( data );
3003 
3004             var text;
3005             if( o.format === "xml" ) {
3006                 text = s.statementsToXML( db.statements );
3007             } else {
3008                 text = s.toN3( db );
3009             }
3010             
3011             pre.text( text );
3012             element.append(pre);
3013         }
3014     });
3015 
3016     /**
3017      * @name jQuery.prototype.image
3018      * @description Search for a valid image depicting a given resource and display it.  Currently, bases this search off of the foaf:depiction property, dbpedia img property, and their subproperties.
3019      * @function
3020      * @param {Object} [options] @see CommonOptions
3021      * @param {String | Object} [options.subject] The resource to display an image of OR if predicate or object is defined, the subject of a statement to be matched.
3022      * @param {String | Object} [options.predicate] The predicate of a statement to be matched.
3023      * @param {String | Object} [options.object] The object of a statement to be matched.
3024      * @param {String | Number} [options.width] The width, in pixels, that the image should be displayed with (height will be scaled).
3025      * @param {String | Number} [options.height] The height, in pixels, that the image should be displayed with (width will be scaled).
3026      */
3027     $.rdfwidget("ui.image", {
3028         options: {
3029             namespaces:namespaces,
3030             selectable:true
3031         },
3032         _create: function() {
3033             this.originalEditTerm = this.options.editTerm;
3034             this.rdfvalue = null;
3035             this.refresh();
3036         },
3037         refresh: function() {
3038             var element = this.element;
3039             var o = this.options;
3040             var that = this;
3041             o.editTerm = this.originalEditTerm;
3042             
3043             if( !( o.subject && !o.predicate && !o.object ) ) {
3044                 processEditTerm( o );
3045             } else {
3046                 o.editTerm = "subject";
3047             }
3048 
3049             var subject, predicate, object;
3050             if( o.subject ) { subject = processResource(o.subject, o); }
3051             if( o.predicate ) { predicate = processResource(o.predicate, o); }
3052             if( o.object ) { object = processObject( o.object, o ); }
3053             
3054             var temp;
3055             var output = null;
3056             var type;
3057             var filters = processSourceFilter( o.sources, o );
3058             if( o.subject && !o.object && !o.predicate ) {
3059                 output = subject;
3060             } else if( o.editTerm==="object" || !o.editTerm ) {
3061                 if( object ) { output = object; }
3062                 else {
3063                     temp = $.rdfwidgets.any( (subject ? subject : undefined), (predicate ? predicate : undefined), undefined, undefined, o);
3064                     if( temp ) { output = temp; }
3065                 }
3066             } else if( o.editTerm==="predicate" ) {
3067                 if( predicate ) { output = predicate; }
3068                 else {
3069                     temp = $.rdfwidgets.any( (subject ? subject : undefined), undefined,(object ? object : undefined), undefined, o );
3070                     if( temp ) { output = temp; }
3071                 }
3072             } else if( o.editTerm==="subject" ) {
3073                 o.editTerm="subject";
3074                 if( subject ) { output = subject; }
3075                 else {
3076                     temp = $.rdfwidgets.any( undefined, (predicate ? predicate : undefined),(object ? object : undefined), undefined, o );
3077                     if( temp ) { output = temp; }
3078                 }
3079             }
3080             var stringLabel;
3081             if( !output ) {
3082                 return;
3083             } else if( output.termType === "symbol" ) {
3084                 stringLabel = doLabel(output, o);
3085                 this.rdfvalue = output;
3086             } else {
3087                 stringLabel = output.value;
3088                 this.rdfvalue = output;
3089             }
3090 
3091             if( o.selectable ) {
3092                 element.click( function(e) { 
3093                         e.stopPropagation();
3094                         setSelected( element, that.rdfvalue );
3095                     } );
3096             }
3097             var imageuri = null;
3098             if( o.mappings ) {
3099                 imageuri = o.mappings[output.value];
3100             }
3101             if( !imageuri && "symbol" === output.termType && o.editTerm !== "predicate" ) {
3102                 var im = isImage( output, o );
3103                 if( im ) {
3104                     imageuri = im.value;
3105                 }
3106             }
3107             if( !imageuri && "symbol" === output.termType ) {
3108                 var im = doImage( output, o );
3109                 if( im ) {
3110                     imageuri = im.value;
3111                 }
3112             }
3113             if( imageuri ) {
3114                 output = "<span class='rdfimage'><img class='rdfimage' style='display:none' "+"src='"+imageuri+"' title='"+doLabel(output,o)+" "+output.value+"'></img></span>"