nsedit/jtable/dev/jquery.jtable.dynamiccolumns.js

461 lines
20 KiB
JavaScript

/************************************************************************
* DYNAMIC COLUMNS extension for jTable *
* (Show/hide/resize columns) *
*************************************************************************/
(function ($) {
//Reference to base object members
var base = {
_create: $.hik.jtable.prototype._create,
_normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions,
_createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField,
_createCellForRecordField: $.hik.jtable.prototype._createCellForRecordField
};
//extension members
$.extend(true, $.hik.jtable.prototype, {
/************************************************************************
* DEFAULT OPTIONS / EVENTS *
*************************************************************************/
options: {
tableId: undefined,
columnResizable: true,
columnSelectable: true
},
/************************************************************************
* PRIVATE FIELDS *
*************************************************************************/
_$columnSelectionDiv: null,
_$columnResizeBar: null,
_cookieKeyPrefix: null,
_currentResizeArgs: null,
/************************************************************************
* OVERRIDED METHODS *
*************************************************************************/
/* Overrides _addRowToTableHead method.
*************************************************************************/
_create: function () {
base._create.apply(this, arguments);
this._createColumnResizeBar();
this._createColumnSelection();
if (this.options.saveUserPreferences) {
this._loadColumnSettings();
}
this._normalizeColumnWidths();
},
/* Normalizes some options for a field (sets default values).
*************************************************************************/
_normalizeFieldOptions: function (fieldName, props) {
base._normalizeFieldOptions.apply(this, arguments);
//columnResizable
if (this.options.columnResizable) {
props.columnResizable = (props.columnResizable != false);
}
//visibility
if (!props.visibility) {
props.visibility = 'visible';
}
},
/* Overrides _createHeaderCellForField to make columns dynamic.
*************************************************************************/
_createHeaderCellForField: function (fieldName, field) {
var $headerCell = base._createHeaderCellForField.apply(this, arguments);
//Make data columns resizable except the last one
if (this.options.columnResizable && field.columnResizable && (fieldName != this._columnList[this._columnList.length - 1])) {
this._makeColumnResizable($headerCell);
}
//Hide column if needed
if (field.visibility == 'hidden') {
$headerCell.hide();
}
return $headerCell;
},
/* Overrides _createHeaderCellForField to decide show or hide a column.
*************************************************************************/
_createCellForRecordField: function (record, fieldName) {
var $column = base._createCellForRecordField.apply(this, arguments);
var field = this.options.fields[fieldName];
if (field.visibility == 'hidden') {
$column.hide();
}
return $column;
},
/************************************************************************
* PUBLIC METHODS *
*************************************************************************/
/* Changes visibility of a column.
*************************************************************************/
changeColumnVisibility: function (columnName, visibility) {
this._changeColumnVisibilityInternal(columnName, visibility);
this._normalizeColumnWidths();
if (this.options.saveUserPreferences) {
this._saveColumnSettings();
}
},
/************************************************************************
* PRIVATE METHODS *
*************************************************************************/
/* Changes visibility of a column.
*************************************************************************/
_changeColumnVisibilityInternal: function (columnName, visibility) {
//Check if there is a column with given name
var columnIndex = this._columnList.indexOf(columnName);
if (columnIndex < 0) {
this._logWarn('Column "' + columnName + '" does not exist in fields!');
return;
}
//Check if visibility value is valid
if (['visible', 'hidden', 'fixed'].indexOf(visibility) < 0) {
this._logWarn('Visibility value is not valid: "' + visibility + '"! Options are: visible, hidden, fixed.');
return;
}
//Get the field
var field = this.options.fields[columnName];
if (field.visibility == visibility) {
return; //No action if new value is same as old one.
}
//Hide or show the column if needed
var columnIndexInTable = this._firstDataColumnOffset + columnIndex + 1;
if (field.visibility != 'hidden' && visibility == 'hidden') {
this._$table
.find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')')
.hide();
} else if (field.visibility == 'hidden' && visibility != 'hidden') {
this._$table
.find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')')
.show()
.css('display', 'table-cell');
}
field.visibility = visibility;
},
/* Prepares dialog to change settings.
*************************************************************************/
_createColumnSelection: function () {
var self = this;
//Create a div for dialog and add to container element
this._$columnSelectionDiv = $('<div />')
.addClass('jtable-column-selection-container')
.appendTo(self._$mainContainer);
this._$table.children('thead').bind('contextmenu', function (e) {
if (!self.options.columnSelectable) {
return;
}
e.preventDefault();
//Make an overlay div to disable page clicks
$('<div />')
.addClass('jtable-contextmenu-overlay')
.click(function () {
$(this).remove();
self._$columnSelectionDiv.hide();
})
.bind('contextmenu', function () { return false; })
.appendTo(document.body);
self._fillColumnSelection();
//Calculate position of column selection list and show it
var containerOffset = self._$mainContainer.offset();
var selectionDivTop = e.pageY - containerOffset.top;
var selectionDivLeft = e.pageX - containerOffset.left;
var selectionDivMinWidth = 100; //in pixels
var containerWidth = self._$mainContainer.width();
//If user clicks right area of header of the table, show list at a little left
if ((containerWidth > selectionDivMinWidth) && (selectionDivLeft > (containerWidth - selectionDivMinWidth))) {
selectionDivLeft = containerWidth - selectionDivMinWidth;
}
self._$columnSelectionDiv.css({
left: selectionDivLeft,
top: selectionDivTop,
'min-width': selectionDivMinWidth + 'px'
}).show();
});
},
/* Prepares content of settings dialog.
*************************************************************************/
_fillColumnSelection: function () {
var self = this;
var $columnsUl = $('<ul></ul>')
.addClass('jtable-column-select-list');
for (var i = 0; i < this._columnList.length; i++) {
var columnName = this._columnList[i];
var field = this.options.fields[columnName];
//Crete li element
var $columnLi = $('<li></li>').appendTo($columnsUl);
//Create label for the checkbox
var $label = $('<label for="' + columnName + '"></label>')
.append($('<span>' + (field.title || columnName) + '</span>'))
.appendTo($columnLi);
//Create checkbox
var $checkbox = $('<input type="checkbox" name="' + columnName + '">')
.prependTo($label)
.click(function () {
var $clickedCheckbox = $(this);
var clickedColumnName = $clickedCheckbox.attr('name');
var clickedField = self.options.fields[clickedColumnName];
if (clickedField.visibility == 'fixed') {
return;
}
self.changeColumnVisibility(clickedColumnName, $clickedCheckbox.is(':checked') ? 'visible' : 'hidden');
});
//Check, if column if shown
if (field.visibility != 'hidden') {
$checkbox.attr('checked', 'checked');
}
//Disable, if column is fixed
if (field.visibility == 'fixed') {
$checkbox.attr('disabled', 'disabled');
}
}
this._$columnSelectionDiv.html($columnsUl);
},
/* creates a vertical bar that is shown while resizing columns.
*************************************************************************/
_createColumnResizeBar: function () {
this._$columnResizeBar = $('<div />')
.addClass('jtable-column-resize-bar')
.appendTo(this._$mainContainer)
.hide();
},
/* Makes a column sortable.
*************************************************************************/
_makeColumnResizable: function ($columnHeader) {
var self = this;
//Create a handler to handle mouse click event
$('<div />')
.addClass('jtable-column-resize-handler')
.appendTo($columnHeader.find('.jtable-column-header-container')) //Append the handler to the column
.mousedown(function (downevent) { //handle mousedown event for the handler
downevent.preventDefault();
downevent.stopPropagation();
var mainContainerOffset = self._$mainContainer.offset();
//Get a reference to the next column
var $nextColumnHeader = $columnHeader.nextAll('th.jtable-column-header:visible:first');
if (!$nextColumnHeader.length) {
return;
}
//Store some information to be used on resizing
var minimumColumnWidth = 10; //A column's width can not be smaller than 10 pixel.
self._currentResizeArgs = {
currentColumnStartWidth: $columnHeader.outerWidth(),
minWidth: minimumColumnWidth,
maxWidth: $columnHeader.outerWidth() + $nextColumnHeader.outerWidth() - minimumColumnWidth,
mouseStartX: downevent.pageX,
minResizeX: function () { return this.mouseStartX - (this.currentColumnStartWidth - this.minWidth); },
maxResizeX: function () { return this.mouseStartX + (this.maxWidth - this.currentColumnStartWidth); }
};
//Handle mouse move event to move resizing bar
var resizeonmousemove = function (moveevent) {
if (!self._currentResizeArgs) {
return;
}
var resizeBarX = self._normalizeNumber(moveevent.pageX, self._currentResizeArgs.minResizeX(), self._currentResizeArgs.maxResizeX());
self._$columnResizeBar.css('left', (resizeBarX - mainContainerOffset.left) + 'px');
};
//Handle mouse up event to finish resizing of the column
var resizeonmouseup = function (upevent) {
if (!self._currentResizeArgs) {
return;
}
$(document).unbind('mousemove', resizeonmousemove);
$(document).unbind('mouseup', resizeonmouseup);
self._$columnResizeBar.hide();
//Calculate new widths in pixels
var mouseChangeX = upevent.pageX - self._currentResizeArgs.mouseStartX;
var currentColumnFinalWidth = self._normalizeNumber(self._currentResizeArgs.currentColumnStartWidth + mouseChangeX, self._currentResizeArgs.minWidth, self._currentResizeArgs.maxWidth);
var nextColumnFinalWidth = $nextColumnHeader.outerWidth() + (self._currentResizeArgs.currentColumnStartWidth - currentColumnFinalWidth);
//Calculate widths as percent
var pixelToPercentRatio = $columnHeader.data('width-in-percent') / self._currentResizeArgs.currentColumnStartWidth;
$columnHeader.data('width-in-percent', currentColumnFinalWidth * pixelToPercentRatio);
$nextColumnHeader.data('width-in-percent', nextColumnFinalWidth * pixelToPercentRatio);
//Set new widths to columns (resize!)
$columnHeader.css('width', $columnHeader.data('width-in-percent') + '%');
$nextColumnHeader.css('width', $nextColumnHeader.data('width-in-percent') + '%');
//Normalize all column widths
self._normalizeColumnWidths();
//Finish resizing
self._currentResizeArgs = null;
//Save current preferences
if (self.options.saveUserPreferences) {
self._saveColumnSettings();
}
};
//Show vertical resize bar
self._$columnResizeBar
.show()
.css({
top: ($columnHeader.offset().top - mainContainerOffset.top) + 'px',
left: (downevent.pageX - mainContainerOffset.left) + 'px',
height: (self._$table.outerHeight()) + 'px'
});
//Bind events
$(document).bind('mousemove', resizeonmousemove);
$(document).bind('mouseup', resizeonmouseup);
});
},
/* Normalizes column widths as percent for current view.
*************************************************************************/
_normalizeColumnWidths: function () {
//Set command column width
var commandColumnHeaders = this._$table
.find('>thead th.jtable-command-column-header')
.data('width-in-percent', 1)
.css('width', '1%');
//Find data columns
var headerCells = this._$table.find('>thead th.jtable-column-header');
//Calculate total width of data columns
var totalWidthInPixel = 0;
headerCells.each(function () {
var $cell = $(this);
if ($cell.is(':visible')) {
totalWidthInPixel += $cell.outerWidth();
}
});
//Calculate width of each column
var columnWidhts = {};
var availableWidthInPercent = 100.0 - commandColumnHeaders.length;
headerCells.each(function () {
var $cell = $(this);
if ($cell.is(':visible')) {
var fieldName = $cell.data('fieldName');
var widthInPercent = $cell.outerWidth() * availableWidthInPercent / totalWidthInPixel;
columnWidhts[fieldName] = widthInPercent;
}
});
//Set width of each column
headerCells.each(function () {
var $cell = $(this);
if ($cell.is(':visible')) {
var fieldName = $cell.data('fieldName');
$cell.data('width-in-percent', columnWidhts[fieldName]).css('width', columnWidhts[fieldName] + '%');
}
});
},
/* Saves field setting to cookie.
* Saved setting will be a string like that:
* fieldName1=visible;23|fieldName2=hidden;17|...
*************************************************************************/
_saveColumnSettings: function () {
var self = this;
var fieldSettings = '';
this._$table.find('>thead >tr >th.jtable-column-header').each(function () {
var $cell = $(this);
var fieldName = $cell.data('fieldName');
var columnWidth = $cell.data('width-in-percent');
var fieldVisibility = self.options.fields[fieldName].visibility;
var fieldSetting = fieldName + "=" + fieldVisibility + ';' + columnWidth;
fieldSettings = fieldSettings + fieldSetting + '|';
});
this._setCookie('column-settings', fieldSettings.substr(0, fieldSettings.length - 1));
},
/* Loads field settings from cookie that is saved by _saveFieldSettings method.
*************************************************************************/
_loadColumnSettings: function () {
var self = this;
var columnSettingsCookie = this._getCookie('column-settings');
if (!columnSettingsCookie) {
return;
}
var columnSettings = {};
$.each(columnSettingsCookie.split('|'), function (inx, fieldSetting) {
var splitted = fieldSetting.split('=');
var fieldName = splitted[0];
var settings = splitted[1].split(';');
columnSettings[fieldName] = {
columnVisibility: settings[0],
columnWidth: settings[1]
};
});
var headerCells = this._$table.find('>thead >tr >th.jtable-column-header');
headerCells.each(function () {
var $cell = $(this);
var fieldName = $cell.data('fieldName');
var field = self.options.fields[fieldName];
if (columnSettings[fieldName]) {
if (field.visibility != 'fixed') {
self._changeColumnVisibilityInternal(fieldName, columnSettings[fieldName].columnVisibility);
}
$cell.data('width-in-percent', columnSettings[fieldName].columnWidth).css('width', columnSettings[fieldName].columnWidth + '%');
}
});
}
});
})(jQuery);