1 var Format =
function(table){
6 Format.prototype.initializeColumn =
function(column){
8 config = {params:column.definition.formatterParams || {}};
11 switch(typeof column.definition.formatter){
14 if(column.definition.formatter ===
"tick"){
15 column.definition.formatter =
"tickCross";
17 if(typeof config.params.crossElement ==
"undefined"){
18 config.params.crossElement =
false;
21 console.warn(
"DEPRECATION WARNING - the tick formatter has been deprecated, please use the tickCross formatter with the crossElement param set to false");
24 if(
self.formatters[column.definition.formatter]){
25 config.formatter =
self.formatters[column.definition.formatter];
27 console.warn(
"Formatter Error - No such formatter found: ", column.definition.formatter);
28 config.formatter =
self.formatters.plaintext;
33 config.formatter = column.definition.formatter;
37 config.formatter =
self.formatters.plaintext;
41 column.modules.format = config;
44 Format.prototype.cellRendered =
function(cell){
45 if(cell.modules.format && cell.modules.format.renderedCallback){
46 cell.modules.format.renderedCallback();
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;
55 function onRendered(callback){
56 if(!cell.modules.format){
57 cell.modules.format = {};
60 cell.modules.format.renderedCallback = callback;
63 return cell.column.modules.format.formatter.call(
this, component, params, onRendered);
67 Format.prototype.sanitizeHTML =
function(value){
80 return String(value).replace(/[&<>
"'`=\/]/g, function (s) {
88 Format.prototype.emptyToSpace = function(value){
89 return value === null || typeof value === "undefined
" ? "
" : value;
92 //get formatter for cell
93 Format.prototype.getFormatter = function(formatter){
96 switch(typeof formatter){
98 if(this.formatters[formatter]){
99 formatter = this.formatters[formatter]
101 console.warn("Formatter Error - No such formatter found:
", formatter);
102 formatter = this.formatters.plaintext;
107 formatter = formatter;
111 formatter = this.formatters.plaintext;
119 //default data formatters
120 Format.prototype.formatters = {
122 plaintext:function(cell, formatterParams, onRendered){
123 return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
127 html:function(cell, formatterParams, onRendered){
128 return cell.getValue();
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()));
137 //currency formatting
138 money:function(cell, formatterParams, onRendered){
139 var floatVal = parseFloat(cell.getValue()),
140 number, integer, decimal, rgx;
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;
149 return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
152 number = precision !== false ? floatVal.toFixed(precision) : floatVal;
153 number = String(number).split(".
");
156 decimal = number.length > 1 ? decimalSym + number[1] : "";
158 rgx = /(\d+)(\d{3})/;
160 while (rgx.test(integer)){
161 integer = integer.replace(rgx, "$1
" + thousandSym + "$2
");
164 return after ? integer + decimal + symbol : symbol + integer + decimal;
167 //clickable anchor tag
168 link:function(cell, formatterParams, onRendered){
169 var value = cell.getValue(),
170 urlPrefix = formatterParams.urlPrefix || "",
171 download = formatterParams.download,
173 el = document.createElement("a
"),
176 if(formatterParams.labelField){
177 data = cell.getData();
178 label = data[formatterParams.labelField];
181 if(formatterParams.label){
182 switch(typeof formatterParams.label){
184 label = formatterParams.label;
188 label = formatterParams.label(cell);
194 if(formatterParams.urlField){
195 data = cell.getData();
196 value = data[formatterParams.urlField];
199 if(formatterParams.url){
200 switch(typeof formatterParams.url){
202 value = formatterParams.url;
206 value = formatterParams.url(cell);
211 el.setAttribute("href
", urlPrefix + value);
213 if(formatterParams.target){
214 el.setAttribute("target
", formatterParams.target);
217 if(formatterParams.download){
219 if(typeof download == "function"){
220 download = download(cell);
222 download = download === true ? "" : download;
225 el.setAttribute("download
", download);
228 el.innerHTML = this.emptyToSpace(this.sanitizeHTML(label));
237 image:function(cell, formatterParams, onRendered){
238 var el = document.createElement("img
");
239 el.setAttribute("src
", cell.getValue());
241 switch(typeof formatterParams.height){
243 el.style.height = formatterParams.height + "px
";
247 el.style.height = formatterParams.height;
251 switch(typeof formatterParams.width){
253 el.style.width = formatterParams.width + "px
";
257 el.style.width = formatterParams.width;
261 el.addEventListener("load
", function(){
262 cell.getRow().normalizeHeight();
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>';
277 if((truthy && value) || (value === true || value === "true" || value === "True
" || value === 1 || value === "1
")){
278 element.setAttribute("aria-checked
", true);
281 if(empty && (value === "null
" || value === "" || value === null || typeof value === "undefined
")){
282 element.setAttribute("aria-checked
", "mixed
");
285 element.setAttribute("aria-checked
", false);
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();
297 var newDatetime = moment(value, inputFormat);
299 if(newDatetime.isValid()){
300 return newDatetime.format(outputFormat);
303 if(invalid === true){
305 }else if(typeof invalid === "function"){
306 return invalid(value);
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();
322 var newDatetime = moment(value, inputFormat);
324 if (newDatetime.isValid()) {
326 return moment.duration(newDatetime.diff(date)).humanize(suffix);
328 return newDatetime.diff(date, unit) + (suffix ? " " + suffix : "");
333 if (invalid === true) {
335 } else if (typeof invalid === "function") {
336 return invalid(value);
344 lookup: function (cell, formatterParams, onRendered) {
345 var value = cell.getValue();
347 if (typeof formatterParams[value] === "undefined
") {
348 console.warn('Missing display value for ' + value);
352 return formatterParams[value];
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
"/>';
366 stars.style.verticalAlign = "middle
";
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
";
375 value = value && !isNaN(value) ? parseInt(value) : 0;
377 value = Math.max(0, Math.min(value, maxStars));
379 for(var i=1;i<= maxStars;i++){
380 var nextStar = star.cloneNode(true);
381 nextStar.innerHTML = i <= value ? starActive : starInactive;
383 stars.appendChild(nextStar);
386 element.style.whiteSpace = "nowrap
";
387 element.style.overflow = "hidden
";
388 element.style.textOverflow = "ellipsis
";
390 element.setAttribute("aria-label
", value);
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
"],
402 percent, percentValue;
404 if(isNaN(value) || typeof cell.getValue() === "undefined
"){
408 el.classList.add("tabulator-traffic-light
");
410 //make sure value is in range
411 percentValue = parseFloat(value) <= max ? parseFloat(value) : max;
412 percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min;
415 percent = (max - min) / 100;
416 percentValue = Math.round((percentValue - min) / percent);
419 switch(typeof colors){
424 color = colors(value);
427 if(Array.isArray(colors)){
428 var unit = 100 / colors.length;
429 var index = Math.floor(percentValue / unit);
431 index = Math.min(index, colors.length - 1);
432 index = Math.max(index, 0);
433 color = colors[index];
438 el.style.backgroundColor = color;
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;
452 //make sure value is in range
453 percentValue = parseFloat(value) <= max ? parseFloat(value) : max;
454 percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min;
457 percent = (max - min) / 100;
458 percentValue = Math.round((percentValue - min) / percent);
461 switch(typeof formatterParams.color){
463 color = formatterParams.color;
466 color = formatterParams.color(value);
469 if(Array.isArray(formatterParams.color)){
470 var unit = 100 / formatterParams.color.length;
471 var index = Math.floor(percentValue / unit);
473 index = Math.min(index, formatterParams.color.length - 1);
474 index = Math.max(index, 0);
475 color = formatterParams.color[index];
483 switch(typeof formatterParams.legend){
485 legend = formatterParams.legend;
488 legend = formatterParams.legend(value);
498 switch(typeof formatterParams.legendColor){
500 legendColor = formatterParams.legendColor;
503 legendColor = formatterParams.legendColor(value);
506 if(Array.isArray(formatterParams.legendColor)){
507 var unit = 100 / formatterParams.legendColor.length;
508 var index = Math.floor(percentValue / unit);
510 index = Math.min(index, formatterParams.legendColor.length - 1);
511 index = Math.max(index, 0);
512 legendColor = formatterParams.legendColor[index];
516 legendColor = "#000
";
519 element.style.minWidth = "30px
";
520 element.style.position = "relative
";
522 element.setAttribute("aria-label
", percentValue);
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%
";
531 barEl.setAttribute('data-max', max);
532 barEl.setAttribute('data-min', min);
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;
546 onRendered(function(){
547 element.appendChild(barEl);
550 element.appendChild(legendEl);
558 color:function(cell, formatterParams, onRendered){
559 cell.getElement().style.backgroundColor = this.sanitizeHTML(cell.getValue());
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>';
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>';
574 rownum:function(cell, formatterParams, onRendered){
575 return this.table.rowManager.activeRows.indexOf(cell.getRow()._getSelf()) + 1;
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>
";
584 responsiveCollapse:function(cell, formatterParams, onRendered){
587 el = document.createElement("div
"),
588 config = cell.getRow()._row.modules.responsiveLayout;
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>
";
593 cell.getElement().classList.add("tabulator-row-handle
");
595 function toggleList(isOpen){
596 var collapseEl = config.element;
598 config.open = isOpen;
603 el.classList.add("open
");
604 collapseEl.style.display = '';
606 el.classList.remove("open
");
607 collapseEl.style.display = 'none';
612 el.addEventListener("click
", function(e){
613 e.stopImmediatePropagation();
614 toggleList(!config.open);
617 toggleList(config.open);
622 rowSelection:function(cell){
623 var checkbox = document.createElement("input
");
625 checkbox.type = 'checkbox';
627 if(this.table.modExists("selectRow
", true)){
629 checkbox.addEventListener("click
", (e) => {
633 if(typeof cell.getRow == 'function'){
634 var row = cell.getRow();
636 checkbox.addEventListener("change
", (e) => {
640 checkbox.checked = row.isSelected();
641 this.table.modules.selectRow.registerRowSelectCheckbox(row, checkbox);
643 checkbox.addEventListener("change
", (e) => {
644 if(this.table.modules.selectRow.selectedRows.length){
645 this.table.deselectRow();
647 this.table.selectRow();
651 this.table.modules.selectRow.registerHeaderSelectCheckbox(checkbox);
658 Tabulator.prototype.registerModule("format
", Format);