]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/javascript/util/RemoteRequest.js
more webby goodness
[working/Evergreen.git] / Open-ILS / src / javascript / util / RemoteRequest.js
1
2 var XML_HTTP_GATEWAY = "gateway";
3 var XML_HTTP_SERVER = "gapines.org";
4 var XML_HTTP_MAX_TRIES = 3;
5
6
7
8 /* true if we've been absorbed by a XUL app */
9 function isXUL() {
10         try {
11                 if(IAMXUL)
12                         return true;
13         } catch(E) {
14                 return false;
15         }
16 }
17
18
19 /* ----------------------------------------------------------------------- */
20 /* class methods */
21
22 /* keeping all requests in a global cache allows us to manage request
23         resends effectively */
24
25 /* Array of globally pending requests */
26 RemoteRequest.pending = new Array();
27
28 /* cleans requests (and null entries) from the pending array */
29 RemoteRequest.prunePending = function(id) {
30         var tmpArray = new Array();
31         for( var x in RemoteRequest.pending ) {
32                 if( RemoteRequest.pending[x] != null ) {
33                         var req = RemoteRequest.pending[x];
34                         if( req.id != id )
35                                 tmpArray.push(req);
36                         else 
37                                 req.clean();
38                 }
39         }
40         RemoteRequest.pending = tmpArray;
41 }
42
43 /* returns the number of pending requests */
44 RemoteRequest.numPending = function() {
45         return RemoteRequest.pending.length;
46 }
47
48 RemoteRequest.cancelAll = function() {
49         for( var x in RemoteRequest.pending ) {
50                 if( RemoteRequest.pending[x] != null ) {
51                         debug("Cancelling request...");
52                         var req = RemoteRequest.pending[x];
53                         req.cancelled = true;
54                         req.clean();
55                 }
56         }
57 }
58
59
60 /* ----------------------------------------------------------------------- */
61 /* Generic request manager */
62 function RequestBatch() {
63         this.requests = new Array();
64 }
65 RequestBatch.prototype.add = function(request) {
66         this.requests.push(request);
67 }
68
69 RequestBatch.prototype.remove = function(request) {
70         var newArray = new Array();
71         for( var i in this.requests ) {
72                 if( this.requests[i] != null &&
73                         this.requests[i].id != request.id )
74                         newArray.push(this.requests[i]);
75         }
76         this.requests = newArray;
77 }
78
79 RequestBatch.prototype.pending = function() {
80         return this.requests.length;
81 }
82
83 /* cancels all requests in this batch that have not already been sent */
84 RequestBatch.prototype.cancel = function() {
85         for(var i in this.requests) {
86                 if(this.requests[i] != null)
87                         this.requests.cancelled = true;
88         }
89 }
90
91 /* ----------------------------------------------------------------------- */
92 /* Request object */
93 function RemoteRequest( service, method ) {
94
95         this.service    = service;
96         this.method             = method;
97         this.xmlhttp    = false;
98         this.name               = null;
99         this.sendCount = 0;
100
101         this.type               = "POST"; /* default */
102         this.id                 = service + method + Math.random();
103         this.cancelled = false;
104
105         var i = 2;
106         this.params = ""; 
107
108         while(i < arguments.length) {
109                 var object = js2JSON(arguments[i++]);
110                 this.params += "&param=" + encodeURIComponent(object);
111                 debug("Remote Request adding param => " + object);
112         }
113
114         if(!this.params) { this.params = ""; }
115         this.param_string = "service=" + service + "&method=" + method + this.params;
116
117         if( ! this.type || ! this.service || ! this.method ) {
118                 alert( "ERROR IN REQUEST PARAMS");
119                 return null;
120         }
121
122         if( this.buildXMLRequest() == null )
123                 alert("NEWER BROWSER");
124 }
125
126 RemoteRequest.prototype.clean = function() {
127         this.xmlhttp.onreadystatechange = function(){};
128         this.callback = null;
129 }
130
131 /* constructs our XMLHTTPRequest object */
132 RemoteRequest.prototype.buildXMLRequest = function() {
133
134         try { 
135                 this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); 
136         } catch (e) {
137                 try { 
138                         this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 
139                 } catch (E) {
140                         this.xmlhttp = false;
141                 }
142         }
143
144         if (!this.xmlhttp && typeof XMLHttpRequest!='undefined') {
145                 this.xmlhttp = new XMLHttpRequest();
146         }
147
148         if(!this.xmlhttp) {
149                 alert("NEEDS NEWER JAVASCRIPT for XMLHTTPRequest()");
150                 return null;
151         }
152
153         if( this.callback )
154                 this.setCompleteCallback( this.callback );
155
156         return true;
157 }
158
159
160 /* define the callback we use when this request has received
161         all of its data */
162 RemoteRequest.prototype.setCompleteCallback = function(callback) {
163
164         if(this.cancelled) return;
165
166         var object = this;
167         var obj = this.xmlhttp;
168         this.callback = callback;
169
170         this.xmlhttp.onreadystatechange = function() {
171                 if( obj.readyState == 4 ) {
172
173                         try {
174                                 if(object.cancelled) return;
175                                 callback(object);
176
177                         } catch(E) {
178
179                                 debug("Processing Error in complete callback: [" + E + "]");
180
181                                 /* if we receive a communication error, retry the request up
182                                         to XML_HTTP_MAX_TRIES attempts */
183                                 if( instanceOf(E, EXCommunication) ) {
184
185                                         debug("Communication Error: [" + E + "]");
186                                         if(object.sendCount >= XML_HTTP_MAX_TRIES ) {
187                                                 if(isXUL()) {
188                                                         throw object;
189                                                 } else {
190                                                         alert("Arrrgghh, Matey! Error communicating:\n" +
191                                                                  E  + "\n" + object.param_string);
192                                                 }
193                                         } else {
194                                                 object.buildXMLRequest();
195                                                 object.send();
196                                                 return;
197                                         }
198                                 } else {
199                                         /* any other exception is alerted for now */
200                                         RemoteRequest.prunePending(object.id);
201                                         //alert("Exception: " + E);
202                                         throw E;
203                                 }
204                         } 
205
206                         /* on success, remove the request from the pending cache */
207                         RemoteRequest.prunePending(object.id);
208                 }
209         }
210 }
211
212
213 /* http by default.  This makes it https. *ONLY works when
214         embedded in a XUL app. */
215 RemoteRequest.prototype.setSecure = function(bool) {
216         this.secure = bool; 
217 }
218
219 /** Send the request 
220   * By default, all calls are asynchronous.  if 'blocking' is
221   * set to true, then the call will block until a response
222   * is received.  If blocking, callbacks will not be called.
223   * In other words, you can assume the data is avaiable 
224   * (getResponseObject()) as soon as the send call returns. 
225   */
226 RemoteRequest.prototype.send = function(blocking) {
227
228         if(this.cancelled) return;
229
230         if( this.sendCount == 0)
231                 RemoteRequest.pending.push(this);
232         else 
233                 debug("Resending request with id " + this.id 
234                                 + " and send count " + this.sendCount);
235         
236         /* determine the xmlhttp server dynamically */
237         var url = location.protocol + "//" + location.host + "/" + XML_HTTP_GATEWAY;
238
239         if(isXUL()) {
240                 if(this.secure)
241                         url =   "https://" + XML_HTTP_SERVER + "/" + XML_HTTP_GATEWAY;
242                 else
243                         //url = "http://" + XML_HTTP_SERVER + ":8080/" + XML_HTTP_GATEWAY;
244                         url =   "http://" + XML_HTTP_SERVER + "/" + XML_HTTP_GATEWAY;
245         }
246
247         var data = null;
248
249         if( this.type == 'GET' ) { 
250                 url +=  "?" + this.param_string; 
251         }
252
253         if(blocking) {
254                 this.xmlhttp.open(this.type, url, false);
255         } else {
256                 this.xmlhttp.open(this.type, url, true);
257         }
258
259
260         if( this.type == 'POST' ) {
261                 data = this.param_string;
262                 this.xmlhttp.setRequestHeader('Content-Type',
263                                 'application/x-www-form-urlencoded');
264         }
265
266         //alert("Sending from " + location.href + " to " + url + " data [" + data + "]");
267         this.xmlhttp.send( data );
268         this.sendCount += 1;
269         debug("Remote Request done sending");
270         return this;
271 }
272
273 /* returns the actual response text from the request */
274 RemoteRequest.prototype.getText = function() {
275         return this.xmlhttp.responseText;
276 }
277
278 RemoteRequest.prototype.isReady = function() {
279         return this.xmlhttp.readyState == 4;
280 }
281
282
283 /* returns the JSON->js result object  */
284 RemoteRequest.prototype.getResultObject = function() {
285         if(this.cancelled) return null;
286
287         var text = this.xmlhttp.responseText;
288         var obj = JSON2js(text);
289
290         if(obj == null) {
291                 debug("received null response");
292                 return null;
293         }
294
295         if(obj.is_err) { 
296                 if( obj.err_msg.match("OpenSRF::EX::User") ) {
297                         alert("Session has timed out or cannot be authenticated.\nPlease log out and log back in if necessary.");
298                         return;
299                 }
300
301                 debug("Something's Wrong: " + js2JSON(obj));
302                 throw new EXCommunication(obj.err_msg); 
303         }
304
305         if( obj[0] != null && obj[1] == null ) 
306                 obj = obj[0];
307
308         /* these are user level exceptions from the server code */
309         if(instanceOf(obj, ex)) {
310                 debug("Received user level exception: " + obj.err_msg());
311                 /* the opac will go ahead and spit out the error msg */
312                 if(!isXUL()) alert(obj.err_msg());
313                 throw obj;
314         }
315
316         if(instanceOf(obj, perm_ex)) {
317                 debug("Received permission error: " + obj.err_msg());
318                 /* the opac will go ahead and spit out the error msg */
319                 if(!isXUL()) alert(obj.err_msg());
320                 throw obj;
321         }
322
323
324         return obj;
325 }
326
327 /* adds a new parameter to the request */
328 RemoteRequest.prototype.addParam = function(param) {
329         var string = encodeURIComponent(js2JSON(param));
330         this.param_string += "&param=" + string;
331 }
332