mas01mj@640
|
1 /**********************************************************
|
mas01mj@640
|
2 Copyright (c) 2006, 2007
|
mas01mj@640
|
3 Lee Feigenbaum ( lee AT thefigtrees DOT net )
|
mas01mj@640
|
4 Elias Torres ( elias AT torrez DOT us )
|
mas01mj@640
|
5 Wing Yung ( wingerz AT gmail DOT com )
|
mas01mj@640
|
6 All rights reserved.
|
mas01mj@640
|
7
|
mas01mj@640
|
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
|
mas01mj@640
|
9 this software and associated documentation files (the "Software"), to deal in
|
mas01mj@640
|
10 the Software without restriction, including without limitation the rights to
|
mas01mj@640
|
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
mas01mj@640
|
12 of the Software, and to permit persons to whom the Software is furnished to do
|
mas01mj@640
|
13 so, subject to the following conditions:
|
mas01mj@640
|
14
|
mas01mj@640
|
15 The above copyright notice and this permission notice shall be included in all
|
mas01mj@640
|
16 copies or substantial portions of the Software.
|
mas01mj@640
|
17
|
mas01mj@640
|
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
mas01mj@640
|
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
mas01mj@640
|
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
mas01mj@640
|
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
mas01mj@640
|
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
mas01mj@640
|
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
mas01mj@640
|
24 SOFTWARE.
|
mas01mj@640
|
25 **********************************************************/
|
mas01mj@640
|
26
|
mas01mj@640
|
27 /**
|
mas01mj@640
|
28 * Example client interactions
|
mas01mj@640
|
29 *
|
mas01mj@640
|
30
|
mas01mj@640
|
31 var sparqler = new SPARQL.Service("http://sparql.org/sparql");
|
mas01mj@640
|
32 sparqler.addDefaultGraph("http://thefigtrees.net/lee/ldf-card"); // inherited by all (future) queries
|
mas01mj@640
|
33 sparqler.addNamedGraph("http://torrez.us/elias/foaf.rdf");
|
mas01mj@640
|
34 sparqler.setPrefix("foaf", "http://xmlns.com/foaf/0.1/"); // inherited by all (future) queries
|
mas01mj@640
|
35 sparqler.setPrefix("rdf", "http://xmlns.com/foaf/0.1/");
|
mas01mj@640
|
36
|
mas01mj@640
|
37 sparqler.setRequestHeader("Authentication", "Basic: " + basicAuthString);
|
mas01mj@640
|
38
|
mas01mj@640
|
39 //sparqler.wantOutputAs("application/json"); // for now we only do JSON
|
mas01mj@640
|
40
|
mas01mj@640
|
41 var query = sparqler.createQuery();
|
mas01mj@640
|
42 query.addDefualtGraph(...) query.addNamedGraph(...) query.setPrefix(...) query.setRequestHeader(...) // this query only
|
mas01mj@640
|
43
|
mas01mj@640
|
44 // query forms:
|
mas01mj@640
|
45
|
mas01mj@640
|
46 // passes standard JSON results object to success callback
|
mas01mj@640
|
47 query.setPrefix("ldf", "http://thefigtrees.net/lee/ldf-card#");
|
mas01mj@640
|
48 query.query("SELECT ?who ?mbox WHERE { ldf:LDF foaf:knows ?who . ?who foaf:mbox ?mbox }",
|
mas01mj@640
|
49 {failure: onFailure, success: function(json) { for (var x in json.head.vars) { ... } ...}}
|
mas01mj@640
|
50 );
|
mas01mj@640
|
51
|
mas01mj@640
|
52 // passes boolean value to success callback
|
mas01mj@640
|
53 query.ask("ASK ?person WHERE { ?person foaf:knows [ foaf:name "Dan Connolly" ] }",
|
mas01mj@640
|
54 {failure: onFailure, success: function(bool) { if (bool) ... }}
|
mas01mj@640
|
55 );
|
mas01mj@640
|
56
|
mas01mj@640
|
57 // passes a single vector (array) of values representing a single column of results to success callback
|
mas01mj@640
|
58 query.setPrefix("ldf", "http://thefigtrees.net/lee/ldf-card#");
|
mas01mj@640
|
59 var addresses = query.selectValues("SELECT ?mbox WHERE { _:someone foaf:mbox ?mbox }",
|
mas01mj@640
|
60 {failure: onFailure, success: function(values) { for (var i = 0; i < values.length; i++) { ... values[i] ...} } }
|
mas01mj@640
|
61 );
|
mas01mj@640
|
62
|
mas01mj@640
|
63 // passes a single value representing a single row of a single column (variable) to success callback
|
mas01mj@640
|
64 query.setPrefix("ldf", "http://thefigtrees.net/lee/ldf-card#");
|
mas01mj@640
|
65 var myAddress = query.selectSingleValue("SELECT ?mbox WHERE {ldf:LDF foaf:mbox ?mbox }",
|
mas01mj@640
|
66 {failure: onFailure, success: function(value) { alert("value is: " + value); } }
|
mas01mj@640
|
67 );
|
mas01mj@640
|
68
|
mas01mj@640
|
69 // shortcuts for all of the above (w/o ability to set any query-specific graphs or prefixes)
|
mas01mj@640
|
70 sparqler.query(...) sparqler.ask(...) sparqler.selectValues(...) sparqler.selectSingleValue(...)
|
mas01mj@640
|
71
|
mas01mj@640
|
72
|
mas01mj@640
|
73 */
|
mas01mj@640
|
74
|
mas01mj@640
|
75 var SPARQL = {}; // SPARQL namespace
|
mas01mj@640
|
76
|
mas01mj@640
|
77
|
mas01mj@640
|
78 /**
|
mas01mj@640
|
79 * Both SPARQL service objects and SPARQL query objects receive one query utility method
|
mas01mj@640
|
80 * per entry in this dictionary. The key is the name of the method, and the value is a function
|
mas01mj@640
|
81 * that transforms the standard JSON output into a more useful form. The return value of a
|
mas01mj@640
|
82 * transformation function is passed into any 'success' callback function provided when the query
|
mas01mj@640
|
83 * is issued. The following transformations are included:
|
mas01mj@640
|
84 * + query -- identity transform; returns the JSON structure unchanged
|
mas01mj@640
|
85 * + ask -- for ASK queries; returns a boolean value indicating the answer to the query
|
mas01mj@640
|
86 * + selectValues -- for SELECT queries with a single variable; returns an array containing
|
mas01mj@640
|
87 * the answers to the query
|
mas01mj@640
|
88 * + selectSingleValue -- for SELECT queries returning one column with one row; returns the
|
mas01mj@640
|
89 * value in the first (and presumably, only) cell in the resultset
|
mas01mj@640
|
90 * + selectValueArrays -- for SELECT queries returning independent columns; returns a hash
|
mas01mj@640
|
91 * keyed on variable name with values as arrays of answers for that variable. Useful
|
mas01mj@640
|
92 * for UNION queries.
|
mas01mj@640
|
93 * Note that all of the transformations that return values directly lose any type information
|
mas01mj@640
|
94 * and the ability to distinguish between URIs, blank nodes, and literals.
|
mas01mj@640
|
95 */
|
mas01mj@640
|
96 SPARQL._query_transformations = {
|
mas01mj@640
|
97 query: function (o) { return o; },
|
mas01mj@640
|
98 ask: function (o) { return o["boolean"]; },
|
mas01mj@640
|
99 selectValues: function (o) {
|
mas01mj@640
|
100 var v = o.head.vars[0]; // assume one variable
|
mas01mj@640
|
101 var values = [];
|
mas01mj@640
|
102 for (var i = 0; i < o.results.bindings.length; i++)
|
mas01mj@640
|
103 values.push(o.results.bindings[i][v].value);
|
mas01mj@640
|
104 return values;
|
mas01mj@640
|
105 },
|
mas01mj@640
|
106 selectSingleValue: function(o) { return o.results.bindings[0][o.head.vars[0]].value; },
|
mas01mj@640
|
107 selectValueArrays: function(o) {
|
mas01mj@640
|
108 // factor by value (useful for UNION queries)
|
mas01mj@640
|
109 var ret = {};
|
mas01mj@640
|
110 for (var i = 0; i < o.head.vars.length; i++)
|
mas01mj@640
|
111 ret[o.head.vars[i]] = [];
|
mas01mj@640
|
112 for (var i = 0; i < o.results.bindings.length; i++)
|
mas01mj@640
|
113 for (var v in o.results.bindings[i])
|
mas01mj@640
|
114 if (ret[v] instanceof Array) ret[v].push(o.results.bindings[i][v].value);
|
mas01mj@640
|
115 return ret;
|
mas01mj@640
|
116 },
|
mas01mj@640
|
117 selectValueHashes: function(o) {
|
mas01mj@640
|
118 var hashes = [];
|
mas01mj@640
|
119 for (var i = 0; i < o.results.bindings.length; i++) {
|
mas01mj@640
|
120 var hash = {};
|
mas01mj@640
|
121 for (var v in o.results.bindings[i])
|
mas01mj@640
|
122 hash[v] = o.results.bindings[i][v].value;
|
mas01mj@640
|
123 hashes.push(hash);
|
mas01mj@640
|
124 }
|
mas01mj@640
|
125 return hashes;
|
mas01mj@640
|
126 }
|
mas01mj@640
|
127 };
|
mas01mj@640
|
128
|
mas01mj@640
|
129 SPARQL.statistics = {
|
mas01mj@640
|
130 queries_sent : 0,
|
mas01mj@640
|
131 successes : 0,
|
mas01mj@640
|
132 failures : 0
|
mas01mj@640
|
133 };
|
mas01mj@640
|
134
|
mas01mj@640
|
135 // A SPARQL service represents a single endpoint which implements the HTTP (GET or POST)
|
mas01mj@640
|
136 // bindings of the SPARQL Protocol. It provides convenience methods to set dataset and
|
mas01mj@640
|
137 // prefix options for all queries created for this endpoint.
|
mas01mj@640
|
138 SPARQL.Service = function(endpoint) {
|
mas01mj@640
|
139 //---------------
|
mas01mj@640
|
140 // private fields
|
mas01mj@640
|
141 var _endpoint = endpoint;
|
mas01mj@640
|
142 var _default_graphs = [];
|
mas01mj@640
|
143 var _named_graphs = [];
|
mas01mj@640
|
144 var _prefix_map = {};
|
mas01mj@640
|
145 var _method = 'POST';
|
mas01mj@640
|
146 var _output = 'json';
|
mas01mj@640
|
147 var _max_simultaneous = 0;
|
mas01mj@640
|
148 var _request_headers = {};
|
mas01mj@640
|
149
|
mas01mj@640
|
150 //----------
|
mas01mj@640
|
151 // accessors
|
mas01mj@640
|
152 this.endpoint = function() { return _endpoint; };
|
mas01mj@640
|
153 this.defaultGraphs = function() { return _default_graphs; };
|
mas01mj@640
|
154 this.namedGraphs = function() { return _named_graphs; };
|
mas01mj@640
|
155 this.prefixes = function() { return _prefix_map; };
|
mas01mj@640
|
156 this.method = function() { return _method; };
|
mas01mj@640
|
157 this.output = function() { return _output; };
|
mas01mj@640
|
158 this.maxSimultaneousQueries = function() { return _max_simultaneous; };
|
mas01mj@640
|
159 this.requestHeaders = function() { return _request_headers; };
|
mas01mj@640
|
160
|
mas01mj@640
|
161 //---------
|
mas01mj@640
|
162 // mutators
|
mas01mj@640
|
163 function _add_graphs(toAdd, arr) {
|
mas01mj@640
|
164 if (toAdd instanceof Array)
|
mas01mj@640
|
165 for (var i = 0; i < toAdd.length; i++) arr.push(toAdd[i]);
|
mas01mj@640
|
166 else
|
mas01mj@640
|
167 arr.push(toAdd);
|
mas01mj@640
|
168 }
|
mas01mj@640
|
169 this.addDefaultGraph = function(g) { _add_graphs(g, this.defaultGraphs()); };
|
mas01mj@640
|
170 this.addNamedGraph = function(g) { _add_graphs(g, this.namedGraphs()); };
|
mas01mj@640
|
171 this.setPrefix = function(p, u) { this.prefixes()[p] = u; };
|
mas01mj@640
|
172 this.createQuery = function(p) { return new SPARQL.Query(this, p); };
|
mas01mj@640
|
173 this.setMethod = function(m) {
|
mas01mj@640
|
174 if (m != 'GET' && m != 'POST') throw("HTTP methods other than GET and POST are not supported.");
|
mas01mj@640
|
175 _method = m;
|
mas01mj@640
|
176 };
|
mas01mj@640
|
177 this.setOutput = function(o) { _output = o; };
|
mas01mj@640
|
178 this.setMaxSimultaneousQueries = function(m) { _max_simultaneous = m; };
|
mas01mj@640
|
179 this.setRequestHeader = function(h, v) { _request_headers[h] = v; };
|
mas01mj@640
|
180
|
mas01mj@640
|
181 //---------------
|
mas01mj@640
|
182 // protected methods (should only be called within this module
|
mas01mj@640
|
183 this._active_queries = 0;
|
mas01mj@640
|
184 this._queued_queries = [];
|
mas01mj@640
|
185 this._next_in_queue = 0;
|
mas01mj@640
|
186 this._canRun = function() { return this.maxSimultaneousQueries() <= 0 || this._active_queries < this.maxSimultaneousQueries();};
|
mas01mj@640
|
187 this._queue = function(q,f, p) {
|
mas01mj@640
|
188 if (!p) p = 0;
|
mas01mj@640
|
189 if (p > 0) {
|
mas01mj@640
|
190 for (var i = 0; i < this._queued_queries.length; i++) {
|
mas01mj@640
|
191 if (this._queued_queries[i] != null && this._queued_queries[i][2] < p) {
|
mas01mj@640
|
192 this._queued_queries.splice(i, 0, [q, f, p]);
|
mas01mj@640
|
193 return;
|
mas01mj@640
|
194 }
|
mas01mj@640
|
195 }
|
mas01mj@640
|
196 }
|
mas01mj@640
|
197 this._queued_queries.push([q,f,p]);
|
mas01mj@640
|
198 };
|
mas01mj@640
|
199 this._markRunning = function(q) { this._active_queries++; };
|
mas01mj@640
|
200 this._markDone = function(q) {
|
mas01mj@640
|
201 this._active_queries--;
|
mas01mj@640
|
202 //document.getElementById('log').innerHTML+="query done. " + this._active_queries + " queries still active.<br>";
|
mas01mj@640
|
203 if (this._queued_queries[this._next_in_queue] != null && this._canRun()) {
|
mas01mj@640
|
204 var a = this._queued_queries[this._next_in_queue];
|
mas01mj@640
|
205 this._queued_queries[this._next_in_queue++] = null;
|
mas01mj@640
|
206 // a[0] is query object, a[1] is function to run query
|
mas01mj@640
|
207 //document.getElementById('log').innerHTML += "running query from Q<br>";
|
mas01mj@640
|
208 a[1]();
|
mas01mj@640
|
209 }
|
mas01mj@640
|
210 };
|
mas01mj@640
|
211
|
mas01mj@640
|
212 //---------------
|
mas01mj@640
|
213 // public methods
|
mas01mj@640
|
214
|
mas01mj@640
|
215 // use our varied transformations to create the various shortcut methods of actually
|
mas01mj@640
|
216 // issuing queries without explicitly creating a query object
|
mas01mj@640
|
217 for (var query_form in SPARQL._query_transformations) {
|
mas01mj@640
|
218 // need the extra function to properly scope query_form (qf)
|
mas01mj@640
|
219 this[query_form] = (function(qf) {
|
mas01mj@640
|
220 return function(queryString, callback) {
|
mas01mj@640
|
221 var q = this.createQuery();
|
mas01mj@640
|
222 q._doQuery(queryString, callback, SPARQL._query_transformations[qf]);
|
mas01mj@640
|
223 };
|
mas01mj@640
|
224 })(query_form);
|
mas01mj@640
|
225 }
|
mas01mj@640
|
226
|
mas01mj@640
|
227 //------------
|
mas01mj@640
|
228 // constructor
|
mas01mj@640
|
229
|
mas01mj@640
|
230 if (!_endpoint)
|
mas01mj@640
|
231 return null;
|
mas01mj@640
|
232
|
mas01mj@640
|
233 return this;
|
mas01mj@640
|
234 }
|
mas01mj@640
|
235
|
mas01mj@640
|
236 /**
|
mas01mj@640
|
237 * A SPARQL query object should be created using the createQuery method of a SPARQL
|
mas01mj@640
|
238 * service object. It allows prefixes and datasets to be defined specifically for
|
mas01mj@640
|
239 * a single query, and provides introspective methods to see the query string and the
|
mas01mj@640
|
240 * full (HTTP GET) URL of the query.
|
mas01mj@640
|
241 */
|
mas01mj@640
|
242 SPARQL.Query = function(service, priority) {
|
mas01mj@640
|
243 //---------------
|
mas01mj@640
|
244 // private fields
|
mas01mj@640
|
245 var _conn = null;
|
mas01mj@640
|
246 var _service = service;
|
mas01mj@640
|
247 var _default_graphs = clone_obj(service.defaultGraphs()); // prevent future updates from affecting us
|
mas01mj@640
|
248 var _named_graphs = clone_obj(service.namedGraphs());
|
mas01mj@640
|
249 var _prefix_map = clone_obj(service.prefixes());
|
mas01mj@640
|
250 var _user_query = ''; // doesn't include auto-generated prefix declarations
|
mas01mj@640
|
251 var _method = service.method();
|
mas01mj@640
|
252 var _output = service.output();
|
mas01mj@640
|
253 var _priority = priority || 0;
|
mas01mj@640
|
254 var _request_headers = clone_obj(service.requestHeaders());
|
mas01mj@640
|
255
|
mas01mj@640
|
256 //------------------
|
mas01mj@640
|
257 // private functions
|
mas01mj@640
|
258 function _create_json(text) {
|
mas01mj@640
|
259 if (!text)
|
mas01mj@640
|
260 return null;
|
mas01mj@640
|
261 // make sure this is safe JSON
|
mas01mj@640
|
262 // see: http://www.ietf.org/internet-drafts/draft-crockford-jsonorg-json-03.txt
|
mas01mj@640
|
263
|
mas01mj@640
|
264 // (1) strip out quoted strings
|
mas01mj@640
|
265 var no_strings = text.replace(/"(\\.|[^"\\])*"/g, '');
|
mas01mj@640
|
266 // (2) make sure that all the characters are explicitly part of the JSON grammar
|
mas01mj@640
|
267 // (in particular, note as discussed in the IETF submission, there are no assignments
|
mas01mj@640
|
268 // or function invocations allowed by this reg. exp.)
|
mas01mj@640
|
269 var hasBadCharacter = /[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(no_strings);
|
mas01mj@640
|
270 // (3) evaluate the JSON string, returning its contents
|
mas01mj@640
|
271 if (!hasBadCharacter) {
|
mas01mj@640
|
272 try {
|
mas01mj@640
|
273 return eval('(' + text + ')');
|
mas01mj@640
|
274 } catch (e) {
|
mas01mj@640
|
275 return null;
|
mas01mj@640
|
276 }
|
mas01mj@640
|
277 }
|
mas01mj@640
|
278 return null;
|
mas01mj@640
|
279 }
|
mas01mj@640
|
280
|
mas01mj@640
|
281 function clone_obj(o) {
|
mas01mj@640
|
282 var o2 = o instanceof Array ? [] : {};
|
mas01mj@640
|
283 for(var x in o) {o2[x] = o[x];}
|
mas01mj@640
|
284 return o2;
|
mas01mj@640
|
285 }
|
mas01mj@640
|
286
|
mas01mj@640
|
287 //----------------
|
mas01mj@640
|
288 // private methods
|
mas01mj@640
|
289 this._doCallback = function(cb, which, arg) {
|
mas01mj@640
|
290 //document.getElementById('log').innerHTML += "_doCallback ... <br>";
|
mas01mj@640
|
291 var user_data = "argument" in cb ? cb.argument : null;
|
mas01mj@640
|
292 if (which in cb) {
|
mas01mj@640
|
293 if (cb.scope) {
|
mas01mj@640
|
294 cb[which].apply(cb.scope, [arg, user_data]);
|
mas01mj@640
|
295 } else {
|
mas01mj@640
|
296 cb[which](arg, user_data);
|
mas01mj@640
|
297 }
|
mas01mj@640
|
298 }
|
mas01mj@640
|
299 }
|
mas01mj@640
|
300
|
mas01mj@640
|
301 this._queryFailure = function(xhr, arg) {
|
mas01mj@640
|
302 SPARQL.statistics.failures++;
|
mas01mj@640
|
303 _service._markDone(this);
|
mas01mj@640
|
304 this._doCallback(arg.callback, 'failure', xhr /* just pass through the connection response object */);
|
mas01mj@640
|
305 };
|
mas01mj@640
|
306 this._querySuccess = function(xhr, arg) {
|
mas01mj@640
|
307 //alert(xhr.responseText);
|
mas01mj@640
|
308 SPARQL.statistics.successes++;
|
mas01mj@640
|
309 _service._markDone(this);
|
mas01mj@640
|
310 this._doCallback(arg.callback, 'success', arg.transformer(
|
mas01mj@640
|
311 _output == 'json' ? _create_json(xhr.responseText) : xhr.responseText
|
mas01mj@640
|
312 ));
|
mas01mj@640
|
313 };
|
mas01mj@640
|
314
|
mas01mj@640
|
315 function getXmlHttpRequest(url) {
|
mas01mj@640
|
316 // right now, this only does Firefox (Opera? Safari?)
|
mas01mj@640
|
317 return new XMLHttpRequest();
|
mas01mj@640
|
318 }
|
mas01mj@640
|
319
|
mas01mj@640
|
320 this._doQuery = function(queryString, callback, transformer) {
|
mas01mj@640
|
321 _user_query = queryString;
|
mas01mj@640
|
322 if (_service._canRun()) {
|
mas01mj@640
|
323 try {
|
mas01mj@640
|
324 if (_method != 'POST' && _method != 'GET')
|
mas01mj@640
|
325 throw("HTTP methods other than GET and POST are not supported.");
|
mas01mj@640
|
326
|
mas01mj@640
|
327 var url = _method == 'GET' ? this.queryUrl() : this.service().endpoint();
|
mas01mj@640
|
328 var xhr = getXmlHttpRequest(url);
|
mas01mj@640
|
329 var content = null;
|
mas01mj@640
|
330
|
mas01mj@640
|
331 try {
|
mas01mj@640
|
332 if (!document.domain || (url.match(/^https?:\/\//) && url.slice(7, document.domain.length + 7) != document.domain && window.netscape && netscape.security && netscape.security.PrivilegeManager)) {
|
mas01mj@640
|
333 netscape.security.PrivilegeManager.enablePrivilege( "UniversalBrowserRead");
|
mas01mj@640
|
334 netscape.security.PrivilegeManager.enablePrivilege( "UniversalXPConnect");
|
mas01mj@640
|
335 }
|
mas01mj@640
|
336 } catch(e) {
|
mas01mj@640
|
337 alert("Cross-site requests prohibited. You will only be able to SPARQL the origin site: " + e);
|
mas01mj@640
|
338 return;
|
mas01mj@640
|
339 }
|
mas01mj@640
|
340
|
mas01mj@640
|
341 xhr.open(_method, url, true /* async */);
|
mas01mj@640
|
342
|
mas01mj@640
|
343 // set the headers, including the content-type for POSTed queries
|
mas01mj@640
|
344 for (var header in this.requestHeaders())
|
mas01mj@640
|
345 if (typeof(this.requestHeaders()[header]) != "function")
|
mas01mj@640
|
346 xhr.setRequestHeader(header, this.requestHeaders()[header]);
|
mas01mj@640
|
347 if (_method == 'POST') {
|
mas01mj@640
|
348 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
mas01mj@640
|
349 content = this.queryParameters();
|
mas01mj@640
|
350 }
|
mas01mj@640
|
351
|
mas01mj@640
|
352 SPARQL.statistics.queries_sent++;
|
mas01mj@640
|
353 _service._markRunning(this);
|
mas01mj@640
|
354
|
mas01mj@640
|
355 var callbackData = {
|
mas01mj@640
|
356 scope:this,
|
mas01mj@640
|
357 success: this._querySuccess,
|
mas01mj@640
|
358 failure: this._queryFailure,
|
mas01mj@640
|
359 argument: {
|
mas01mj@640
|
360 transformer: transformer,
|
mas01mj@640
|
361 callback: callback
|
mas01mj@640
|
362 }
|
mas01mj@640
|
363 };
|
mas01mj@640
|
364
|
mas01mj@640
|
365 // I've seen some strange race-condition behavior (strange since normally
|
mas01mj@640
|
366 // JS is single-threaded, so synchronization conditions don't occur barring
|
mas01mj@640
|
367 // reentrancy) with onreadystatechange. Instead, we poll asynchronously to
|
mas01mj@640
|
368 // determine when the request is done.
|
mas01mj@640
|
369 var token = window.setInterval(
|
mas01mj@640
|
370 function () {
|
mas01mj@640
|
371 if (xhr.readyState == 4) { // ready!
|
mas01mj@640
|
372 // clear this handler
|
mas01mj@640
|
373 window.clearInterval(token);
|
mas01mj@640
|
374 // we check the status to know which callback to call
|
mas01mj@640
|
375 if (xhr.status >= 200 && xhr.status < 300)
|
mas01mj@640
|
376 callbackData.success.apply(callbackData.scope, [xhr, callbackData.argument]);
|
mas01mj@640
|
377 else
|
mas01mj@640
|
378 callbackData.failure.apply(callbackData.scope, [xhr, callbackData.argument]);
|
mas01mj@640
|
379 }
|
mas01mj@640
|
380 },
|
mas01mj@640
|
381 200 /* maybe this should be customizable */
|
mas01mj@640
|
382 );
|
mas01mj@640
|
383
|
mas01mj@640
|
384 xhr.send(content);
|
mas01mj@640
|
385 } catch (e) {
|
mas01mj@640
|
386 alert("Error sending SPARQL query: " + e);
|
mas01mj@640
|
387 }
|
mas01mj@640
|
388 } else {
|
mas01mj@640
|
389 var self = this;
|
mas01mj@640
|
390 _service._queue(self, function() { self._doQuery(queryString, callback, transformer); }, _priority);
|
mas01mj@640
|
391 }
|
mas01mj@640
|
392 };
|
mas01mj@640
|
393
|
mas01mj@640
|
394
|
mas01mj@640
|
395 //----------
|
mas01mj@640
|
396 // accessors
|
mas01mj@640
|
397 this.request = function() { return _conn; };
|
mas01mj@640
|
398 this.service = function() { return _service; };
|
mas01mj@640
|
399 this.defaultGraphs = function() { return _default_graphs; };
|
mas01mj@640
|
400 this.namedGraphs = function() { return _named_graphs; };
|
mas01mj@640
|
401 this.prefixes = function() { return _prefix_map; };
|
mas01mj@640
|
402 this.method = function() { return _method; };
|
mas01mj@640
|
403 this.requestHeaders = function() { return _request_headers; };
|
mas01mj@640
|
404
|
mas01mj@640
|
405
|
mas01mj@640
|
406 /**
|
mas01mj@640
|
407 * Returns the SPARQL query represented by this object. The parameter, which can
|
mas01mj@640
|
408 * be omitted, determines whether or not auto-generated PREFIX clauses are included
|
mas01mj@640
|
409 * in the returned query string.
|
mas01mj@640
|
410 */
|
mas01mj@640
|
411 this.queryString = function(excludePrefixes) {
|
mas01mj@640
|
412 var preamble = '';
|
mas01mj@640
|
413 if (!excludePrefixes) {
|
mas01mj@640
|
414 for (var prefix in this.prefixes()) {
|
mas01mj@640
|
415 if(typeof(this.prefixes()[prefix]) != 'string') continue;
|
mas01mj@640
|
416 preamble += 'PREFIX ' + prefix + ': <' + this.prefixes()[prefix] + '> ';
|
mas01mj@640
|
417 }
|
mas01mj@640
|
418 }
|
mas01mj@640
|
419 return preamble + _user_query;
|
mas01mj@640
|
420 };
|
mas01mj@640
|
421
|
mas01mj@640
|
422 /**
|
mas01mj@640
|
423 * Returns the HTTP query parameters to invoke this query. This includes entries for
|
mas01mj@640
|
424 * all of the default graphs, the named graphs, the SPARQL query itself, and an
|
mas01mj@640
|
425 * output parameter to specify JSON (or other) output is desired.
|
mas01mj@640
|
426 */
|
mas01mj@640
|
427 this.queryParameters = function () {
|
mas01mj@640
|
428 var urlQueryString = '';
|
mas01mj@640
|
429 var i;
|
mas01mj@640
|
430
|
mas01mj@640
|
431 // add default and named graphs to the protocol invocation
|
mas01mj@640
|
432 for (i = 0; i < this.defaultGraphs().length; i++) urlQueryString += 'default-graph-uri=' + encodeURIComponent(this.defaultGraphs()[i]) + '&';
|
mas01mj@640
|
433 for (i = 0; i < this.namedGraphs().length; i++) urlQueryString += 'named-graph-uri=' + encodeURIComponent(this.namedGraphs()[i]) + '&';
|
mas01mj@640
|
434
|
mas01mj@640
|
435 // specify JSON output (currently output= supported by latest Joseki) (or other output)
|
mas01mj@640
|
436 urlQueryString += 'output=' + _output + '&soft-limit=-1&';
|
mas01mj@640
|
437 return urlQueryString + 'query=' + encodeURIComponent(this.queryString());
|
mas01mj@640
|
438 }
|
mas01mj@640
|
439
|
mas01mj@640
|
440 /**
|
mas01mj@640
|
441 * Returns the HTTP GET URL to invoke this query. (Note that this returns a full HTTP GET URL
|
mas01mj@640
|
442 * even if this query is set to actually use POST.)
|
mas01mj@640
|
443 */
|
mas01mj@640
|
444 this.queryUrl = function() {
|
mas01mj@640
|
445 var url = this.service().endpoint() + '?';
|
mas01mj@640
|
446 return url + this.queryParameters();
|
mas01mj@640
|
447 };
|
mas01mj@640
|
448
|
mas01mj@640
|
449 //---------
|
mas01mj@640
|
450 // mutators
|
mas01mj@640
|
451 function _add_graphs(toAdd, arr) {
|
mas01mj@640
|
452 if (toAdd instanceof Array)
|
mas01mj@640
|
453 for (var i = 0; i < toAdd.length; i++) arr.push(toAdd[i]);
|
mas01mj@640
|
454 else
|
mas01mj@640
|
455 arr.push(toAdd);
|
mas01mj@640
|
456 }
|
mas01mj@640
|
457 this.addDefaultGraph = function(g) { _add_graphs(g, this.defaultGraphs()); };
|
mas01mj@640
|
458 this.addNamedGraph = function(g) { _add_graphs(g, this.namedGraphs()); };
|
mas01mj@640
|
459 this.setPrefix = function(p, u) { this.prefixes()[p] = u; };
|
mas01mj@640
|
460 this.setMethod = function(m) {
|
mas01mj@640
|
461 if (m != 'GET' && m != 'POST') throw("HTTP methods other than GET and POST are not supported.");
|
mas01mj@640
|
462 _method = m;
|
mas01mj@640
|
463 };
|
mas01mj@640
|
464 this.setRequestHeader = function(h, v) { _request_headers[h] = v; };
|
mas01mj@640
|
465
|
mas01mj@640
|
466 //---------------
|
mas01mj@640
|
467 // public methods
|
mas01mj@640
|
468
|
mas01mj@640
|
469 // use our varied transformations to create the various methods of actually issuing
|
mas01mj@640
|
470 // queries
|
mas01mj@640
|
471 for (var query_form in SPARQL._query_transformations) {
|
mas01mj@640
|
472 // need the extra function to properly scope query_form (qf)
|
mas01mj@640
|
473 this[query_form] = (function(qf) {
|
mas01mj@640
|
474 return function(queryString, callback) {
|
mas01mj@640
|
475 this._doQuery(queryString, callback, SPARQL._query_transformations[qf]);
|
mas01mj@640
|
476 };
|
mas01mj@640
|
477 })(query_form);
|
mas01mj@640
|
478 }
|
mas01mj@640
|
479
|
mas01mj@640
|
480
|
mas01mj@640
|
481 //------------
|
mas01mj@640
|
482 // constructor
|
mas01mj@640
|
483
|
mas01mj@640
|
484 return this;
|
mas01mj@640
|
485 }
|
mas01mj@640
|
486
|
mas01mj@640
|
487 // Nothing to see here, yet.
|
mas01mj@640
|
488 SPARQL.QueryUtilities = {
|
mas01mj@640
|
489 };
|
mas01mj@640
|
490
|