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