2011-08-10 16:36:31 +02:00
/ * *
2011-08-24 09:18:59 +02:00
* EGroupware eTemplate2 - JS Tabs object
2011-08-10 16:36:31 +02:00
*
* @ license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @ package etemplate
* @ subpackage api
2021-06-07 17:33:53 +02:00
* @ link https : //www.egroupware.org
2011-08-10 16:36:31 +02:00
* @ author Andreas Stöckel
* /
/ * e g w : u s e s
2020-01-21 17:51:44 +01:00
jsapi . egw ;
/ 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 _valueWidget ;
2011-08-10 16:36:31 +02:00
* /
2021-06-07 17:33:53 +02:00
import { ClassWithAttributes } from "./et2_core_inheritance" ;
import { et2 _createWidget , et2 _register _widget } from "./et2_core_widget" ;
import { et2 _valueWidget } from './et2_core_valueWidget' ;
import { et2 _nextmatch } from "./et2_extension_nextmatch" ;
import { et2 _no _init } from "./et2_core_common" ;
import { et2 _directChildrenByTagName , et2 _filteredNodeIterator , et2 _readAttrWithDefault } from "./et2_core_xml" ;
2011-08-10 16:36:31 +02:00
/ * *
* Class which implements the tabbox - tag
2014-03-12 18:54:52 +01:00
* /
2021-06-07 17:33:53 +02:00
export class et2 _tabbox extends et2 _valueWidget {
2020-01-21 17:51:44 +01:00
/ * *
* Constructor
* /
2021-06-07 17:33:53 +02:00
constructor ( _parent , _attrs , _child ) {
2020-01-21 17:51:44 +01:00
// Call the inherited constructor
2021-06-07 17:33:53 +02:00
super ( _parent , _attrs , ClassWithAttributes . extendAttributes ( et2 _tabbox . _attributes , _child || { } ) ) ;
2020-01-21 17:51:44 +01:00
/ * *
* Currently selected tab
* /
2021-06-07 17:33:53 +02:00
this . selected _index = 0 ;
this . tabData = [ ] ;
2020-01-21 17:51:44 +01:00
// Create the outer tabbox container
2021-06-07 17:33:53 +02:00
this . container = jQuery ( document . createElement ( "div" ) )
2020-01-21 17:51:44 +01:00
. addClass ( "et2_tabbox" ) ;
// Create the upper container for the tab flags
2021-06-07 17:33:53 +02:00
this . flagContainer = jQuery ( document . createElement ( "ul" ) )
2020-01-21 17:51:44 +01:00
. addClass ( "et2_tabheader" )
2021-05-13 18:01:38 +02:00
. attr ( "role" , "tablist" )
2021-06-07 17:33:53 +02:00
. appendTo ( this . container ) ;
2020-01-21 17:51:44 +01:00
// Create the lower tab container
2021-06-07 17:33:53 +02:00
this . tabContainer = jQuery ( document . createElement ( "div" ) )
2020-01-21 17:51:44 +01:00
. addClass ( "et2_tabs" )
2021-06-07 17:33:53 +02:00
. appendTo ( this . container ) ;
2020-01-21 17:51:44 +01:00
}
2021-06-07 17:33:53 +02:00
destroy ( ) {
super . destroy ( ) ;
2020-01-21 17:51:44 +01:00
this . container = null ;
this . flagContainer = null ;
this . tabData = [ ] ;
2021-06-07 17:33:53 +02:00
}
_readTabs ( tabData , tabs ) {
2020-01-21 17:51:44 +01:00
var selected = "" ;
this . selected _index = false ;
var hidden = { } ;
if ( this . id ) {
// Set the value for this element
var contentMgr = this . getArrayMgr ( "content" ) ;
if ( contentMgr != null ) {
var val = contentMgr . getEntry ( this . id ) ;
if ( val !== null ) {
selected = val ;
}
}
contentMgr = this . getArrayMgr ( "readonlys" ) ;
if ( contentMgr != null ) {
var val = contentMgr . getEntry ( this . id ) ;
if ( val !== null && typeof val !== 'undefined' ) {
hidden = val ;
}
}
}
var i = 0 ;
et2 _filteredNodeIterator ( tabs , function ( node , nodeName ) {
if ( nodeName == "tab" ) {
2021-06-07 17:33:53 +02:00
const index _name = et2 _readAttrWithDefault ( node , "id" , '' ) ;
2020-01-21 17:51:44 +01:00
var hide = false ;
var widget _options = { } ;
if ( index _name ) {
if ( selected == index _name )
this . selected _index = i ;
if ( hidden [ index _name ] ) {
hide = true ;
}
// Get the class attribute and add it as widget_options
2021-06-07 17:33:53 +02:00
const classAttr = et2 _readAttrWithDefault ( node , "class" , '' ) ;
2020-01-21 17:51:44 +01:00
if ( classAttr ) {
widget _options = { 'class' : classAttr } ;
}
}
tabData . push ( {
"id" : index _name ,
"label" : this . egw ( ) . lang ( et2 _readAttrWithDefault ( node , "label" , "Tab" ) ) ,
"widget" : null ,
"widget_options" : widget _options ,
"contentDiv" : null ,
"flagDiv" : null ,
"hidden" : hide ,
"XMLNode" : null ,
"promise" : null
} ) ;
}
else {
throw ( "Error while parsing: Invalid tag '" + nodeName +
"' in tabs tag" ) ;
}
i ++ ;
} , this ) ;
// Make sure we don't try to display a hidden tab
for ( var i = 0 ; i < tabData . length && this . selected _index === false ; i ++ ) {
if ( ! tabData [ i ] . hidden )
this . selected _index = i ;
}
2021-06-07 17:33:53 +02:00
}
_readTabPanels ( tabData , tabpanels ) {
2020-01-21 17:51:44 +01:00
var i = 0 ;
et2 _filteredNodeIterator ( tabpanels , function ( node , nodeName ) {
if ( i < tabData . length ) {
// Store node for later evaluation
tabData [ i ] . XMLNode = node ;
}
else {
throw ( "Error while reading tabpanels tag, too many widgets!" ) ;
}
i ++ ;
} , this ) ;
2021-06-07 17:33:53 +02:00
}
loadFromXML ( _node ) {
2020-01-21 17:51:44 +01:00
// Get the tabs and tabpanels tags
var tabsElems = et2 _directChildrenByTagName ( _node , "tabs" ) ;
var tabpanelsElems = et2 _directChildrenByTagName ( _node , "tabpanels" ) ;
var tabData = [ ] ;
// Check for a parent height, we'll apply it to tab panels
var height = et2 _readAttrWithDefault ( _node . parentNode , "height" , null ) ;
if ( height ) {
this . tabContainer . css ( "height" , height ) ;
}
// if no tabs set or they should be added to tabs from xml
if ( ! this . options . tabs || this . options . add _tabs ) {
if ( tabsElems . length == 1 && tabpanelsElems . length == 1 ) {
var tabs = tabsElems [ 0 ] ;
var tabpanels = tabpanelsElems [ 0 ] ;
// Parse the "tabs" tag
this . _readTabs ( tabData , tabs ) ;
// Read and create the widgets defined in the "tabpanels"
this . _readTabPanels ( tabData , tabpanels ) ;
}
else {
this . egw ( ) . debug ( "error" , "Error while parsing tabbox, none or multiple tabs or tabpanels tags!" , this ) ;
}
}
if ( this . options . tabs ) {
var readonly = this . getArrayMgr ( "readonlys" ) . getEntry ( this . id ) || { } ;
for ( var i = 0 ; i < this . options . tabs . length ; i ++ ) {
var tab = this . options . tabs [ i ] ;
var tab _id = tab . id || tab . template ;
var tab _options = { id : tab _id , template : tab . template , url : tab . url , content : undefined } ;
if ( tab . id ) {
tab _options . content = tab . id ;
}
tabData [ tab . prepend ? 'unshift' : 'push' ] . call ( tabData , {
"id" : tab _id ,
"label" : this . egw ( ) . lang ( tab . label ) ,
"widget" : null ,
"widget_options" : tab _options ,
"contentDiv" : null ,
"flagDiv" : null ,
"hidden" : typeof tab . hidden != "undefined" ? tab . hidden : readonly [ tab _id ] || false ,
"XMLNode" : null ,
"promise" : null
} ) ;
}
}
// Create the tab DOM-Nodes
this . createTabs ( tabData ) ;
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Load is finished , set up tabs to load on their own
* /
2021-06-07 17:33:53 +02:00
doLoadingFinished ( ) {
2020-01-21 17:51:44 +01:00
var tab _deferred = jQuery . Deferred ( ) ;
var promises = [ ] ;
var tabs = this ;
// Specially process the selected index so it shows up right away
this . _loadTab ( this . selected _index , promises ) ;
2021-02-26 23:22:47 +01:00
// Avoid reloading if tabs were modified by data
if ( this . isInTree ( ) && this . isAttached ( ) )
return ;
2020-01-21 17:51:44 +01:00
// Apply parent now, which actually puts into the DOM
// This has to be before loading the child, so the dom sub-tree is not
// disconnected, which causes problems for things like CKEditor
2021-06-07 17:33:53 +02:00
super . doLoadingFinished ( ) ;
2020-01-21 17:51:44 +01:00
// We can do this and not wind up with 2 because child is a template,
// which has special handling
this . _children [ 0 ] . loadingFinished ( promises ) ;
// Defer parsing & loading of other tabs until later
window . setTimeout ( function ( ) {
for ( var i = 0 ; i < tabs . tabData . length ; i ++ ) {
if ( i == tabs . selected _index )
continue ;
tabs . _loadTab ( i , promises ) ;
}
jQuery . when . apply ( jQuery , promises ) . then ( function ( ) {
tab _deferred . resolve ( ) ;
2020-06-19 21:27:41 +02:00
tabs . resetDirty ( ) ;
2020-01-21 17:51:44 +01:00
} ) ;
} , 0 ) ;
return tab _deferred . promise ( ) ;
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Load & render a tab ' s content
*
* @ param { number } index numerical index of tab in this . tabData array
* @ param { array } promises
* /
2021-06-07 17:33:53 +02:00
_loadTab ( index , promises ) {
2020-01-21 17:51:44 +01:00
var tabData = this . tabData [ index ] ;
if ( ! tabData || tabData . loaded )
return ;
2021-02-26 23:22:47 +01:00
// Set loaded flag to not do this again, even if not fully done
tabData . loaded = true ;
2020-01-21 17:51:44 +01:00
if ( tabData . XMLNode != null ) {
if ( tabData . hidden ) {
// Set hidden tab to readonly, so widgets aren't active
// Do not modify the XMLNode, or the change will be cached for all
tabData . XMLNode = tabData . XMLNode . cloneNode ( ) ;
tabData . XMLNode . setAttribute ( 'readonly' , true ) ;
}
tabData . widget = this . createElementFromNode ( tabData . XMLNode , tabData . XMLNode . nodeName . toLowerCase ( ) ) ;
// Release the XML node
tabData . XMLNode = null ;
}
else if ( tabData . widget _options ) {
2021-06-07 17:33:53 +02:00
tabData . widget = et2 _createWidget ( 'template' , tabData . widget _options , this ) ;
2020-01-21 17:51:44 +01:00
}
// loadingFinished() will be called either when the promise from doLoadingFinished is resolved,
// or during the normal execution
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Check for custom tabs
*
* @ param { object } _attrs
* /
2021-06-07 17:33:53 +02:00
transformAttributes ( _attrs ) {
super . transformAttributes ( _attrs ) ;
2020-01-21 17:51:44 +01:00
// Add in settings that are objects
var data = this . getArrayMgr ( "modifications" ) . getEntry ( this . id ) ;
for ( var key in data ) {
if ( typeof data [ key ] === 'object' && ! _attrs [ key ] )
_attrs [ key ] = data [ key ] ;
}
2021-06-07 17:33:53 +02:00
}
createTabs ( tabData ) {
2020-01-21 17:51:44 +01:00
this . tabData = tabData ;
this . tabContainer . empty ( ) ;
this . flagContainer . empty ( ) ;
for ( var i = 0 ; i < this . tabData . length ; i ++ ) {
var entry = this . tabData [ i ] ;
2021-05-13 18:01:38 +02:00
entry . flagDiv = jQuery ( document . createElement ( "li" ) )
2020-01-21 17:51:44 +01:00
. addClass ( "et2_tabflag" )
. appendTo ( this . flagContainer ) ;
// Class to tab's div container
if ( entry . widget _options && typeof entry . widget _options . class != 'undefined' ) {
entry . flagDiv . addClass ( entry . widget _options . class ) ;
}
2021-05-13 18:01:38 +02:00
entry . flagDiv . html ( "<a href=\"#\" role='tab'>" + ( entry . label || "Tab" ) + "</a>" ) ;
2020-01-21 17:51:44 +01:00
if ( entry . hidden || this . tabData . length === 1 ) {
entry . flagDiv . hide ( ) ;
}
else {
entry . flagDiv . click ( { "tabs" : this , "idx" : i } , function ( e ) {
e . data . tabs . setActiveTab ( e . data . idx ) ;
} ) ;
}
entry . contentDiv = jQuery ( document . createElement ( "div" ) )
. addClass ( "et2_tabcntr" )
2021-05-13 18:01:38 +02:00
. attr ( "role" , "tabpanel" )
2020-01-21 17:51:44 +01:00
. appendTo ( this . tabContainer ) ;
if ( this . options . align _tabs == 'v' ) {
entry . flagDiv . unbind ( 'click' ) ;
entry . flagDiv . text ( "" ) ;
jQuery ( document . createElement ( 'div' ) )
. addClass ( 'et2_tabtitle' )
. text ( entry . label || "Tab" )
. click ( { "tabs" : this , "idx" : i } , function ( e ) {
e . data . tabs . flagContainer . children ( ":eq(" + e . data . idx + ")" ) . toggleClass ( 'active' ) ;
if ( e . data . tabs . selected _index != e . data . idx )
e . data . tabs . setActiveTab ( e . data . idx ) ;
} )
. appendTo ( entry . flagDiv ) ;
entry . contentDiv . appendTo ( entry . flagDiv ) ;
}
}
if ( this . options . align _tabs == 'v' ) {
this . container . addClass ( 'vertical' ) ;
this . tabContainer . hide ( ) ;
}
// Check for a passed in value
if ( this . options . value ) {
this . selected _index = 0 ;
for ( var i = 0 ; i < this . tabData . length ; i ++ ) {
if ( this . tabData [ i ] . id == this . options . value ) {
this . selected _index = i ;
break ;
}
}
}
this . setActiveTab ( this . selected _index ) ;
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Gets the index of the currently active tab
*
* @ returns { number }
* /
2021-06-07 17:33:53 +02:00
get _active _tab ( ) {
2020-01-21 17:51:44 +01:00
return this . selected _index ;
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Sets the currently active tab by index
*
* @ param { number } _idx
* /
2021-06-07 17:33:53 +02:00
setActiveTab ( _idx ) {
2020-01-21 17:51:44 +01:00
this . selected _index = _idx ;
// Remove the "active" flag from all tabs-flags
2021-05-13 18:01:38 +02:00
jQuery ( ".et2_tabflag" , this . flagContainer ) . removeClass ( "active" )
. attr ( "aria-selected" , "false" ) ;
2020-01-21 17:51:44 +01:00
// Hide all tab containers
this . tabContainer . children ( ) . hide ( ) ;
// Set the tab flag with the given index active and show the corresponding
// container
2021-05-13 18:01:38 +02:00
this . flagContainer . children ( ":eq(" + _idx + ")" )
. addClass ( "active" )
. attr ( "aria-selected" , "true" ) ;
2020-01-21 17:51:44 +01:00
this . tabContainer . children ( ":eq(" + _idx + ")" ) . show ( ) ;
// lookup for nm children and trigger a resize, since nm inside inactive
// tabs are not getting render due to tab's deffer loading.
if ( this . _children . length > 0 && this . tabData && this . tabData . length > 0 ) {
this . tabData [ _idx ] [ 'widget' ] . iterateOver ( function ( nm ) {
if ( nm && nm . _type == 'nextmatch' )
nm . resize ( ) ;
2021-06-07 17:33:53 +02:00
} , this . tabData [ _idx ] [ 'widget' ] , et2 _nextmatch ) ;
2020-01-21 17:51:44 +01:00
}
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Activate the tab containing the given widget
*
* @ param { et2 _widget } widget
* @ return { bool } widget was found in a tab
* /
2021-06-07 17:33:53 +02:00
activateTab ( widget ) {
2020-01-21 17:51:44 +01:00
var tab = widget ;
while ( tab . _parent && tab . _parent . _type != 'tabbox' ) {
tab = tab . _parent ;
}
var child _index = this . _children . indexOf ( tab ) ;
for ( var i = 0 ; i < this . tabData . length ; i ++ ) {
if ( this . tabData [ i ] . widget == tab ) {
this . setActiveTab ( i ) ;
return true ;
}
}
return false ;
2021-06-07 17:33:53 +02:00
}
getDOMNode ( _sender ) {
2020-01-21 17:51:44 +01:00
if ( _sender === this || typeof _sender === 'undefined' ) {
return this . container [ 0 ] ;
}
else {
for ( var i = 0 ; i < this . tabData . length ; i ++ ) {
if ( this . tabData [ i ] . widget == _sender ) {
return this . tabData [ i ] . contentDiv [ 0 ] ;
}
}
return null ;
}
2021-06-07 17:33:53 +02:00
}
set _tab _height ( _height ) {
2020-01-21 17:51:44 +01:00
this . tab _height = _height ;
this . tabContainer . css ( "height" , _height ) ;
2021-06-07 17:33:53 +02:00
}
set _height ( _value ) {
2020-01-21 17:51:44 +01:00
this . height = _value ;
this . tabContainer . css ( "height" , _value ) ;
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* getValue has to return the value of the input widget
* /
2021-06-07 17:33:53 +02:00
getValue ( ) {
2020-01-21 17:51:44 +01:00
return this . selected _index !== false ? this . tabData [ this . selected _index ] . id : undefined ;
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Is dirty returns true if the value of the widget has changed since it
* was loaded .
* /
2021-06-07 17:33:53 +02:00
isDirty ( ) {
2020-06-23 21:37:31 +02:00
// We consider tab changes are not real changes
return false ;
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Causes the dirty flag to be reseted .
* /
2021-06-07 17:33:53 +02:00
resetDirty ( ) {
2020-01-21 17:51:44 +01:00
this . value = this . selected _index ;
2021-06-07 17:33:53 +02:00
}
isValid ( messages ) {
2020-01-21 17:51:44 +01:00
return true ;
2021-06-07 17:33:53 +02:00
}
resize ( _height ) {
2020-01-21 17:51:44 +01:00
if ( _height ) {
this . set _height ( this . tabContainer . height ( ) + _height ) ;
}
//Set the height of tabs with the heighest height
else if ( _height === 0 ) {
this . set _height ( this . tabContainer . height ( ) ) ;
}
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Set up for printing
*
* @ return { undefined | Deferred } Return a jQuery Deferred object if not done setting up
* ( waiting for data )
* /
2021-06-07 17:33:53 +02:00
beforePrint ( ) {
2020-01-21 17:51:44 +01:00
// Remove the "active" flag from all tabs-flags
jQuery ( ".et2_tabflag" , this . flagContainer ) . removeClass ( "active" ) ;
// Remove height limit
this . tabContainer . css ( "height" , '' ) ;
// Show all enabled tabs
for ( var i = 0 ; i < this . tabData . length ; i ++ ) {
var entry = this . tabData [ i ] ;
if ( entry . hidden )
continue ;
entry . flagDiv . insertBefore ( entry . contentDiv ) ;
entry . contentDiv . show ( ) ;
}
2021-06-07 17:33:53 +02:00
}
2020-01-21 17:51:44 +01:00
/ * *
* Reset after printing
* /
2021-06-07 17:33:53 +02:00
afterPrint ( ) {
2020-01-21 17:51:44 +01:00
for ( var i = 0 ; i < this . tabData . length ; i ++ ) {
var entry = this . tabData [ i ] ;
entry . flagDiv . appendTo ( this . flagContainer ) ;
}
this . setActiveTab ( this . get _active _tab ( ) ) ;
2021-06-07 17:33:53 +02:00
}
}
et2 _tabbox . _attributes = {
'tabs' : {
'name' : 'Tabs' ,
'default' : et2 _no _init ,
'description' : "Array of [extra] tabs. Each tab needs {label:..., template:...}. Additional optional keys are prepend, hidden and id, for access into content array"
} ,
'add_tabs' : {
'name' : 'Add tabs' ,
'default' : false ,
'description' : 'Set to true if tabs should be added to tabs from read from template, default false if not'
} ,
'tab_height' : {
name : 'Tabs innerHeight' ,
default : '' ,
description : 'Set the innerHeight for the tab content'
} ,
'align_tabs' : {
name : 'Tabs alignment' ,
type : 'string' ,
default : 'h' ,
description : 'Set tabs and their headers arrangment either horizental (h) or vertical (v). Default value is horizental.'
}
} ;
et2 _register _widget ( et2 _tabbox , [ "tabbox" ] ) ;
2020-01-22 18:38:33 +01:00
//# sourceMappingURL=et2_widget_tabs.js.map