otsdaq_utilities  v2_05_02_indev
ajax.js
1 var Ajax = function(table){
2 
3  this.table = table; //hold Tabulator object
4  this.config = false; //hold config object for ajax request
5  this.url = ""; //request URL
6  this.urlGenerator = false;
7  this.params = false; //request parameters
8 
9  this.loaderElement = this.createLoaderElement(); //loader message div
10  this.msgElement = this.createMsgElement(); //message element
11  this.loadingElement = false;
12  this.errorElement = false;
13  this.loaderPromise = false;
14 
15  this.progressiveLoad = false;
16  this.loading = false;
17 
18  this.requestOrder = 0; //prevent requests comming out of sequence if overridden by another load request
19 };
20 
21 //initialize setup options
22 Ajax.prototype.initialize = function(){
23  var template;
24 
25  this.loaderElement.appendChild(this.msgElement);
26 
27  if(this.table.options.ajaxLoaderLoading){
28  if(typeof this.table.options.ajaxLoaderLoading == "string"){
29  template = document.createElement('template');
30  template.innerHTML = this.table.options.ajaxLoaderLoading.trim();
31  this.loadingElement = template.content.firstChild;
32  }else{
33  this.loadingElement = this.table.options.ajaxLoaderLoading;
34  }
35  }
36 
37  this.loaderPromise = this.table.options.ajaxRequestFunc || this.defaultLoaderPromise;
38 
39  this.urlGenerator = this.table.options.ajaxURLGenerator || this.defaultURLGenerator;
40 
41  if(this.table.options.ajaxLoaderError){
42  if(typeof this.table.options.ajaxLoaderError == "string"){
43  template = document.createElement('template');
44  template.innerHTML = this.table.options.ajaxLoaderError.trim();
45  this.errorElement = template.content.firstChild;
46  }else{
47  this.errorElement = this.table.options.ajaxLoaderError;
48  }
49  }
50 
51  if(this.table.options.ajaxParams){
52  this.setParams(this.table.options.ajaxParams);
53  }
54 
55  if(this.table.options.ajaxConfig){
56  this.setConfig(this.table.options.ajaxConfig);
57  }
58 
59  if(this.table.options.ajaxURL){
60  this.setUrl(this.table.options.ajaxURL);
61  }
62 
63  if(this.table.options.ajaxProgressiveLoad){
64  if(this.table.options.pagination){
65  this.progressiveLoad = false;
66  console.error("Progressive Load Error - Pagination and progressive load cannot be used at the same time");
67  }else{
68  if(this.table.modExists("page")){
69  this.progressiveLoad = this.table.options.ajaxProgressiveLoad;
70  this.table.modules.page.initializeProgressive(this.progressiveLoad);
71  }else{
72  console.error("Pagination plugin is required for progressive ajax loading");
73  }
74  }
75  }
76 };
77 
78 Ajax.prototype.createLoaderElement = function (){
79  var el = document.createElement("div");
80  el.classList.add("tabulator-loader");
81  return el;
82 };
83 
84 Ajax.prototype.createMsgElement = function (){
85  var el = document.createElement("div");
86 
87  el.classList.add("tabulator-loader-msg");
88  el.setAttribute("role", "alert");
89 
90  return el;
91 };
92 
93 //set ajax params
94 Ajax.prototype.setParams = function(params, update){
95  if(update){
96  this.params = this.params || {};
97 
98  for(let key in params){
99  this.params[key] = params[key];
100  }
101  }else{
102  this.params = params;
103  }
104 };
105 
106 Ajax.prototype.getParams = function(){
107  return this.params || {};
108 };
109 
110 //load config object
111 Ajax.prototype.setConfig = function(config){
112  this._loadDefaultConfig();
113 
114  if(typeof config == "string"){
115  this.config.method = config;
116  }else{
117  for(let key in config){
118  this.config[key] = config[key];
119  }
120  }
121 };
122 
123 //create config object from default
124 Ajax.prototype._loadDefaultConfig = function(force){
125  var self = this;
126  if(!self.config || force){
127 
128  self.config = {};
129 
130  //load base config from defaults
131  for(let key in self.defaultConfig){
132  self.config[key] = self.defaultConfig[key];
133  }
134  }
135 };
136 
137 //set request url
138 Ajax.prototype.setUrl = function(url){
139  this.url = url;
140 };
141 
142 //get request url
143 Ajax.prototype.getUrl = function(){
144  return this.url;
145 };
146 
147 //lstandard loading function
148 Ajax.prototype.loadData = function(inPosition){
149  var self = this;
150 
151  if(this.progressiveLoad){
152  return this._loadDataProgressive();
153  }else{
154  return this._loadDataStandard(inPosition);
155  }
156 };
157 
158 Ajax.prototype.nextPage = function(diff){
159  var margin;
160 
161  if(!this.loading){
162 
163  margin = this.table.options.ajaxProgressiveLoadScrollMargin || (this.table.rowManager.getElement().clientHeight * 2);
164 
165  if(diff < margin){
166  this.table.modules.page.nextPage()
167  .then(()=>{}).catch(()=>{});
168  }
169  }
170 };
171 
172 Ajax.prototype.blockActiveRequest = function(){
173  this.requestOrder ++;
174 };
175 
176 Ajax.prototype._loadDataProgressive = function(){
177  this.table.rowManager.setData([]);
178  return this.table.modules.page.setPage(1);
179 };
180 
181 Ajax.prototype._loadDataStandard = function(inPosition){
182  return new Promise((resolve, reject)=>{
183  this.sendRequest(inPosition)
184  .then((data)=>{
185  this.table.rowManager.setData(data, inPosition)
186  .then(()=>{
187  resolve();
188  })
189  .catch((e)=>{
190  reject(e)
191  });
192  })
193  .catch((e)=>{
194  reject(e)
195  });
196  });
197 };
198 
199 Ajax.prototype.generateParamsList = function(data, prefix){
200  var self = this,
201  output = [];
202 
203  prefix = prefix || "";
204 
205  if ( Array.isArray(data) ) {
206  data.forEach(function(item, i){
207  output = output.concat(self.generateParamsList(item, prefix ? prefix + "[" + i + "]" : i));
208  });
209  }else if (typeof data === "object"){
210  for (var key in data){
211  output = output.concat(self.generateParamsList(data[key], prefix ? prefix + "[" + key + "]" : key));
212  }
213  }else{
214  output.push({key:prefix, value:data});
215  }
216 
217  return output;
218 };
219 
220 
221 Ajax.prototype.serializeParams = function(params){
222  var output = this.generateParamsList(params),
223  encoded = [];
224 
225  output.forEach(function(item){
226  encoded.push(encodeURIComponent(item.key) + "=" + encodeURIComponent(item.value));
227  });
228 
229  return encoded.join("&");
230 };
231 
232 
233 //send ajax request
234 Ajax.prototype.sendRequest = function(silent){
235  var self = this,
236  url = self.url,
237  requestNo, esc, query;
238 
239  self.requestOrder ++;
240  requestNo = self.requestOrder;
241 
242  self._loadDefaultConfig();
243 
244  return new Promise((resolve, reject)=>{
245  if(self.table.options.ajaxRequesting.call(this.table, self.url, self.params) !== false){
246 
247  self.loading = true;
248 
249  if(!silent){
250  self.showLoader();
251  }
252 
253  this.loaderPromise(url, self.config, self.params).then((data)=>{
254  if(requestNo === self.requestOrder){
255  if(self.table.options.ajaxResponse){
256  data = self.table.options.ajaxResponse.call(self.table, self.url, self.params, data);
257  }
258  resolve(data);
259  }else{
260  console.warn("Ajax Response Blocked - An active ajax request was blocked by an attempt to change table data while the request was being made");
261  }
262 
263  self.hideLoader();
264 
265  self.loading = false;
266  })
267  .catch((error)=>{
268  console.error("Ajax Load Error: ", error);
269  self.table.options.ajaxError.call(self.table, error);
270 
271  self.showError();
272 
273  setTimeout(function(){
274  self.hideLoader();
275  }, 3000);
276 
277  self.loading = false;
278 
279  reject();
280  });
281  }else{
282  reject();
283  }
284  });
285 
286 
287 };
288 
289 Ajax.prototype.showLoader = function(){
290  var shouldLoad = typeof this.table.options.ajaxLoader === "function" ? this.table.options.ajaxLoader() : this.table.options.ajaxLoader;
291 
292  if(shouldLoad){
293 
294  this.hideLoader();
295 
296  while(this.msgElement.firstChild) this.msgElement.removeChild(this.msgElement.firstChild);
297  this.msgElement.classList.remove("tabulator-error");
298  this.msgElement.classList.add("tabulator-loading");
299 
300  if(this.loadingElement){
301  this.msgElement.appendChild(this.loadingElement);
302  }else{
303  this.msgElement.innerHTML = this.table.modules.localize.getText("ajax|loading");
304  }
305 
306  this.table.element.appendChild(this.loaderElement);
307  }
308 };
309 
310 Ajax.prototype.showError = function(){
311  this.hideLoader();
312 
313  while(this.msgElement.firstChild) this.msgElement.removeChild(this.msgElement.firstChild);
314  this.msgElement.classList.remove("tabulator-loading");
315  this.msgElement.classList.add("tabulator-error");
316 
317  if(this.errorElement){
318  this.msgElement.appendChild(this.errorElement);
319  }else{
320  this.msgElement.innerHTML = this.table.modules.localize.getText("ajax|error");
321  }
322 
323  this.table.element.appendChild(this.loaderElement);
324 };
325 
326 Ajax.prototype.hideLoader = function(){
327  if(this.loaderElement.parentNode){
328  this.loaderElement.parentNode.removeChild(this.loaderElement);
329  }
330 };
331 
332 //default ajax config object
333 Ajax.prototype.defaultConfig = {
334  method: "GET",
335 };
336 
337 Ajax.prototype.defaultURLGenerator = function(url, config, params){
338 
339  if(url){
340  if(params && Object.keys(params).length){
341  if(!config.method || config.method.toLowerCase() == "get"){
342  config.method = "get";
343 
344  url += (url.includes("?") ? "&" : "?") + this.serializeParams(params);
345  }
346  }
347  }
348 
349  return url;
350 };
351 
352 Ajax.prototype.defaultLoaderPromise = function(url, config, params){
353  var self = this, contentType;
354 
355  return new Promise(function(resolve, reject){
356 
357  //set url
358  url = self.urlGenerator(url, config, params);
359 
360  //set body content if not GET request
361  if(config.method.toUpperCase() != "GET"){
362  contentType = typeof self.table.options.ajaxContentType === "object" ? self.table.options.ajaxContentType : self.contentTypeFormatters[self.table.options.ajaxContentType];
363  if(contentType){
364 
365  for(var key in contentType.headers){
366  if(!config.headers){
367  config.headers = {};
368  }
369 
370  if(typeof config.headers[key] === "undefined"){
371  config.headers[key] = contentType.headers[key];
372  }
373  }
374 
375  config.body = contentType.body.call(self, url, config, params);
376 
377  }else{
378  console.warn("Ajax Error - Invalid ajaxContentType value:", self.table.options.ajaxContentType);
379  }
380  }
381 
382  if(url){
383 
384  //configure headers
385  if(typeof config.headers === "undefined"){
386  config.headers = {};
387  }
388 
389  if(typeof config.headers.Accept === "undefined"){
390  config.headers.Accept = "application/json";
391  }
392 
393  if(typeof config.headers["X-Requested-With"] === "undefined"){
394  config.headers["X-Requested-With"] = "XMLHttpRequest";
395  }
396 
397  if(typeof config.mode === "undefined"){
398  config.mode = "cors";
399  }
400 
401  if(config.mode == "cors"){
402 
403  if(typeof config.headers["Access-Control-Allow-Origin"] === "undefined"){
404  config.headers["Access-Control-Allow-Origin"] = window.location.origin;
405  }
406 
407  if(typeof config.credentials === "undefined"){
408  config.credentials = 'same-origin';
409  }
410  }else{
411  if(typeof config.credentials === "undefined"){
412  config.credentials = 'include';
413  }
414  }
415 
416  //send request
417  fetch(url, config)
418  .then((response)=>{
419  if(response.ok) {
420  response.json()
421  .then((data)=>{
422  resolve(data);
423  }).catch((error)=>{
424  reject(error);
425  console.warn("Ajax Load Error - Invalid JSON returned", error);
426  });
427  }else{
428  console.error("Ajax Load Error - Connection Error: " + response.status, response.statusText);
429  reject(response);
430  }
431  })
432  .catch((error)=>{
433  console.error("Ajax Load Error - Connection Error: ", error);
434  reject(error);
435  });
436  }else{
437  console.warn("Ajax Load Error - No URL Set");
438  resolve([]);
439  }
440 
441  });
442 };
443 
444 Ajax.prototype.contentTypeFormatters = {
445  "json":{
446  headers:{
447  'Content-Type': 'application/json',
448  },
449  body:function(url, config, params){
450  return JSON.stringify(params);
451  },
452  },
453  "form":{
454  headers:{
455  },
456  body:function(url, config, params){
457  var output = this.generateParamsList(params),
458  form = new FormData();
459 
460  output.forEach(function(item){
461  form.append(item.key, item.value);
462  });
463 
464  return form;
465  },
466  },
467 }
468 
469 Tabulator.prototype.registerModule("ajax", Ajax);