otsdaq_utilities  v2_05_02_indev
format.js
1 var Format = function(table){
2  this.table = table; //hold Tabulator object
3 };
4 
5 //initialize column formatter
6 Format.prototype.initializeColumn = function(column){
7  var self = this,
8  config = {params:column.definition.formatterParams || {}};
9 
10  //set column formatter
11  switch(typeof column.definition.formatter){
12  case "string":
13 
14  if(column.definition.formatter === "tick"){
15  column.definition.formatter = "tickCross";
16 
17  if(typeof config.params.crossElement == "undefined"){
18  config.params.crossElement = false;
19  }
20 
21  console.warn("DEPRECATION WARNING - the tick formatter has been deprecated, please use the tickCross formatter with the crossElement param set to false");
22  }
23 
24  if(self.formatters[column.definition.formatter]){
25  config.formatter = self.formatters[column.definition.formatter];
26  }else{
27  console.warn("Formatter Error - No such formatter found: ", column.definition.formatter);
28  config.formatter = self.formatters.plaintext;
29  }
30  break;
31 
32  case "function":
33  config.formatter = column.definition.formatter;
34  break;
35 
36  default:
37  config.formatter = self.formatters.plaintext;
38  break;
39  }
40 
41  column.modules.format = config;
42 };
43 
44 Format.prototype.cellRendered = function(cell){
45  if(cell.modules.format && cell.modules.format.renderedCallback){
46  cell.modules.format.renderedCallback();
47  }
48 };
49 
50 //return a formatted value for a cell
51 Format.prototype.formatValue = function(cell){
52  var component = cell.getComponent(),
53  params = typeof cell.column.modules.format.params === "function" ? cell.column.modules.format.params(component) : cell.column.modules.format.params;
54 
55  function onRendered(callback){
56  if(!cell.modules.format){
57  cell.modules.format = {};
58  }
59 
60  cell.modules.format.renderedCallback = callback;
61  }
62 
63  return cell.column.modules.format.formatter.call(this, component, params, onRendered);
64 };
65 
66 
67 Format.prototype.sanitizeHTML = function(value){
68  if(value){
69  var entityMap = {
70  '&': '&',
71  '<': '&lt;',
72  '>': '&gt;',
73  '"': '&quot;',
74  "'": '&#39;',
75  '/': '&#x2F;',
76  '`': '&#x60;',
77  '=': '&#x3D;'
78  };
79 
80  return String(value).replace(/[&<>"'`=\/]/g, function (s) {
81  return entityMap[s];
82  });
83  }else{
84  return value;
85  }
86 };
87 
88 Format.prototype.emptyToSpace = function(value){
89  return value === null || typeof value === "undefined" ? "&nbsp;" : value;
90 };
91 
92 //get formatter for cell
93 Format.prototype.getFormatter = function(formatter){
94  var formatter;
95 
96  switch(typeof formatter){
97  case "string":
98  if(this.formatters[formatter]){
99  formatter = this.formatters[formatter]
100  }else{
101  console.warn("Formatter Error - No such formatter found: ", formatter);
102  formatter = this.formatters.plaintext;
103  }
104  break;
105 
106  case "function":
107  formatter = formatter;
108  break;
109 
110  default:
111  formatter = this.formatters.plaintext;
112  break;
113  }
114 
115  return formatter;
116 
117 };
118 
119 //default data formatters
120 Format.prototype.formatters = {
121  //plain text value
122  plaintext:function(cell, formatterParams, onRendered){
123  return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
124  },
125 
126  //html text value
127  html:function(cell, formatterParams, onRendered){
128  return cell.getValue();
129  },
130 
131  //multiline text area
132  textarea:function(cell, formatterParams, onRendered){
133  cell.getElement().style.whiteSpace = "pre-wrap";
134  return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
135  },
136 
137  //currency formatting
138  money:function(cell, formatterParams, onRendered){
139  var floatVal = parseFloat(cell.getValue()),
140  number, integer, decimal, rgx;
141 
142  var decimalSym = formatterParams.decimal || ".";
143  var thousandSym = formatterParams.thousand || ",";
144  var symbol = formatterParams.symbol || "";
145  var after = !!formatterParams.symbolAfter;
146  var precision = typeof formatterParams.precision !== "undefined" ? formatterParams.precision : 2;
147 
148  if(isNaN(floatVal)){
149  return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
150  }
151 
152  number = precision !== false ? floatVal.toFixed(precision) : floatVal;
153  number = String(number).split(".");
154 
155  integer = number[0];
156  decimal = number.length > 1 ? decimalSym + number[1] : "";
157 
158  rgx = /(\d+)(\d{3})/;
159 
160  while (rgx.test(integer)){
161  integer = integer.replace(rgx, "$1" + thousandSym + "$2");
162  }
163 
164  return after ? integer + decimal + symbol : symbol + integer + decimal;
165  },
166 
167  //clickable anchor tag
168  link:function(cell, formatterParams, onRendered){
169  var value = cell.getValue(),
170  urlPrefix = formatterParams.urlPrefix || "",
171  download = formatterParams.download,
172  label = value,
173  el = document.createElement("a"),
174  data;
175 
176  if(formatterParams.labelField){
177  data = cell.getData();
178  label = data[formatterParams.labelField];
179  }
180 
181  if(formatterParams.label){
182  switch(typeof formatterParams.label){
183  case "string":
184  label = formatterParams.label;
185  break;
186 
187  case "function":
188  label = formatterParams.label(cell);
189  break;
190  }
191  }
192 
193  if(label){
194  if(formatterParams.urlField){
195  data = cell.getData();
196  value = data[formatterParams.urlField];
197  }
198 
199  if(formatterParams.url){
200  switch(typeof formatterParams.url){
201  case "string":
202  value = formatterParams.url;
203  break;
204 
205  case "function":
206  value = formatterParams.url(cell);
207  break;
208  }
209  }
210 
211  el.setAttribute("href", urlPrefix + value);
212 
213  if(formatterParams.target){
214  el.setAttribute("target", formatterParams.target);
215  }
216 
217  if(formatterParams.download){
218 
219  if(typeof download == "function"){
220  download = download(cell);
221  }else{
222  download = download === true ? "" : download;
223  }
224 
225  el.setAttribute("download", download);
226  }
227 
228  el.innerHTML = this.emptyToSpace(this.sanitizeHTML(label));
229 
230  return el;
231  }else{
232  return "&nbsp;";
233  }
234  },
235 
236  //image element
237  image:function(cell, formatterParams, onRendered){
238  var el = document.createElement("img");
239  el.setAttribute("src", cell.getValue());
240 
241  switch(typeof formatterParams.height){
242  case "number":
243  el.style.height = formatterParams.height + "px";
244  break;
245 
246  case "string":
247  el.style.height = formatterParams.height;
248  break;
249  }
250 
251  switch(typeof formatterParams.width){
252  case "number":
253  el.style.width = formatterParams.width + "px";
254  break;
255 
256  case "string":
257  el.style.width = formatterParams.width;
258  break;
259  }
260 
261  el.addEventListener("load", function(){
262  cell.getRow().normalizeHeight();
263  });
264 
265  return el;
266  },
267 
268  //tick or cross
269  tickCross:function(cell, formatterParams, onRendered){
270  var value = cell.getValue(),
271  element = cell.getElement(),
272  empty = formatterParams.allowEmpty,
273  truthy = formatterParams.allowTruthy,
274  tick = typeof formatterParams.tickElement !== "undefined" ? formatterParams.tickElement : '<svg enable-background="new 0 0 24 24" height="14" width="14" viewBox="0 0 24 24" xml:space="preserve" ><path fill="#2DC214" clip-rule="evenodd" d="M21.652,3.211c-0.293-0.295-0.77-0.295-1.061,0L9.41,14.34 c-0.293,0.297-0.771,0.297-1.062,0L3.449,9.351C3.304,9.203,3.114,9.13,2.923,9.129C2.73,9.128,2.534,9.201,2.387,9.351 l-2.165,1.946C0.078,11.445,0,11.63,0,11.823c0,0.194,0.078,0.397,0.223,0.544l4.94,5.184c0.292,0.296,0.771,0.776,1.062,1.07 l2.124,2.141c0.292,0.293,0.769,0.293,1.062,0l14.366-14.34c0.293-0.294,0.293-0.777,0-1.071L21.652,3.211z" fill-rule="evenodd"/></svg>',
275  cross = typeof formatterParams.crossElement !== "undefined" ? formatterParams.crossElement : '<svg enable-background="new 0 0 24 24" height="14" width="14" viewBox="0 0 24 24" xml:space="preserve" ><path fill="#CE1515" d="M22.245,4.015c0.313,0.313,0.313,0.826,0,1.139l-6.276,6.27c-0.313,0.312-0.313,0.826,0,1.14l6.273,6.272 c0.313,0.313,0.313,0.826,0,1.14l-2.285,2.277c-0.314,0.312-0.828,0.312-1.142,0l-6.271-6.271c-0.313-0.313-0.828-0.313-1.141,0 l-6.276,6.267c-0.313,0.313-0.828,0.313-1.141,0l-2.282-2.28c-0.313-0.313-0.313-0.826,0-1.14l6.278-6.269 c0.313-0.312,0.313-0.826,0-1.14L1.709,5.147c-0.314-0.313-0.314-0.827,0-1.14l2.284-2.278C4.308,1.417,4.821,1.417,5.135,1.73 L11.405,8c0.314,0.314,0.828,0.314,1.141,0.001l6.276-6.267c0.312-0.312,0.826-0.312,1.141,0L22.245,4.015z"/></svg>';
276 
277  if((truthy && value) || (value === true || value === "true" || value === "True" || value === 1 || value === "1")){
278  element.setAttribute("aria-checked", true);
279  return tick || "";
280  }else{
281  if(empty && (value === "null" || value === "" || value === null || typeof value === "undefined")){
282  element.setAttribute("aria-checked", "mixed");
283  return "";
284  }else{
285  element.setAttribute("aria-checked", false);
286  return cross || "";
287  }
288  }
289  },
290 
291  datetime:function(cell, formatterParams, onRendered){
292  var inputFormat = formatterParams.inputFormat || "YYYY-MM-DD hh:mm:ss";
293  var outputFormat = formatterParams.outputFormat || "DD/MM/YYYY hh:mm:ss";
294  var invalid = typeof formatterParams.invalidPlaceholder !== "undefined" ? formatterParams.invalidPlaceholder : "";
295  var value = cell.getValue();
296 
297  var newDatetime = moment(value, inputFormat);
298 
299  if(newDatetime.isValid()){
300  return newDatetime.format(outputFormat);
301  }else{
302 
303  if(invalid === true){
304  return value;
305  }else if(typeof invalid === "function"){
306  return invalid(value);
307  }else{
308  return invalid;
309  }
310  }
311  },
312 
313  datetimediff: function datetime(cell, formatterParams, onRendered) {
314  var inputFormat = formatterParams.inputFormat || "YYYY-MM-DD hh:mm:ss";
315  var invalid = typeof formatterParams.invalidPlaceholder !== "undefined" ? formatterParams.invalidPlaceholder : "";
316  var suffix = typeof formatterParams.suffix !== "undefined" ? formatterParams.suffix : false;
317  var unit = typeof formatterParams.unit !== "undefined" ? formatterParams.unit : undefined;
318  var humanize = typeof formatterParams.humanize !== "undefined" ? formatterParams.humanize : false;
319  var date = typeof formatterParams.date !== "undefined" ? formatterParams.date : moment();
320  var value = cell.getValue();
321 
322  var newDatetime = moment(value, inputFormat);
323 
324  if (newDatetime.isValid()) {
325  if(humanize){
326  return moment.duration(newDatetime.diff(date)).humanize(suffix);
327  }else{
328  return newDatetime.diff(date, unit) + (suffix ? " " + suffix : "");
329  }
330 
331  } else {
332 
333  if (invalid === true) {
334  return value;
335  } else if (typeof invalid === "function") {
336  return invalid(value);
337  } else {
338  return invalid;
339  }
340  }
341  },
342 
343  //select
344  lookup: function (cell, formatterParams, onRendered) {
345  var value = cell.getValue();
346 
347  if (typeof formatterParams[value] === "undefined") {
348  console.warn('Missing display value for ' + value);
349  return value;
350  }
351 
352  return formatterParams[value];
353  },
354 
355  //star rating
356  star:function(cell, formatterParams, onRendered){
357  var value = cell.getValue(),
358  element = cell.getElement(),
359  maxStars = formatterParams && formatterParams.stars ? formatterParams.stars : 5,
360  stars = document.createElement("span"),
361  star = document.createElementNS('http://www.w3.org/2000/svg', "svg"),
362  starActive = '<polygon fill="#FFEA00" stroke="#C1AB60" stroke-width="37.6152" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="259.216,29.942 330.27,173.919 489.16,197.007 374.185,309.08 401.33,467.31 259.216,392.612 117.104,467.31 144.25,309.08 29.274,197.007 188.165,173.919 "/>',
363  starInactive = '<polygon fill="#D2D2D2" stroke="#686868" stroke-width="37.6152" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="259.216,29.942 330.27,173.919 489.16,197.007 374.185,309.08 401.33,467.31 259.216,392.612 117.104,467.31 144.25,309.08 29.274,197.007 188.165,173.919 "/>';
364 
365  //style stars holder
366  stars.style.verticalAlign = "middle";
367 
368  //style star
369  star.setAttribute("width", "14");
370  star.setAttribute("height", "14");
371  star.setAttribute("viewBox", "0 0 512 512");
372  star.setAttribute("xml:space", "preserve");
373  star.style.padding = "0 1px";
374 
375  value = value && !isNaN(value) ? parseInt(value) : 0;
376 
377  value = Math.max(0, Math.min(value, maxStars));
378 
379  for(var i=1;i<= maxStars;i++){
380  var nextStar = star.cloneNode(true);
381  nextStar.innerHTML = i <= value ? starActive : starInactive;
382 
383  stars.appendChild(nextStar);
384  }
385 
386  element.style.whiteSpace = "nowrap";
387  element.style.overflow = "hidden";
388  element.style.textOverflow = "ellipsis";
389 
390  element.setAttribute("aria-label", value);
391 
392  return stars;
393  },
394 
395  traffic:function(cell, formatterParams, onRendered){
396  var value = this.sanitizeHTML(cell.getValue()) || 0,
397  el = document.createElement("span"),
398  max = formatterParams && formatterParams.max ? formatterParams.max : 100,
399  min = formatterParams && formatterParams.min ? formatterParams.min : 0,
400  colors = formatterParams && typeof formatterParams.color !== "undefined" ? formatterParams.color : ["red", "orange", "green"],
401  color = "#666666",
402  percent, percentValue;
403 
404  if(isNaN(value) || typeof cell.getValue() === "undefined"){
405  return;
406  }
407 
408  el.classList.add("tabulator-traffic-light");
409 
410  //make sure value is in range
411  percentValue = parseFloat(value) <= max ? parseFloat(value) : max;
412  percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min;
413 
414  //workout percentage
415  percent = (max - min) / 100;
416  percentValue = Math.round((percentValue - min) / percent);
417 
418  //set color
419  switch(typeof colors){
420  case "string":
421  color = colors;
422  break;
423  case "function":
424  color = colors(value);
425  break;
426  case "object":
427  if(Array.isArray(colors)){
428  var unit = 100 / colors.length;
429  var index = Math.floor(percentValue / unit);
430 
431  index = Math.min(index, colors.length - 1);
432  index = Math.max(index, 0);
433  color = colors[index];
434  break;
435  }
436  }
437 
438  el.style.backgroundColor = color;
439 
440  return el;
441  },
442 
443  //progress bar
444  progress:function(cell, formatterParams, onRendered){ //progress bar
445  var value = this.sanitizeHTML(cell.getValue()) || 0,
446  element = cell.getElement(),
447  max = formatterParams && formatterParams.max ? formatterParams.max : 100,
448  min = formatterParams && formatterParams.min ? formatterParams.min : 0,
449  legendAlign = formatterParams && formatterParams.legendAlign ? formatterParams.legendAlign : "center",
450  percent, percentValue, color, legend, legendColor, top, left, right, bottom;
451 
452  //make sure value is in range
453  percentValue = parseFloat(value) <= max ? parseFloat(value) : max;
454  percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min;
455 
456  //workout percentage
457  percent = (max - min) / 100;
458  percentValue = Math.round((percentValue - min) / percent);
459 
460  //set bar color
461  switch(typeof formatterParams.color){
462  case "string":
463  color = formatterParams.color;
464  break;
465  case "function":
466  color = formatterParams.color(value);
467  break;
468  case "object":
469  if(Array.isArray(formatterParams.color)){
470  var unit = 100 / formatterParams.color.length;
471  var index = Math.floor(percentValue / unit);
472 
473  index = Math.min(index, formatterParams.color.length - 1);
474  index = Math.max(index, 0);
475  color = formatterParams.color[index];
476  break;
477  }
478  default:
479  color = "#2DC214";
480  }
481 
482  //generate legend
483  switch(typeof formatterParams.legend){
484  case "string":
485  legend = formatterParams.legend;
486  break;
487  case "function":
488  legend = formatterParams.legend(value);
489  break;
490  case "boolean":
491  legend = value;
492  break;
493  default:
494  legend = false;
495  }
496 
497  //set legend color
498  switch(typeof formatterParams.legendColor){
499  case "string":
500  legendColor = formatterParams.legendColor;
501  break;
502  case "function":
503  legendColor = formatterParams.legendColor(value);
504  break;
505  case "object":
506  if(Array.isArray(formatterParams.legendColor)){
507  var unit = 100 / formatterParams.legendColor.length;
508  var index = Math.floor(percentValue / unit);
509 
510  index = Math.min(index, formatterParams.legendColor.length - 1);
511  index = Math.max(index, 0);
512  legendColor = formatterParams.legendColor[index];
513  }
514  break;
515  default:
516  legendColor = "#000";
517  }
518 
519  element.style.minWidth = "30px";
520  element.style.position = "relative";
521 
522  element.setAttribute("aria-label", percentValue);
523 
524  var barEl = document.createElement("div");
525  barEl.style.display = "inline-block";
526  barEl.style.position = "relative";
527  barEl.style.width = percentValue + "%";
528  barEl.style.backgroundColor = color;
529  barEl.style.height = "100%";
530 
531  barEl.setAttribute('data-max', max);
532  barEl.setAttribute('data-min', min);
533 
534 
535  if(legend){
536  var legendEl = document.createElement("div");
537  legendEl.style.position = "absolute";
538  legendEl.style.top = "4px";
539  legendEl.style.left = 0;
540  legendEl.style.textAlign = legendAlign;
541  legendEl.style.width = "100%";
542  legendEl.style.color = legendColor;
543  legendEl.innerHTML = legend;
544  }
545 
546  onRendered(function(){
547  element.appendChild(barEl);
548 
549  if(legend){
550  element.appendChild(legendEl);
551  }
552  });
553 
554  return "";
555  },
556 
557  //background color
558  color:function(cell, formatterParams, onRendered){
559  cell.getElement().style.backgroundColor = this.sanitizeHTML(cell.getValue());
560  return "";
561  },
562 
563  //tick icon
564  buttonTick:function(cell, formatterParams, onRendered){
565  return '<svg enable-background="new 0 0 24 24" height="14" width="14" viewBox="0 0 24 24" xml:space="preserve" ><path fill="#2DC214" clip-rule="evenodd" d="M21.652,3.211c-0.293-0.295-0.77-0.295-1.061,0L9.41,14.34 c-0.293,0.297-0.771,0.297-1.062,0L3.449,9.351C3.304,9.203,3.114,9.13,2.923,9.129C2.73,9.128,2.534,9.201,2.387,9.351 l-2.165,1.946C0.078,11.445,0,11.63,0,11.823c0,0.194,0.078,0.397,0.223,0.544l4.94,5.184c0.292,0.296,0.771,0.776,1.062,1.07 l2.124,2.141c0.292,0.293,0.769,0.293,1.062,0l14.366-14.34c0.293-0.294,0.293-0.777,0-1.071L21.652,3.211z" fill-rule="evenodd"/></svg>';
566  },
567 
568  //cross icon
569  buttonCross:function(cell, formatterParams, onRendered){
570  return '<svg enable-background="new 0 0 24 24" height="14" width="14" viewBox="0 0 24 24" xml:space="preserve" ><path fill="#CE1515" d="M22.245,4.015c0.313,0.313,0.313,0.826,0,1.139l-6.276,6.27c-0.313,0.312-0.313,0.826,0,1.14l6.273,6.272 c0.313,0.313,0.313,0.826,0,1.14l-2.285,2.277c-0.314,0.312-0.828,0.312-1.142,0l-6.271-6.271c-0.313-0.313-0.828-0.313-1.141,0 l-6.276,6.267c-0.313,0.313-0.828,0.313-1.141,0l-2.282-2.28c-0.313-0.313-0.313-0.826,0-1.14l6.278-6.269 c0.313-0.312,0.313-0.826,0-1.14L1.709,5.147c-0.314-0.313-0.314-0.827,0-1.14l2.284-2.278C4.308,1.417,4.821,1.417,5.135,1.73 L11.405,8c0.314,0.314,0.828,0.314,1.141,0.001l6.276-6.267c0.312-0.312,0.826-0.312,1.141,0L22.245,4.015z"/></svg>';
571  },
572 
573  //current row number
574  rownum:function(cell, formatterParams, onRendered){
575  return this.table.rowManager.activeRows.indexOf(cell.getRow()._getSelf()) + 1;
576  },
577 
578  //row handle
579  handle:function(cell, formatterParams, onRendered){
580  cell.getElement().classList.add("tabulator-row-handle");
581  return "<div class='tabulator-row-handle-box'><div class='tabulator-row-handle-bar'></div><div class='tabulator-row-handle-bar'></div><div class='tabulator-row-handle-bar'></div></div>";
582  },
583 
584  responsiveCollapse:function(cell, formatterParams, onRendered){
585  var self = this,
586  open = false,
587  el = document.createElement("div"),
588  config = cell.getRow()._row.modules.responsiveLayout;
589 
590  el.classList.add("tabulator-responsive-collapse-toggle");
591  el.innerHTML = "<span class='tabulator-responsive-collapse-toggle-open'>+</span><span class='tabulator-responsive-collapse-toggle-close'>-</span>";
592 
593  cell.getElement().classList.add("tabulator-row-handle");
594 
595  function toggleList(isOpen){
596  var collapseEl = config.element;
597 
598  config.open = isOpen;
599 
600  if(collapseEl){
601 
602  if(config.open){
603  el.classList.add("open");
604  collapseEl.style.display = '';
605  }else{
606  el.classList.remove("open");
607  collapseEl.style.display = 'none';
608  }
609  }
610  }
611 
612  el.addEventListener("click", function(e){
613  e.stopImmediatePropagation();
614  toggleList(!config.open);
615  });
616 
617  toggleList(config.open);
618 
619  return el;
620  },
621 
622  rowSelection:function(cell){
623  var checkbox = document.createElement("input");
624 
625  checkbox.type = 'checkbox';
626 
627  if(this.table.modExists("selectRow", true)){
628 
629  checkbox.addEventListener("click", (e) => {
630  e.stopPropagation();
631  });
632 
633  if(typeof cell.getRow == 'function'){
634  var row = cell.getRow();
635 
636  checkbox.addEventListener("change", (e) => {
637  row.toggleSelect();
638  });
639 
640  checkbox.checked = row.isSelected();
641  this.table.modules.selectRow.registerRowSelectCheckbox(row, checkbox);
642  }else {
643  checkbox.addEventListener("change", (e) => {
644  if(this.table.modules.selectRow.selectedRows.length){
645  this.table.deselectRow();
646  }else {
647  this.table.selectRow();
648  }
649  });
650 
651  this.table.modules.selectRow.registerHeaderSelectCheckbox(checkbox);
652  }
653  }
654  return checkbox;
655  },
656 };
657 
658 Tabulator.prototype.registerModule("format", Format);