2020-01-24 12:14:08 +01:00
/ * *
* EGroupware eTemplate2 - dataview code
*
* @license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
2021-06-07 17:33:53 +02:00
* @link https : //www.egroupware.org
2020-01-24 12:14:08 +01:00
* @author Andreas Stöckel
2021-06-07 17:33:53 +02:00
* @copyright EGroupware GmbH 2011 - 2021
* /
2020-01-24 12:14:08 +01:00
/ * e g w : u s e s
/ v e n d o r / b o w e r - a s s e t / j q u e r y / d i s t / j q u e r y . j s ;
et2_core_common ;
et2_dataview_model_columns ;
et2_dataview_view_grid ;
2020-01-31 21:07:27 +01:00
et2_dataview_view_rowProvider ;
2020-01-24 12:14:08 +01:00
et2_dataview_view_resizeable ;
* /
2021-06-07 17:33:53 +02:00
import { et2_dataview_column , et2_dataview_columns } from './et2_dataview_model_columns' ;
2020-01-30 00:05:01 +01:00
import { et2_dataview_view_resizable } from "./et2_dataview_view_resizeable" ;
2020-01-31 21:07:27 +01:00
import { et2_dataview_grid } from "./et2_dataview_view_grid" ;
import { et2_dataview_rowProvider } from "./et2_dataview_view_rowProvider"
2021-06-10 15:40:49 +02:00
import { egw } from "../jsapi/egw_global" ;
2020-01-30 00:05:01 +01:00
2020-01-24 12:14:08 +01:00
/ * *
* The et2_dataview class is the main class for displaying a dataview . The
* dataview class manages the creation of the outer html nodes ( like the table ,
* header , etc . ) and contains the root container : an instance of
* et2_dataview_view_grid , which can be accessed using the "grid" property of
* this object .
*
* @augments Class
* /
export class et2_dataview
{
/ * *
* Constant which regulates the column padding .
* /
columnPadding : number ;
/ * *
* Some browser dependant variables which will be calculated on creation of
* the first gridContainer object .
* /
scrollbarWidth : number ;
headerBorderWidth : number ;
columnBorderWidth : number ;
private width : number ;
private height : number ;
private uniqueId : string ;
/ * *
* Hooks to allow parent to keep up to date if things change
* /
onUpdateColumns : Function ;
selectColumnsClick : Function ;
private parentNode : JQuery ;
egw : any ;
private columnNodes : any [ ] ;
private columns : any [ ] ;
private columnMgr : et2_dataview_columns ;
2021-06-07 17:33:53 +02:00
rowProvider : et2_dataview_rowProvider ;
2020-01-24 12:14:08 +01:00
2021-06-07 17:33:53 +02:00
grid : et2_dataview_grid ;
2020-01-24 12:14:08 +01:00
// DOM stuff
private selectColIcon : JQuery ;
private headTr : any ;
private containerTr : JQuery ;
private selectCol : JQuery ;
private thead : JQuery ;
private tbody : JQuery ;
private table : JQuery ;
private visibleColumnCount : number ;
/ * *
* Constructor for the grid container
*
* @param { DOMElement } _parentNode is the DOM - Node into which the grid view will be inserted
* @param { egw } _egw
* @memberOf et2_dataview
* /
constructor ( _parentNode , _egw ) {
// Copy the arguments
this . parentNode = jQuery ( _parentNode ) ;
this . egw = _egw ;
// Initialize some variables
this . columnNodes = [ ] ; // Array with the header containers
this . columns = [ ] ;
this . columnMgr = null ;
this . rowProvider = null ;
this . width = 0 ;
this . height = 0 ;
this . uniqueId = "gridCont_" + this . egw . uid ( ) ;
// Build the base nodes
this . _createElements ( ) ;
// Read the browser dependant variables
this . _getDepVars ( ) ;
}
/ * *
* Destroys the object , removes all dom nodes and clears all references .
* /
destroy ( )
{
// Clear the columns
this . _clearHeader ( ) ;
// Free the grid
if ( this . grid )
{
2020-01-31 21:07:27 +01:00
this . grid . destroy ( ) ;
2020-01-24 12:14:08 +01:00
}
// Free the row provider
if ( this . rowProvider )
{
2020-01-31 21:07:27 +01:00
this . rowProvider . destroy ( ) ;
2020-01-24 12:14:08 +01:00
}
// Detatch the outer element
this . table . remove ( ) ;
}
/ * *
* Clears all data rows and reloads them
* /
clear ( )
{
if ( this . grid )
{
this . grid . clear ( ) ;
}
}
/ * *
* Returns the column container node for the given column index
*
* @param _columnIdx the integer column index
* /
getHeaderContainerNode ( _columnIdx )
{
if ( typeof this . columnNodes [ _columnIdx ] != "undefined" )
{
return this . columnNodes [ _columnIdx ] . container [ 0 ] ;
}
return null ;
}
/ * *
* Sets the column descriptors and creates the column header according to it .
* The inner grid will be emptied if it has already been built .
* /
setColumns ( _columnData )
{
// Free all column objects which have been created till this moment
this . _clearHeader ( ) ;
// Copy the given column data
this . columnMgr = new et2_dataview_columns ( _ columnData ) ;
// Create the stylesheets
this . updateColumns ( ) ;
// Build the header row
this . _buildHeader ( ) ;
// Build the grid
this . _buildGrid ( ) ;
}
/ * *
* Resizes the grid
* /
resize ( _w : number , _h : number )
{
// Not fully initialized yet...
if ( ! this . columnMgr ) return ;
if ( this . width != _w )
{
this . width = _w ;
// Take grid border width into account
_w -= ( this . table . outerWidth ( true ) - this . table . innerWidth ( ) ) ;
// Take grid header border's width into account. eg. category colors may add extra pixel into width
_w = _w - ( this . thead . find ( 'tr' ) . outerWidth ( ) - this . thead . find ( 'tr' ) . innerWidth ( ) ) ;
// Rebuild the column stylesheets
this . columnMgr . setTotalWidth ( _w - this . scrollbarWidth ) ;
this . _updateColumns ( ) ;
}
if ( this . height != _h )
{
this . height = _h ;
// Set the height of the grid.
if ( this . grid )
{
this . grid . setScrollHeight ( this . height -
this . headTr . outerHeight ( true ) ) ;
}
}
}
/ * *
* Returns the column manager object . You can use it to set the visibility
* of columns etc . Call "updateHeader" if you did any changes .
* /
getColumnMgr() {
return this . columnMgr ;
}
/ * *
* Recalculates the stylesheets which determine the column visibility and
* width .
*
* @param setDefault boolean Allow admins to save current settings as default for all users
* /
updateColumns ( setDefault : boolean = false )
{
if ( this . columnMgr )
{
this . _updateColumns ( ) ;
}
// Ability to notify parent / someone else
if ( this . onUpdateColumns )
{
this . onUpdateColumns ( setDefault ) ;
}
}
/* --- PRIVATE FUNCTIONS --- */
/* --- Code for building the grid container DOM-Tree elements ---- */
/ * *
* Builds the base DOM - Tree elements
* /
private _createElements ( )
{
/ *
Structure :
< table class = "egwGridView_outer" >
< thead >
< tr > [ HEAD ] < / tr >
< / thead >
< tbody >
< tr > [ GRID CONTAINER ] < / tr >
< / tbody >
< / table >
* /
this . containerTr = jQuery ( document . createElement ( "tr" ) ) ;
this . headTr = jQuery ( document . createElement ( "tr" ) ) ;
this . thead = jQuery ( document . createElement ( "thead" ) )
. append ( this . headTr ) ;
this . tbody = jQuery ( document . createElement ( "tbody" ) )
. append ( this . containerTr ) ;
this . table = jQuery ( document . createElement ( "table" ) )
. addClass ( "egwGridView_outer" )
. append ( this . thead , this . tbody )
. appendTo ( this . parentNode ) ;
}
/* --- Code for building the header row --- */
/ * *
* Clears the header row
* /
private _clearHeader ( )
{
if ( this . columnMgr )
{
2020-01-30 00:05:01 +01:00
this . columnMgr . destroy ( ) ;
2020-01-24 12:14:08 +01:00
this . columnMgr = null ;
}
// Remove dynamic CSS,
for ( var i = 0 ; i < this . columns . length ; i ++ )
{
if ( this . columns [ i ] . tdClass )
{
this . egw . css ( '.' + this . columns [ i ] . tdClass ) ;
}
if ( this . columns [ i ] . divClass )
{
this . egw . css ( '.' + this . columns [ i ] . divClass ) ;
this . egw . css ( ".egwGridView_outer ." + this . columns [ i ] . divClass ) ;
this . egw . css ( ".egwGridView_grid ." + this . columns [ i ] . divClass ) ;
}
}
this . egw . css ( ".egwGridView_grid ." + this . uniqueId + "_div_fullRow" ) ;
this . egw . css ( ".egwGridView_outer ." + this . uniqueId + "_td_fullRow" ) ;
this . egw . css ( ".egwGridView_outer ." + this . uniqueId + "_spacer_fullRow" ) ;
// Reset the headerColumns array and empty the table row
this . columnNodes = [ ] ;
this . columns = [ ] ;
this . headTr . empty ( ) ;
}
/ * *
* Sets the column data which is retrieved by calling egwGridColumns . getColumnData .
* The columns will be updated .
* /
private _updateColumns ( )
{
// Copy the columns data
this . columns = this . columnMgr . getColumnData ( ) ;
// Count the visible rows
var total_cnt = 0 ;
for ( var i = 0 ; i < this . columns . length ; i ++ )
{
if ( this . columns [ i ] . visible )
{
total_cnt ++ ;
}
}
// Set the grid column styles
var first = true ;
var vis_col = this . visibleColumnCount = 0 ;
var totalWidth = 0 ;
for ( var i = 0 ; i < this . columns . length ; i ++ )
{
var col = this . columns [ i ] ;
col . tdClass = this . uniqueId + "_td_" + col . id ;
col . divClass = this . uniqueId + "_div_" + col . id ;
if ( col . visible )
{
vis_col ++ ;
this . visibleColumnCount ++ ;
// Update the visibility of the column
this . egw . css ( "." + col . tdClass ,
"display: table-cell; " +
"!important;" ) ;
// Ugly browser dependant code - each browser seems to treat the
// right (collapsed) border of the row differently
var subBorder = 0 ;
var subHBorder = 0 ;
/ *
if ( jQuery . browser . mozilla )
{
var maj = jQuery . browser . version . split ( "." ) [ 0 ] ;
if ( maj < 2 ) {
subBorder = 1 ; // Versions <= FF 3.6
}
}
if ( jQuery . browser . webkit )
{
if ( ! first )
{
subBorder = 1 ;
}
subHBorder = 1 ;
}
if ( ( jQuery . browser . msie || jQuery . browser . opera ) && first )
{
subBorder = - 1 ;
}
* /
// Make the last columns one pixel smaller, to prevent a horizontal
// scrollbar from showing up
if ( vis_col == total_cnt )
{
subBorder += 1 ;
}
// Write the width of the header columns
var headerWidth = Math . max ( 0 , ( col . width - this . headerBorderWidth - subHBorder ) ) ;
this . egw . css ( ".egwGridView_outer ." + col . divClass ,
"width: " + headerWidth + "px;" ) ;
// Write the width of the body-columns
var columnWidth = Math . max ( 0 , ( col . width - this . columnBorderWidth - subBorder ) ) ;
this . egw . css ( ".egwGridView_grid ." + col . divClass ,
"width: " + columnWidth + "px;" ) ;
totalWidth += col . width ;
first = false ;
}
else
{
this . egw . css ( "." + col . tdClass , "display: none;" ) ;
}
}
// Add the full row and spacer class
this . egw . css ( ".egwGridView_grid ." + this . uniqueId + "_div_fullRow" ,
"width: " + ( totalWidth - this . columnBorderWidth - 2 ) + "px; border-right-width: 0 !important;" ) ;
this . egw . css ( ".egwGridView_outer ." + this . uniqueId + "_td_fullRow" ,
"border-right-width: 0 !important;" ) ;
this . egw . css ( ".egwGridView_outer ." + this . uniqueId + "_spacer_fullRow" ,
"width: " + ( totalWidth - 1 ) + "px; border-right-width: 0 !important;" ) ;
}
/ * *
* Builds the containers for the header row
* /
private _buildHeader ( )
{
var self = this ;
var handler = function ( event ) {
} ;
for ( var i = 0 ; i < this . columns . length ; i ++ )
{
var col = this . columns [ i ] ;
// Create the column header and the container element
var cont = jQuery ( document . createElement ( "div" ) )
. addClass ( "innerContainer" )
. addClass ( col . divClass ) ;
var column = jQuery ( document . createElement ( "th" ) )
. addClass ( col . tdClass )
. attr ( "align" , "left" )
. append ( cont )
. appendTo ( this . headTr ) ;
2020-01-30 00:05:01 +01:00
if ( this . columnMgr && this . columnMgr . getColumnById ( i ) )
2020-01-24 12:14:08 +01:00
{
2020-01-30 00:05:01 +01:00
column . addClass ( this . columnMgr . getColumnById ( i ) . fixedWidth ? 'fixedWidth' : 'relativeWidth' ) ;
if ( this . columnMgr . getColumnById ( i ) . visibility === et2_dataview_column . ET2_COL_VISIBILITY_ALWAYS_NOSELECT )
2020-01-24 12:14:08 +01:00
{
column . addClass ( 'noResize' ) ;
}
}
// make column resizable
var enc_column = self . columnMgr . getColumnById ( col . id ) ;
if ( enc_column . visibility !== et2_dataview_column . ET2_COL_VISIBILITY_ALWAYS_NOSELECT )
{
et2_dataview_view_resizable . makeResizeable ( column , function ( _w ) {
// User wants the column to stay where they put it, even for relative
// width columns, so set it explicitly first and adjust other relative
// columns to match.
if ( this . relativeWidth )
{
// Set to selected width
this . set_width ( _w + "px" ) ;
2020-01-31 21:07:27 +01:00
self . columnMgr . updated ( ) ;
2020-01-24 12:14:08 +01:00
// Just triggers recalculation
self . columnMgr . getColumnWidth ( 0 ) ;
// Set relative widths to match
var relative = self . columnMgr . totalWidth - self . columnMgr . totalFixed + _w ;
this . set_width ( _w / relative ) ;
2020-01-31 21:07:27 +01:00
for ( var i = 0 ; i < self . columnMgr . columnCount ( ) ; i ++ )
2020-01-24 12:14:08 +01:00
{
2020-06-15 19:42:45 +02:00
var col = self . columnMgr . getColumnById ( 'col_' + i ) ;
if ( ! col || col == this || col . fixedWidth ) continue ;
2020-01-30 00:05:01 +01:00
col . set_width ( self . columnMgr . getColumnWidth ( i ) / relative ) ;
2020-01-24 12:14:08 +01:00
}
// Triggers column change callback, which saves
self . updateColumns ( ) ;
}
else
{
this . set_width ( this . relativeWidth ? ( _w / self . columnMgr . totalWidth ) : _w + "px" ) ;
2020-01-31 21:07:27 +01:00
self . columnMgr . updated ( ) ;
2020-01-24 12:14:08 +01:00
self . updateColumns ( ) ;
}
} , enc_column ) ;
}
// Store both nodes in the columnNodes array
this . columnNodes . push ( {
"column" : column ,
"container" : cont
} ) ;
}
this . _buildSelectCol ( ) ;
}
/ * *
* Builds the select cols column
* /
private _buildSelectCol ( )
{
// Build the "select columns" icon
this . selectColIcon = jQuery ( document . createElement ( "span" ) )
. addClass ( "selectcols" )
. css ( 'display' , 'inline-block' ) ; // otherwise jQuery('span.selectcols',this.dataview.headTr).show() set it to "inline" causing it to not show up because 0 height
// Build the option column
this . selectCol = jQuery ( document . createElement ( "th" ) )
. addClass ( "optcol" )
. append ( this . selectColIcon )
// Toggle display of option popup
. click ( this , function ( e ) { if ( e . data . selectColumnsClick ) e . data . selectColumnsClick ( e ) ; } )
. appendTo ( this . headTr ) ;
this . selectCol . css ( "width" , this . scrollbarWidth - this . selectCol . outerWidth ( )
+ this . selectCol . width ( ) + 1 ) ;
}
/ * *
* Builds the inner grid class
* /
private _buildGrid ( )
{
// Create the collection of column ids
2020-03-11 17:56:51 +01:00
var colIds = [ ] ;
2020-01-24 12:14:08 +01:00
for ( var i = 0 ; i < this . columns . length ; i ++ )
{
2020-03-11 17:56:51 +01:00
if ( this . columns [ i ] . visible )
{
colIds [ i ] = this . columns [ i ] . id ;
}
2020-01-24 12:14:08 +01:00
}
// Create the row provider
2020-03-11 17:56:51 +01:00
if ( this . rowProvider )
2020-01-24 12:14:08 +01:00
{
2020-01-31 21:07:27 +01:00
this . rowProvider . destroy ( ) ;
2020-01-24 12:14:08 +01:00
}
this . rowProvider = new et2_dataview_rowProvider ( this . uniqueId , colIds ) ;
// Create the grid class and pass "19" as the starting average row height
this . grid = new et2_dataview_grid ( null , null , this . egw , this . rowProvider , 19 ) ;
// Insert the grid into the DOM-Tree
2020-01-31 21:07:27 +01:00
var tr = jQuery ( this . grid . getFirstNode ( ) ) ;
2020-01-24 12:14:08 +01:00
this . containerTr . replaceWith ( tr ) ;
this . containerTr = tr ;
}
/* --- Code for calculating the browser/css depending widths --- */
/ * *
* Reads the browser dependant variables
* /
private _getDepVars ( )
{
if ( typeof this . scrollbarWidth === 'undefined' )
{
// Clone the table and attach it to the outer body tag
var clone = this . table . clone ( ) ;
2020-05-18 20:55:57 +02:00
jQuery ( egw . top . document . getElementsByTagName ( "body" ) [ 0 ] )
2020-01-24 12:14:08 +01:00
. append ( clone ) ;
// Read the scrollbar width
this . scrollbarWidth = this . constructor . prototype . scrollbarWidth =
this . _getScrollbarWidth ( clone ) ;
// Read the header border width
this . headerBorderWidth = this . constructor . prototype . headerBorderWidth =
this . _getHeaderBorderWidth ( clone ) ;
// Read the column border width
this . columnBorderWidth = this . constructor . prototype . columnBorderWidth =
this . _getColumnBorderWidth ( clone ) ;
// Remove the cloned DOM-Node again from the outer body
clone . remove ( ) ;
}
}
/ * *
* Reads the scrollbar width
* /
private _getScrollbarWidth ( _table : JQuery )
{
// Create a temporary td and two divs, which are inserted into the
// DOM-Tree. The outer div has a fixed size and "overflow" set to auto.
// When the second div is inserted, it will be forced to display a scrollbar.
var div_inner = jQuery ( document . createElement ( "div" ) )
. css ( "height" , "1000px" ) ;
var div_outer = jQuery ( document . createElement ( "div" ) )
. css ( "height" , "100px" )
. css ( "width" , "100px" )
. css ( "overflow" , "auto" )
. append ( div_inner ) ;
var td = jQuery ( document . createElement ( "td" ) )
. append ( div_outer ) ;
// Store the scrollbar width statically.
jQuery ( "tbody tr" , _table ) . append ( td ) ;
var width = Math . max ( 10 , div_outer . outerWidth ( ) - div_inner . outerWidth ( ) ) ;
// Remove the elements again
div_outer . remove ( ) ;
return width ;
}
/ * *
* Calculates the total width of the header column border
* /
private _getHeaderBorderWidth ( _table : JQuery )
{
// Create a temporary th which is appended to the outer thead row
var cont = jQuery ( document . createElement ( "div" ) )
. addClass ( "innerContainer" ) ;
var th = jQuery ( document . createElement ( "th" ) )
. append ( cont ) ;
// Insert the th into the document tree
jQuery ( "thead tr" , _table ) . append ( th ) ;
// Calculate the total border width
var width = th . outerWidth ( true ) - cont . width ( ) ;
// Remove the appended element again
th . remove ( ) ;
return width ;
}
/ * *
* Calculates the total width of the column border
* /
private _getColumnBorderWidth ( _table : JQuery )
{
// Create a temporary th which is appended to the outer thead row
var cont = jQuery ( document . createElement ( "div" ) )
. addClass ( "innerContainer" ) ;
var td = jQuery ( document . createElement ( "td" ) )
. append ( cont ) ;
// Insert the th into the document tree
jQuery ( "tbody tr" , _table ) . append ( td ) ;
// Calculate the total border width
_table . addClass ( "egwGridView_grid" ) ;
var width = td . outerWidth ( true ) - cont . width ( ) ;
// Remove the appended element again
td . remove ( ) ;
return width ;
}
}