1 if(typeof Rico=='undefined') throw("LiveGridAjax requires the Rico JavaScript framework");
2 if(typeof RicoUtil=='undefined') throw("LiveGridAjax requires the RicoUtil object");
3 if(typeof Rico.Buffer=='undefined') throw("LiveGridAjax requires the Rico.Buffer object");
7 * Data source is a static XML file located on the server
9 Rico.Buffer.AjaxXML = Class.create();
11 Rico.Buffer.AjaxXML.prototype = {
13 initialize: function(url,options,ajaxOptions) {
14 Object.extend(this, new Rico.Buffer.Base());
15 Object.extend(this, new Rico.Buffer.AjaxXMLMethods);
17 this.options.bufferTimeout = 20000; // time to wait for ajax response (milliseconds)
18 this.options.requestParameters = [];
19 Object.extend(this.options, options || {});
20 this.ajaxOptions = { parameters: null, method : 'get' };
21 Object.extend(this.ajaxOptions, ajaxOptions || {});
23 this.processingRequest=false;
24 this.pendingRequest=-1;
28 Rico.Buffer.AjaxXMLMethods = function() {};
30 Rico.Buffer.AjaxXMLMethods.prototype = {
32 fetch: function(offset) {
33 if ( this.isInRange(offset) ) {
34 Rico.writeDebugMsg("AjaxXML fetch: in buffer");
35 this.liveGrid.refreshContents(offset);
38 this.processingRequest=true
39 Rico.writeDebugMsg("AjaxXML fetch, offset="+offset);
40 this.liveGrid.showMsg("Waiting for data...");
41 this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
42 this.sendAjaxRequest(offset,0,this.ajaxUpdate.bind(this,offset));
46 * Server did not respond in time... assume that there could have been
47 * an error, and allow requests to be processed again.
49 handleTimedOut: function() {
50 Rico.writeDebugMsg("Request Timed Out");
51 this.liveGrid.showMsg("Request for data timed out!");
54 formQueryHash: function(startPos,fetchSize) {
55 if (typeof fetchSize!='number') fetchSize=this.totalRows;
57 id: this.liveGrid.tableId,
61 if (!this.foundRowCount) queryHash['get_total']='true';
62 if (this.options.requestParameters) {
63 for ( var i=0; i < this.options.requestParameters.length; i++ ) {
64 var anArg = this.options.requestParameters[i];
65 if ( anArg.name != undefined && anArg.value != undefined ) {
66 queryHash[anArg.name]=anArg.value;
68 var ePos = anArg.indexOf('=');
69 var argName = anArg.substring( 0, ePos );
70 var argValue = anArg.substring( ePos + 1 );
71 queryHash[argName]=argValue;
77 Object.extend(queryHash,this.sortParm);
80 for (n=0; n<this.liveGrid.columns.length; n++) {
81 var c=this.liveGrid.columns[n];
82 if (c.filterType == Rico.TableColumn.UNFILTERED) continue;
83 queryHash['f['+c.index+'][op]']=c.filterOp;
84 queryHash['f['+c.index+'][len]']=c.filterValues.length
85 for (var i=0; i<c.filterValues.length; i++)
86 queryHash['f['+c.index+']['+i+']']=c.filterValues[i];
92 sendAjaxRequest: function(startPos,fetchSize,onComplete) {
93 this.ajaxOptions.parameters = this.formQueryHash(startPos,fetchSize);
94 this.ajaxOptions.onComplete = onComplete;
96 Rico.writeDebugMsg('req '+this.requestCount+':'+this.ajaxOptions.parameters.inspect());
97 new Ajax.Request(this.dataSource, this.ajaxOptions);
100 clearTimer: function() {
101 if(typeof this.timeoutHandler != "number") return;
102 window.clearTimeout(this.timeoutHandler);
103 delete this.timeoutHandler;
106 ajaxUpdate: function(startPos,request) {
108 this.processingRequest=false;
109 if (request.status != 200) {
110 Rico.writeDebugMsg("ajaxUpdate: received http error="+request.status);
111 this.liveGrid.showMsg('Received HTTP error: '+request.status);
114 var response = request.responseXML.getElementsByTagName("ajax-response");
115 if (response == null || response.length != 1) return;
116 this.updateBuffer(response[0],startPos);
117 this.CheckRowCount(response[0],startPos);
118 if (this.options.TimeOut && this.timerMsg)
119 this.restartSessionTimer();
120 if (this.options.onAjaxUpdate)
121 this.options.onAjaxUpdate();
122 if (this.pendingRequest>=0) {
123 var offset=this.pendingRequest;
124 Rico.writeDebugMsg("ajaxUpdate: found pending request for offset="+offset);
125 this.pendingRequest=-1;
130 CheckRowCount: function(ajaxResponse,offset) {
132 Rico.writeDebugMsg("CheckRowCount, size="+this.size+' rcv cnt type='+typeof(this.rowcntContent));
133 if (this.rcvdRowCount==true) {
134 Rico.writeDebugMsg("found row cnt: "+this.rowcntContent);
135 var eofrow=parseInt(this.rowcntContent);
136 var lastTotalRows=this.totalRows;
137 if (!isNaN(eofrow) && eofrow!=lastTotalRows) {
138 this.setTotalRows(eofrow);
139 var newpos=Math.min(this.liveGrid.topOfLastPage(),offset);
140 Rico.writeDebugMsg("CheckRowCount: new rowcnt="+eofrow+" newpos="+newpos);
141 if (lastTotalRows==0 && this.liveGrid.sizeTo=='data')
142 this.liveGrid.adjustPageSize();
143 //this.lastRowPos=-1;
144 this.liveGrid.scrollToRow(newpos);
145 if ( this.isInRange(newpos) ) {
146 this.liveGrid.refreshContents(newpos);
153 var lastbufrow=offset+this.rcvdRows;
154 if (lastbufrow>this.totalRows) {
155 var newcnt=lastbufrow;
156 Rico.writeDebugMsg("extending totrows to "+newcnt);
157 this.setTotalRows(newcnt);
160 var newpos=this.liveGrid.pixeltorow(this.liveGrid.scrollDiv.scrollTop);
161 Rico.writeDebugMsg("CheckRowCount: newpos="+newpos);
162 this.liveGrid.refreshContents(newpos);
165 // alert("Error in CheckRowCount:"+err.message);
169 updateBuffer: function(ajaxResponse, start) {
170 Rico.writeDebugMsg("updateBuffer: "+start);
172 var newRows = this.loadRows(ajaxResponse);
173 if (newRows==null) return;
174 this.rcvdRows = newRows.length;
175 Rico.writeDebugMsg("updateBuffer: # of rows="+this.rcvdRows);
176 if (this.rows.length == 0) { // initial load
178 this.startPos = start;
179 } else if (start > this.startPos) { //appending
180 if (this.startPos + this.rows.length < start) {
182 this.startPos = start;//
184 this.rows = this.rows.concat( newRows.slice(0, newRows.length));
185 if (this.rows.length > this.maxBufferSize) {
186 var fullSize = this.rows.length;
187 this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
188 this.startPos = this.startPos + (fullSize - this.rows.length);
191 } else { //prepending
192 if (start + newRows.length < this.startPos) {
195 this.rows = newRows.slice(0, this.startPos).concat(this.rows);
196 if (this.maxBufferSize && this.rows.length > this.maxBufferSize)
197 this.rows = this.rows.slice(0, this.maxBufferSize)
199 this.startPos = start;
201 this.size = this.rows.length;
204 loadRows: function(ajaxResponse) {
205 Rico.writeDebugMsg("loadRows");
206 this.rcvdRowCount = false;
207 var debugtags = ajaxResponse.getElementsByTagName('debug');
208 for (var i=0; i<debugtags.length; i++)
209 Rico.writeDebugMsg("loadRows, debug msg "+i+": "+RicoUtil.getContentAsString(debugtags[i],this.options.isEncoded));
210 var error = ajaxResponse.getElementsByTagName('error');
211 if (error.length > 0) {
212 var msg=RicoUtil.getContentAsString(error[0],this.options.isEncoded);
213 alert("Data provider returned an error:\n"+msg);
214 Rico.writeDebugMsg("Data provider returned an error:\n"+msg);
217 var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
218 var rowcnttags = ajaxResponse.getElementsByTagName('rowcount');
219 if (rowcnttags && rowcnttags.length==1) {
220 this.rowcntContent = RicoUtil.getContentAsString(rowcnttags[0],this.options.isEncoded);
221 this.rcvdRowCount = true;
222 this.foundRowCount = true;
223 Rico.writeDebugMsg("loadRows, found RowCount="+this.rowcntContent);
225 this.updateUI = rowsElement.getAttribute("update_ui") == "true";
226 this.rcvdOffset = rowsElement.getAttribute("offset");
227 Rico.writeDebugMsg("loadRows, rcvdOffset="+this.rcvdOffset);
228 return this.dom2jstable(rowsElement);
235 Rico.Buffer.AjaxSQL = Class.create();
237 Rico.Buffer.AjaxSQL.prototype = {
239 initialize: function(url,options,ajaxOptions) {
240 Object.extend(this, new Rico.Buffer.AjaxXML());
241 Object.extend(this, new Rico.Buffer.AjaxSQLMethods());
243 this.options.canFilter=true;
244 this.options.largeBufferSize = 7.0; // 7 pages
245 this.options.nearLimitFactor = 1.0; // 1 page
246 Object.extend(this.options, options || {});
247 Object.extend(this.ajaxOptions, ajaxOptions || {});
252 Rico.Buffer.AjaxSQLMethods = function() {};
254 Rico.Buffer.AjaxSQLMethods.prototype = {
256 registerGrid: function(liveGrid) {
257 this.liveGrid = liveGrid;
258 this.sessionExpired=false;
259 this.timerMsg=$(liveGrid.tableId+'_timer');
260 if (this.options.TimeOut && this.timerMsg) {
261 if (!this.timerMsg.title) this.timerMsg.title=RicoTranslate.getPhrase("minutes before your session expires")
262 this.restartSessionTimer();
266 setBufferSize: function(pageSize) {
267 this.maxFetchSize = Math.max(50,parseInt(this.options.largeBufferSize * pageSize));
268 this.nearLimit = parseInt(this.options.nearLimitFactor * pageSize);
269 this.maxBufferSize = this.maxFetchSize * 3;
272 restartSessionTimer: function() {
273 if (this.sessionExpired==true) return;
274 this.timeRemaining=this.options.TimeOut+1;
275 if (this.sessionTimer) clearTimeout(this.sessionTimer);
276 this.updateSessionTimer();
279 updateSessionTimer: function() {
280 if (--this.timeRemaining<=0) {
281 this.displaySessionTimer(RicoTranslate.getPhrase("EXPIRED"));
282 this.timerMsg.style.backgroundColor="red";
283 this.sessionExpired=true;
285 this.displaySessionTimer(this.timeRemaining);
286 this.sessionTimer=setTimeout(this.updateSessionTimer.bind(this),60000);
290 displaySessionTimer: function(msg) {
291 this.timerMsg.innerHTML=' '+msg+' ';
294 refresh: function() {
295 this.fetch(this.lastOffset);
299 * Fetch data from database.
300 * @param offset position (row) within the dataset (-1=clear existing buffer before issuing request)
302 fetch: function(offset) {
303 Rico.writeDebugMsg("AjaxSQL fetch, offset="+offset+' lastOffset='+this.lastOffset);
304 if (this.processingRequest) {
305 Rico.writeDebugMsg("AjaxSQL fetch: queue request");
306 this.pendingRequest=offset;
311 this.setTotalRows(0);
312 this.foundRowCount = false;
315 var lastOffset = this.lastOffset;
316 this.lastOffset = offset;
317 var inRange=this.isInRange(offset);
319 Rico.writeDebugMsg("AjaxSQL fetch: in buffer");
320 this.liveGrid.refreshContents(offset);
321 if (offset > lastOffset) {
322 if (offset+this.liveGrid.pageSize < this.endPos()-this.nearLimit) return;
323 if (this.endPos()==this.totalRows && this.foundRowCount) return;
324 } else if (offset < lastOffset) {
325 if (offset > this.startPos+this.nearLimit) return;
326 if (this.startPos==0) return;
329 if (offset >= this.totalRows && this.foundRowCount) return;
331 this.processingRequest=true
332 Rico.writeDebugMsg("AjaxSQL fetch, processing offset="+offset);
333 var bufferStartPos = this.getFetchOffset(offset);
334 var fetchSize = this.getFetchSize(bufferStartPos);
335 var partialLoaded = false;
337 if (!inRange) this.liveGrid.showMsg("Waiting for data...");
338 this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
339 this.sendAjaxRequest(bufferStartPos,fetchSize,this.ajaxUpdate.bind(this,bufferStartPos));
342 getFetchSize: function(adjustedOffset) {
343 var adjustedSize = 0;
344 if (adjustedOffset >= this.startPos) { //appending
345 var endFetchOffset = this.maxFetchSize + adjustedOffset;
346 adjustedSize = endFetchOffset - adjustedOffset;
347 if(adjustedOffset == 0 && adjustedSize < this.maxFetchSize)
348 adjustedSize = this.maxFetchSize;
349 Rico.writeDebugMsg("getFetchSize/append, adjustedSize="+adjustedSize+" adjustedOffset="+adjustedOffset+' endFetchOffset='+endFetchOffset);
350 } else { //prepending
351 adjustedSize = Math.min(this.startPos - adjustedOffset,this.maxFetchSize);
356 getFetchOffset: function(offset) {
357 var adjustedOffset = offset;
358 if (offset > this.startPos)
359 adjustedOffset = Math.max(offset, this.endPos()); //appending
360 else if (offset + this.maxFetchSize >= this.startPos)
361 adjustedOffset = Math.max(this.startPos - this.maxFetchSize, 0); //prepending
362 return adjustedOffset;
365 sortBuffer: function(colnum,sortdir,coltype) {
367 if (this.options.sortParmFmt && this.options.sortParmFmt=='displayName') {
368 this.sortParm['sort_col']=this.liveGrid.columns[colnum].displayName.toLowerCase();
369 this.sortParm['sort_dir']=sortdir;
371 this.sortParm['s'+colnum]=sortdir;
376 exportAllRows: function(populate,finish) {
377 this.exportPopulate=populate;
378 this.exportFinish=finish;
379 this.liveGrid.showMsg("Waiting for data...");
380 this.sendExportRequest(0);
384 * Make ajax request for print window data
386 sendExportRequest: function(offset) {
387 this.timeoutHandler = setTimeout(this.exportTimedOut.bind(this), this.options.bufferTimeout);
388 this.sendAjaxRequest(offset,200,this.exportAppend.bind(this,offset));
391 exportTimedOut: function() {
392 Rico.writeDebugMsg("Print Request Timed Out");
393 this.liveGrid.showMsg("Request for data timed out!");
397 exportAppend: function(startPos,request) {
399 var response = request.responseXML.getElementsByTagName("ajax-response");
400 if (response == null || response.length != 1) return;
401 var rowsElement = response[0].getElementsByTagName('rows')[0];
402 var rows=this.dom2jstable(rowsElement);
403 this.exportPopulate(rows);
407 this.sendExportRequest(startPos+rows.length);
412 Rico.includeLoaded('ricoLiveGridAjax.js');