UNPKG

jquery.tabulator

Version:

Interactive table generation plugin for jQuery UI

1,012 lines (777 loc) 24.1 kB
//public column object var ColumnComponent = function (column){ this.column = column; this.type = "ColumnComponent"; }; ColumnComponent.prototype.getElement = function(){ return this.column.getElement(); }; ColumnComponent.prototype.getDefinition = function(){ return this.column.getDefinition(); }; ColumnComponent.prototype.getField = function(){ return this.column.getField(); }; ColumnComponent.prototype.getCells = function(){ var cells = []; this.column.cells.forEach(function(cell){ cells.push(cell.getComponent()); }); return cells; }; ColumnComponent.prototype.getVisibility = function(){ return this.column.visible; }; ColumnComponent.prototype.show = function(){ if(this.column.isGroup){ this.column.columns.forEach(function(column){ column.show(); }); }else{ this.column.show(); } }; ColumnComponent.prototype.hide = function(){ if(this.column.isGroup){ this.column.columns.forEach(function(column){ column.hide(); }); }else{ this.column.hide(); } }; ColumnComponent.prototype.toggle = function(){ if(this.column.visible){ this.hide(); }else{ this.show(); } }; ColumnComponent.prototype.delete = function(){ this.column.delete(); }; ColumnComponent.prototype.getSubColumns = function(){ var output = []; if(this.column.columns.length){ this.column.columns.forEach(function(column){ output.push(column.getComponent()); }); } return output; }; ColumnComponent.prototype.getParentColumn = function(){ return this.column.parent instanceof Column ? this.column.parent.getComponent() : false; }; ColumnComponent.prototype._getSelf = function(){ return this.column; }; ColumnComponent.prototype.scrollTo = function(){ this.column.table.columManager.scrollToColumn(this.column); }; var Column = function(def, parent){ var self = this; this.table = parent.table; this.definition = def; //column definition this.parent = parent; //hold parent object this.type = "column"; //type of element this.columns = []; //child columns this.cells = []; //cells bound to this column this.element = $("<div class='tabulator-col' role='columnheader' aria-sort='none'></div>"); //column header element this.contentElement = false; this.groupElement = $("<div class='tabulator-col-group-cols'></div>"); //column group holder element this.isGroup = false; this.tooltip = false; //hold column tooltip this.hozAlign = ""; //horizontal text alignment //multi dimentional filed handling this.field =""; this.fieldStructure = ""; this.getFieldValue = ""; this.setFieldValue = ""; this.setField(this.definition.field); this.extensions = {}; //hold extension variables; this.cellEvents = { cellClick:false, cellDblClick:false, cellContext:false, cellTap:false, cellDblTap:false, cellTapHold:false }; this.width = null; //column width this.minWidth = null; //column minimum width this.widthFixed = false; //user has specified a width for this column this.visible = true; //default visible state //initialize column if(def.columns){ this.isGroup = true; def.columns.forEach(function(def, i){ var newCol = new Column(def, self); self.attachColumn(newCol); }); self.checkColumnVisibility(); }else{ parent.registerColumnField(this); } if(def.rowHandle && this.table.options.movableRows !== false && this.table.extExists("moveRow")){ this.table.extensions.moveRow.setHandle(true); } this._mapDepricatedFunctionality() this._buildHeader(); }; //////////////// Setup Functions ///////////////// Column.prototype._mapDepricatedFunctionality = function(field){ if(this.definition.tooltipHeader){ console.warn("The%c tooltipHeader%c column definition property has been depricated and will be removed in version 4.0, use %c headerTooltip%c instead.", "font-weight:bold;", "font-weight:regular;", "font-weight:bold;", "font-weight:regular;"); if(typeof this.definition.headerTooltip == "undefined"){ this.definition.headerTooltip = this.definition.tooltipHeader; } } }; Column.prototype.setField = function(field){ this.field = field; this.fieldStructure = field ? field.split(".") : []; this.getFieldValue = this.fieldStructure.length > 1 ? this._getNestedData : this._getFlatData; this.setFieldValue = this.fieldStructure.length > 1 ? this._setNesteData : this._setFlatData; }; //register column position with column manager Column.prototype.registerColumnPosition = function(column){ this.parent.registerColumnPosition(column); }; //register column position with column manager Column.prototype.registerColumnField = function(column){ this.parent.registerColumnField(column); }; //trigger position registration Column.prototype.reRegisterPosition = function(){ if(this.isGroup){ this.columns.forEach(function(column){ column.reRegisterPosition(); }); }else{ this.registerColumnPosition(this); } }; Column.prototype.setTooltip = function(){ var self = this, def = self.definition; //set header tooltips var tooltip = def.headerTooltip || def.tooltip === false ? def.headerTooltip : self.table.options.tooltipsHeader; if(tooltip){ if(tooltip === true){ if(def.field){ self.table.extensions.localize.bind("columns|" + def.field, function(value){ self.element.attr("title", value || def.title); }); }else{ self.element.attr("title", def.title); } }else{ if(typeof(tooltip) == "function"){ tooltip = tooltip(self.getComponent()); if(tooltip === false){ tooltip = ""; } } self.element.attr("title", tooltip); } }else{ self.element.attr("title", ""); } } //build header element Column.prototype._buildHeader = function(){ var self = this, def = self.definition, dblTap, tapHold, tap; self.element.empty(); self.contentElement = self._buildColumnHeaderContent(); self.element.append(self.contentElement); if(self.isGroup){ self._buildGroupHeader(); }else{ self._buildColumnHeader(); } self.setTooltip(); //set resizable handles if(self.table.options.resizableColumns && self.table.extExists("resizeColumns")){ self.table.extensions.resizeColumns.initializeColumn("header", self, self.element); } //set resizable handles if(def.headerFilter && self.table.extExists("filter") && self.table.extExists("edit")){ if(typeof def.headerFilterPlaceholder !== "undefined" && def.field){ self.table.extensions.localize.setHeaderFilterColumnPlaceholder(def.field, def.headerFilterPlaceholder); } self.table.extensions.filter.initializeColumn(self); } //set resizable handles if(self.table.extExists("frozenColumns")){ self.table.extensions.frozenColumns.initializeColumn(self); } //set movable column if(self.table.options.movableColumns && !self.isGroup && self.table.extExists("moveColumn")){ self.table.extensions.moveColumn.initializeColumn(self); } //set calcs column if((def.topCalc || def.bottomCalc) && self.table.extExists("columnCalcs")){ self.table.extensions.columnCalcs.initializeColumn(self); } //update header tooltip on mouse enter self.element.on("mouseenter", function(e){ self.setTooltip(); }); //setup header click event bindings if(typeof(def.headerClick) == "function"){ self.element.on("click", function(e){def.headerClick(e, self.getComponent())}) } if(typeof(def.headerDblClick) == "function"){ self.element.on("dblclick", function(e){def.headerDblClick(e, self.getComponent())}); } if(typeof(def.headerContext) == "function"){ self.element.on("contextmenu", function(e){def.headerContext(e, self.getComponent())}); } //setup header tap event bindings if(typeof(def.headerTap) == "function"){ tap = false; self.element.on("touchstart", function(e){ tap = true; }); self.element.on("touchend", function(e){ if(tap){ def.headerTap(e, self.getComponent()); } tap = false; }); } if(typeof(def.headerDblTap) == "function"){ dblTap = null; self.element.on("touchend", function(e){ if(dblTap){ clearTimeout(dblTap); dblTap = null; def.headerDblTap(e, self.getComponent()); }else{ dblTap = setTimeout(function(){ clearTimeout(dblTap); dblTap = null; }, 300); } }); } if(typeof(def.headerTapHold) == "function"){ tapHold = null; self.element.on("touchstart", function(e){ clearTimeout(tapHold); tapHold = setTimeout(function(){ clearTimeout(tapHold); tapHold = null; tap = false; def.headerTapHold(e, self.getComponent()); }, 1000) }); self.element.on("touchend", function(e){ clearTimeout(tapHold); tapHold = null; }); } //store column cell click event bindings if(typeof(def.cellClick) == "function"){ self.cellEvents.cellClick = def.cellClick; } if(typeof(def.cellDblClick) == "function"){ self.cellEvents.cellDblClick = def.cellDblClick; } if(typeof(def.cellContext) == "function"){ self.cellEvents.cellContext = def.cellContext; } //setup column cell tap event bindings if(typeof(def.cellTap) == "function"){ self.cellEvents.cellTap = def.cellTap; } if(typeof(def.cellDblTap) == "function"){ self.cellEvents.cellDblTap = def.cellDblTap; } if(typeof(def.cellTapHold) == "function"){ self.cellEvents.cellTapHold = def.cellTapHold; } //setup column cell edit callbacks if(typeof(def.cellEdited) == "function"){ self.cellEvents.cellEdited = def.cellEdited; } if(typeof(def.cellEditing) == "function"){ self.cellEvents.cellEditing = def.cellEditing; } if(typeof(def.cellEditCancelled) == "function"){ self.cellEvents.cellEditCancelled = def.cellEditCancelled; } }; //build header element for header Column.prototype._buildColumnHeader = function(){ var self = this, def = self.definition, table = self.table, sortable; //set column sorter if(table.extExists("sort")){ table.extensions.sort.initializeColumn(self, self.contentElement); } //set column formatter if(table.extExists("format")){ table.extensions.format.initializeColumn(self); } //set column editor if(typeof def.editor != "undefined" && table.extExists("edit")){ table.extensions.edit.initializeColumn(self); } //set colum validator if(typeof def.validator != "undefined" && table.extExists("validate")){ table.extensions.validate.initializeColumn(self); } //set column mutator if(table.extExists("mutator")){ table.extensions.mutator.initializeColumn(self); } //set column accessor if(table.extExists("accessor")){ table.extensions.accessor.initializeColumn(self); } //set respoviveLayout if(typeof table.options.responsiveLayout && table.extExists("responsiveLayout")){ table.extensions.responsiveLayout.initializeColumn(self); } //set column visibility if(typeof def.visible != "undefined"){ if(def.visible){ self.show(true); }else{ self.hide(true); } } //asign additional css classes to column header if(def.cssClass){ self.element.addClass(def.cssClass); } if(def.field){ this.element.attr("tabulator-field", def.field) } //set min width if present self.setMinWidth(typeof def.minWidth == "undefined" ? self.table.options.columnMinWidth : def.minWidth); self.reinitializeWidth(); //set tooltip if present self.tooltip = self.definition.tooltip || self.definition.tooltip === false ? self.definition.tooltip : self.table.options.tooltips; //set orizontal text alignment self.hozAlign = typeof(self.definition.align) == "undefined" ? "" : self.definition.align; }; Column.prototype._buildColumnHeaderContent = function(){ var self = this, def = self.definition, table = self.table; var contentElement = $("<div class='tabulator-col-content'></div>"); contentElement.append(self._buildColumnHeaderTitle()); return contentElement; }; //build title element of column Column.prototype._buildColumnHeaderTitle = function(){ var self = this, def = self.definition, table = self.table, title; var titleHolderElement = $("<div class='tabulator-col-title'></div>"); if(def.editableTitle){ var titleElement = $("<input class='tabulator-title-editor'>"); titleElement.on("click", function(e){ e.stopPropagation(); $(this).focus(); }); titleElement.on("change", function(){ var newTitle = $(this).val(); def.title = newTitle; table.options.columnTitleChanged(self.getComponent()); }); titleHolderElement.append(titleElement); if(def.field){ table.extensions.localize.bind("columns|" + def.field, function(text){ titleElement.val(text || (def.title || "&nbsp")); }); }else{ titleElement.val(def.title || "&nbsp"); } }else{ if(def.field){ table.extensions.localize.bind("columns|" + def.field, function(text){ self._formatColumnHeaderTitle(titleHolderElement, text || (def.title || "&nbsp")); }); }else{ self._formatColumnHeaderTitle(titleHolderElement, def.title || "&nbsp"); } } return titleHolderElement; }; Column.prototype._formatColumnHeaderTitle = function(el, title){ var formatter, contents; if(this.definition.titleFormatter && this.table.extExists("format")){ formatter = this.table.extensions.format.getFormatter(this.definition.titleFormatter); contents = formatter.call(this.table.extensions.format, { getValue:function(){ return title; }, getElement:function(){ return el; } }, this.definition.titleFormatterParams || {}); el.append(contents); }else{ el.html(title); } }; //build header element for column group Column.prototype._buildGroupHeader = function(){ var self = this, def = self.definition, table = self.table; self.element.addClass("tabulator-col-group") .attr("role", "columngroup") .attr("aria-title", def.title); self.element.append(self.groupElement); }; //flat field lookup Column.prototype._getFlatData = function(data){ return data[this.field]; }; //nested field lookup Column.prototype._getNestedData = function(data){ var dataObj = data, structure = this.fieldStructure, length = structure.length, output; for(let i = 0; i < length; i++){ dataObj = dataObj[structure[i]]; output = dataObj; if(!dataObj){ break; } } return output; }; //flat field set Column.prototype._setFlatData = function(data, value){ data[this.field] = value; }; //nested field set Column.prototype._setNesteData = function(data, value){ var dataObj = data, structure = this.fieldStructure, length = structure.length; for(let i = 0; i < length; i++){ if(i == length -1){ dataObj[structure[i]] = value; }else{ if(!dataObj[structure[i]]){ dataObj[structure[i]] = {}; } dataObj = dataObj[structure[i]]; } } }; //attach column to this group Column.prototype.attachColumn = function(column){ var self = this; if(self.groupElement){ self.columns.push(column); self.groupElement.append(column.getElement()); }else{ console.warn("Column Warning - Column being attached to another column instead of column group"); } }; //vertically align header in column Column.prototype.verticalAlign = function(alignment){ //calculate height of column header and group holder element var parentHeight = this.parent.isGroup ? this.parent.getGroupElement().innerHeight() : this.parent.getHeadersElement().innerHeight(); this.element.css("height", parentHeight); if(this.isGroup){ this.groupElement.css("min-height", parentHeight - this.contentElement.outerHeight()); } //vertically align cell contents if(!this.isGroup && alignment !== "top"){ if(alignment === "bottom"){ this.element.css({"padding-top": this.element.innerHeight() - this.contentElement.outerHeight()}); }else{ this.element.css({"padding-top": (this.element.innerHeight() - this.contentElement.outerHeight()) / 2 }); } } this.columns.forEach(function(column){ column.verticalAlign(alignment); }); }; //clear vertical alignmenet Column.prototype.clearVerticalAlign = function(){ this.element.css("padding-top",""); this.element.css("height",""); this.element.css("min-height",""); this.columns.forEach(function(column){ column.clearVerticalAlign(); }); }; //// Retreive Column Information //// //return column header element Column.prototype.getElement = function(){ return this.element; }; //return colunm group element Column.prototype.getGroupElement = function(){ return this.groupElement; }; //return field name Column.prototype.getField = function(){ return this.field; }; //return the first column in a group Column.prototype.getFirstColumn = function(){ if(!this.isGroup){ return this; }else{ if(this.columns.length){ return this.columns[0].getFirstColumn(); }else{ return false; } } }; //return the last column in a group Column.prototype.getLastColumn = function(){ if(!this.isGroup){ return this; }else{ if(this.columns.length){ return this.columns[this.columns.length -1].getLastColumn(); }else{ return false; } } }; //return all columns in a group Column.prototype.getColumns = function(){ return this.columns; }; //return all columns in a group Column.prototype.getCells = function(){ return this.cells; }; //retreive the top column in a group of columns Column.prototype.getTopColumn = function(){ if(this.parent.isGroup){ return this.parent.getTopColumn(); }else{ return this; } }; //return column definition object Column.prototype.getDefinition = function(updateBranches){ var colDefs = []; if(this.isGroup && updateBranches){ this.columns.forEach(function(column){ colDefs.push(column.getDefinition(true)); }); this.definition.columns = colDefs; } return this.definition; }; //////////////////// Actions //////////////////// Column.prototype.checkColumnVisibility = function(){ var visible = false; this.columns.forEach(function(column){ if(column.visible){ visible = true; } }); if(visible){ this.show() this.parent.table.options.columnVisibilityChanged(this.getComponent(), false); }else{ this.hide(); } }; //show column Column.prototype.show = function(silent, responsiveToggle){ if(!this.visible){ this.visible = true; this.element.css({ "display":"", }); this.table.columnManager._verticalAlignHeaders(); if(this.parent.isGroup){ this.parent.checkColumnVisibility(); } this.cells.forEach(function(cell){ cell.show(); }); if(this.table.options.persistentLayout && this.table.extExists("responsiveLayout", true)){ this.table.extensions.persistence.save("columns"); } if(!responsiveToggle && this.table.options.responsiveLayout && this.table.extExists("responsiveLayout", true)){ this.table.extensions.responsiveLayout.updateColumnVisibility(this, this.visible); } if(!silent){ this.table.options.columnVisibilityChanged(this.getComponent(), true); } } }; //hide column Column.prototype.hide = function(silent, responsiveToggle){ if(this.visible){ this.visible = false; this.element.css({ "display":"none", }); this.table.columnManager._verticalAlignHeaders(); if(this.parent.isGroup){ this.parent.checkColumnVisibility(); } this.cells.forEach(function(cell){ cell.hide(); }); if(this.table.options.persistentLayout && this.table.extExists("persistence", true)){ this.table.extensions.persistence.save("columns"); } if(!responsiveToggle && this.table.options.responsiveLayout && this.table.extExists("responsiveLayout", true)){ this.table.extensions.responsiveLayout.updateColumnVisibility(this, this.visible); } if(!silent){ this.table.options.columnVisibilityChanged(this.getComponent(), false); } } }; Column.prototype.matchChildWidths = function(){ var childWidth = 0; if(this.contentElement && this.columns.length){ this.columns.forEach(function(column){ childWidth += column.getWidth(); }); this.contentElement.css("max-width", childWidth - 1); } } Column.prototype.setWidth = function(width){ this.widthFixed = true; this.setWidthActual(width); }; Column.prototype.setWidthActual = function(width){ if(isNaN(width)){ width = Math.floor((this.table.element.innerWidth()/100) * parseInt(width)); } width = Math.max(this.minWidth, width); this.width = width; this.element.css("width", width || ""); if(!this.isGroup){ this.cells.forEach(function(cell){ cell.setWidth(width); }); } if(this.parent.isGroup){ this.parent.matchChildWidths(); } //set resizable handles if(this.table.extExists("frozenColumns")){ this.table.extensions.frozenColumns.layout(); } }; Column.prototype.checkCellHeights = function(){ var rows = []; this.cells.forEach(function(cell){ if(cell.row.heightInitialized){ if(cell.row.element[0].offsetParent !== null){ rows.push(cell.row); cell.row.clearCellHeight(); }else{ cell.row.heightInitialized = false; } } }); rows.forEach(function(row){ row.calcHeight(); }) rows.forEach(function(row){ row.setCellHeight(); }) }; Column.prototype.getWidth = function(){ return this.element.outerWidth(); }; Column.prototype.getHeight = function(){ return this.element.outerHeight(); }; Column.prototype.setMinWidth = function(minWidth){ this.minWidth = minWidth; this.element.css("min-width", minWidth || ""); this.cells.forEach(function(cell){ cell.setMinWidth(minWidth); }); }; Column.prototype.delete = function(){ if(this.isGroup){ this.columns.forEach(function(column){ column.delete(); }); } var cellCount = this.cells.length; for(let i = 0; i < cellCount; i++){ this.cells[0].delete(); } this.element.detach(); this.table.columnManager.deregisterColumn(this); }; //////////////// Cell Management ///////////////// //generate cell for this column Column.prototype.generateCell = function(row){ var self = this; var cell = new Cell(self, row); this.cells.push(cell); return cell; }; Column.prototype.reinitializeWidth = function(force){ this.widthFixed = false; //set width if present if(typeof this.definition.width !== "undefined" && !force){ this.setWidth(this.definition.width); } //hide header filters to prevent them altering column width if(this.table.extExists("filter")){ this.table.extensions.filter.hideHeaderFilterElements(); } this.fitToData(); //show header filters again after layout is complete if(this.table.extExists("filter")){ this.table.extensions.filter.showHeaderFilterElements(); } } //set column width to maximum cell width Column.prototype.fitToData = function(){ var self = this; if(!this.widthFixed){ this.element.css("width", "") self.cells.forEach(function(cell){ cell.setWidth(""); }); } var maxWidth = this.element.outerWidth(); if(!self.width || !this.widthFixed){ self.cells.forEach(function(cell){ var width = cell.getWidth(); if(width > maxWidth){ maxWidth = width; } }); if(maxWidth){ self.setWidthActual(maxWidth + 1); } } }; Column.prototype.deleteCell = function(cell){ var index = this.cells.indexOf(cell); if(index > -1){ this.cells.splice(index, 1); } }; //////////////// Event Bindings ///////////////// //////////////// Object Generation ///////////////// Column.prototype.getComponent = function(){ return new ColumnComponent(this); };