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
* @ link http : //www.egroupware.org
* @ author Andreas Stöckel
* @ copyright Stylite 2011
* @ version $Id$
* /
/ * e g w : u s e s
2011-08-23 19:10:56 +02:00
jsapi . egw ;
2016-06-06 17:38:20 +02:00
/ 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 ;
2014-03-12 19:49:50 +01:00
et2 _core _valueWidget ;
2011-08-10 16:36:31 +02:00
* /
/ * *
* Class which implements the tabbox - tag
2014-03-12 18:54:52 +01:00
*
2014-03-12 19:49:50 +01:00
* @ augments et2 _valueWidget
2014-03-12 18:54:52 +01:00
* /
2016-02-29 21:40:43 +01:00
var et2 _tabbox = ( function ( ) { "use strict" ; return et2 _valueWidget . extend ( [ et2 _IInput , et2 _IResizeable ] ,
2013-04-13 21:00:13 +02:00
{
2012-07-09 23:26:24 +02:00
attributes : {
'tabs' : {
'name' : 'Tabs' ,
'default' : et2 _no _init ,
2014-03-12 18:54:52 +01:00
'description' : "Array of [extra] tabs. Each tab needs {label:..., template:...}. Additional optional keys are prepend, hidden and id, for access into content array"
2013-08-20 18:41:31 +02:00
} ,
'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'
2014-12-05 18:29:41 +01:00
} ,
2016-01-28 12:26:22 +01:00
'tab_height' : {
2014-12-05 18:29:41 +01:00
name : 'Tabs innerHeight' ,
default : '' ,
description : 'Set the innerHeight for the tab content'
2016-01-28 12:26:22 +01:00
} ,
'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.'
2012-07-09 23:26:24 +02:00
}
} ,
2011-08-23 23:33:40 +02:00
/ * *
* Currently selected tab
* /
selected _index : 0 ,
2014-01-10 15:32:03 +01:00
2013-04-13 21:00:13 +02:00
/ * *
* Construtor
2014-03-12 18:54:52 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _tabbox
* /
2011-08-24 12:05:52 +02:00
init : function ( ) {
2011-08-10 16:36:31 +02:00
// Create the outer tabbox container
2016-06-02 16:51:15 +02:00
this . container = jQuery ( document . createElement ( "div" ) )
2011-08-10 16:36:31 +02:00
. addClass ( "et2_tabbox" ) ;
2016-02-03 16:20:50 +01:00
2011-08-10 16:36:31 +02:00
// Create the upper container for the tab flags
2016-06-02 16:51:15 +02:00
this . flagContainer = jQuery ( document . createElement ( "div" ) )
2011-08-17 11:07:09 +02:00
. addClass ( "et2_tabheader" )
2011-08-10 16:36:31 +02:00
. appendTo ( this . container ) ;
// Create the lower tab container
2016-06-02 16:51:15 +02:00
this . tabContainer = jQuery ( document . createElement ( "div" ) )
2011-08-10 16:36:31 +02:00
. addClass ( "et2_tabs" )
. appendTo ( this . container ) ;
this . _super . apply ( this , arguments ) ;
this . tabData = [ ] ;
} ,
2011-08-24 12:05:52 +02:00
destroy : function ( ) {
2011-08-10 16:36:31 +02:00
this . _super . apply ( this , arguments ) ;
this . container = null ;
this . flagContainer = null ;
this . tabData = [ ] ;
} ,
_readTabs : function ( tabData , tabs ) {
2011-08-23 23:33:40 +02:00
var selected = "" ;
2014-09-10 00:19:50 +02:00
this . selected _index = false ;
2011-08-23 23:33:40 +02:00
var hidden = { } ;
if ( this . id )
{
// Set the value for this element
var contentMgr = this . getArrayMgr ( "content" ) ;
if ( contentMgr != null ) {
2011-09-08 20:36:09 +02:00
var val = contentMgr . getEntry ( this . id ) ;
2011-08-23 23:33:40 +02:00
if ( val !== null )
{
selected = val ;
}
}
contentMgr = this . getArrayMgr ( "readonlys" ) ;
if ( contentMgr != null ) {
2011-09-08 20:36:09 +02:00
var val = contentMgr . getEntry ( this . id ) ;
2011-10-18 17:41:30 +02:00
if ( val !== null && typeof val !== 'undefined' )
2011-08-23 23:33:40 +02:00
{
hidden = val ;
}
}
}
var i = 0 ;
2011-08-10 16:36:31 +02:00
et2 _filteredNodeIterator ( tabs , function ( node , nodeName ) {
if ( nodeName == "tab" )
{
2011-08-24 09:18:59 +02:00
var index _name = et2 _readAttrWithDefault ( node , "id" ) ;
2011-08-23 23:33:40 +02:00
var hide = false ;
2015-05-07 19:18:01 +02:00
var widget _options = { } ;
2011-08-23 23:33:40 +02:00
if ( index _name ) {
if ( selected == index _name ) this . selected _index = i ;
if ( hidden [ index _name ] ) {
hide = true ;
}
2015-05-07 19:18:01 +02:00
// Get the class attribute and add it as widget_options
var classAttr = et2 _readAttrWithDefault ( node , "class" ) ;
if ( classAttr )
{
widget _options = { 'class' : classAttr } ;
}
2011-08-23 23:33:40 +02:00
}
2011-08-10 16:36:31 +02:00
tabData . push ( {
2013-05-10 22:39:12 +02:00
"id" : index _name ,
2012-03-02 11:44:56 +01:00
"label" : this . egw ( ) . lang ( et2 _readAttrWithDefault ( node , "label" , "Tab" ) ) ,
2011-08-10 16:36:31 +02:00
"widget" : null ,
2015-05-07 19:18:01 +02:00
"widget_options" : widget _options ,
2011-08-10 16:36:31 +02:00
"contentDiv" : null ,
2011-08-23 23:33:40 +02:00
"flagDiv" : null ,
2014-01-10 15:32:03 +01:00
"hidden" : hide ,
2015-08-20 16:57:18 +02:00
"XMLNode" : null ,
2014-01-10 15:32:03 +01:00
"promise" : null
2011-08-10 16:36:31 +02:00
} ) ;
}
else
{
throw ( "Error while parsing: Invalid tag '" + nodeName +
"' in tabs tag" ) ;
}
2011-08-23 23:33:40 +02:00
i ++ ;
2011-08-10 16:36:31 +02:00
} , this ) ;
2014-09-10 00:19:50 +02:00
// 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 ;
}
2011-08-10 16:36:31 +02:00
} ,
_readTabPanels : function ( tabData , tabpanels ) {
var i = 0 ;
et2 _filteredNodeIterator ( tabpanels , function ( node , nodeName ) {
if ( i < tabData . length )
{
2014-01-10 15:32:03 +01:00
// Store node for later evaluation
2015-08-20 16:57:18 +02:00
tabData [ i ] . XMLNode = node ;
2011-08-10 16:36:31 +02:00
}
else
{
throw ( "Error while reading tabpanels tag, too many widgets!" ) ;
}
i ++ ;
} , this ) ;
} ,
2015-08-20 16:57:18 +02:00
loadFromXML : function ( _node ) {
2011-08-10 16:36:31 +02:00
// Get the tabs and tabpanels tags
var tabsElems = et2 _directChildrenByTagName ( _node , "tabs" ) ;
var tabpanelsElems = et2 _directChildrenByTagName ( _node , "tabpanels" ) ;
2013-05-09 11:28:27 +02:00
var tabData = [ ] ;
2011-08-10 16:36:31 +02:00
2013-05-09 11:28:27 +02:00
// Check for a parent height, we'll apply it to tab panels
var height = et2 _readAttrWithDefault ( _node . parentNode , "height" , null ) ;
if ( height )
2011-08-10 16:36:31 +02:00
{
2013-05-09 11:28:27 +02:00
this . tabContainer . css ( "height" , height ) ;
2013-05-06 19:17:52 +02:00
}
2011-08-10 16:36:31 +02:00
2013-08-20 18:41:31 +02:00
// 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 ] ;
2014-03-12 18:54:52 +01:00
2013-08-20 18:41:31 +02:00
// Parse the "tabs" tag
this . _readTabs ( tabData , tabs ) ;
2014-03-12 18:54:52 +01:00
2013-08-20 18:41:31 +02:00
// 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 )
2013-05-06 19:17:52 +02:00
{
var readonly = this . getArrayMgr ( "readonlys" ) . getEntry ( this . id ) || { } ;
for ( var i = 0 ; i < this . options . tabs . length ; i ++ )
2012-07-09 23:26:24 +02:00
{
2013-05-06 19:17:52 +02:00
var tab = this . options . tabs [ i ] ;
var tab _id = tab . id || tab . template ;
2016-02-03 16:20:50 +01:00
var tab _options = { id : tab _id , template : tab . template , url : tab . url } ;
2013-05-06 19:17:52 +02:00
if ( tab . id )
2012-07-09 23:26:24 +02:00
{
2013-05-06 19:17:52 +02:00
tab _options . content = tab . id ;
2012-07-09 23:26:24 +02:00
}
2014-03-12 18:54:52 +01:00
tabData [ tab . prepend ? 'unshift' : 'push' ] . call ( tabData , {
2018-12-04 17:05:39 +01:00
"id" : tab _id ,
2013-05-06 19:17:52 +02:00
"label" : this . egw ( ) . lang ( tab . label ) ,
2014-01-10 15:32:03 +01:00
"widget" : null ,
"widget_options" : tab _options ,
2013-05-06 19:17:52 +02:00
"contentDiv" : null ,
"flagDiv" : null ,
2014-01-10 15:32:03 +01:00
"hidden" : typeof tab . hidden != "undefined" ? tab . hidden : readonly [ tab _id ] || false ,
2015-08-20 16:57:18 +02:00
"XMLNode" : null ,
2014-01-10 15:32:03 +01:00
"promise" : null
2013-05-06 19:17:52 +02:00
} ) ;
2012-07-09 23:26:24 +02:00
}
2011-08-10 16:36:31 +02:00
}
2013-05-06 19:17:52 +02:00
// Create the tab DOM-Nodes
this . createTabs ( tabData ) ;
2011-08-10 16:36:31 +02:00
} ,
2014-01-10 15:32:03 +01:00
/ * *
* Load is finished , set up tabs to load on their own
* /
doLoadingFinished : function ( )
{
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 ) ;
2014-03-12 18:54:52 +01:00
2014-01-10 15:32:03 +01:00
// Apply parent now, which actually puts into the DOM
2014-02-11 23:44:06 +01:00
// This has to be before loading the child, so the dom sub-tree is not
// disconnected, which causes problems for things like CKEditor
2014-01-10 15:32:03 +01:00
this . _super . apply ( this , arguments ) ;
2014-03-12 18:54:52 +01:00
2014-02-11 23:44:06 +01:00
// We can do this and not wind up with 2 because child is a template,
// which has special handling
2014-10-17 13:37:15 +02:00
this . _children [ 0 ] . loadingFinished ( promises ) ;
2014-03-12 18:54:52 +01:00
2014-01-10 15:32:03 +01:00
// Defer parsing & loading of other tabs until later
window . setTimeout ( function ( ) {
for ( var i = 0 ; i < tabs . tabData . length ; i ++ )
{
2014-10-13 11:07:23 +02:00
if ( i == tabs . selected _index ) continue ;
2014-01-10 15:32:03 +01:00
tabs . _loadTab ( i , promises ) ;
}
2014-05-27 00:27:57 +02:00
jQuery . when . apply ( jQuery , promises ) . then ( function ( ) {
2014-01-10 15:32:03 +01:00
tab _deferred . resolve ( ) ;
} ) ;
} , 0 ) ;
return tab _deferred . promise ( ) ;
} ,
/ * *
* Load & render a tab ' s content
2014-03-12 18:54:52 +01:00
*
* @ param { number } index numerical index of tab in this . tabData array
* @ param { array } promises
2014-01-10 15:32:03 +01:00
* /
_loadTab : function ( index , promises ) {
var tabData = this . tabData [ index ] ;
if ( ! tabData || tabData . loaded ) return ;
2015-08-20 16:57:18 +02:00
if ( tabData . XMLNode != null )
2014-01-10 15:32:03 +01:00
{
2017-04-19 18:43:42 +02:00
if ( tabData . hidden )
{
// Set hidden tab to readonly, so widgets aren't active
2017-04-19 20:51:58 +02:00
// Do not modify the XMLNode, or the change will be cached for all
tabData . XMLNode = tabData . XMLNode . cloneNode ( ) ;
2017-04-19 18:43:42 +02:00
tabData . XMLNode . setAttribute ( 'readonly' , true ) ;
}
2015-08-20 16:57:18 +02:00
tabData . widget = this . createElementFromNode ( tabData . XMLNode , tabData . XMLNode . nodeName . toLowerCase ( ) ) ;
2014-03-12 18:54:52 +01:00
2015-08-20 16:57:18 +02:00
// Release the XML node
tabData . XMLNode = null ;
2014-01-10 15:32:03 +01:00
}
else if ( tabData . widget _options )
{
tabData . widget = et2 _createWidget ( 'template' , tabData . widget _options , this ) ;
}
// Set loaded flag to not do this again, even if not fully done
tabData . loaded = true ;
// loadingFinished() will be called either when the promise from doLoadingFinished is resolved,
// or during the normal execution
} ,
2012-07-09 23:26:24 +02:00
/ * *
* Check for custom tabs
2014-03-12 18:54:52 +01:00
*
* @ param { object } _attrs
2012-07-09 23:26:24 +02:00
* /
transformAttributes : function ( _attrs ) {
this . _super . apply ( this , arguments ) ;
// 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 ] ;
}
} ,
2011-08-10 16:36:31 +02:00
createTabs : function ( tabData ) {
this . tabData = tabData ;
this . tabContainer . empty ( ) ;
this . flagContainer . empty ( ) ;
for ( var i = 0 ; i < this . tabData . length ; i ++ )
{
var entry = this . tabData [ i ] ;
2016-06-02 16:51:15 +02:00
entry . flagDiv = jQuery ( document . createElement ( "span" ) )
2011-08-10 16:36:31 +02:00
. addClass ( "et2_tabflag" )
2013-08-20 18:43:37 +02:00
. appendTo ( this . flagContainer ) ;
2015-05-07 19:18:01 +02:00
// Class to tab's div container
if ( entry . widget _options && typeof entry . widget _options . class != 'undefined' )
{
entry . flagDiv . addClass ( entry . widget _options . class ) ;
2015-10-26 22:13:34 +01:00
}
2013-05-10 22:39:12 +02:00
entry . flagDiv . text ( entry . label || "Tab" ) ;
2011-08-23 23:33:40 +02:00
if ( entry . hidden )
{
entry . flagDiv . hide ( ) ;
}
else
{
entry . flagDiv . click ( { "tabs" : this , "idx" : i } , function ( e ) {
2011-08-17 11:07:09 +02:00
e . data . tabs . setActiveTab ( e . data . idx ) ;
} ) ;
2011-08-23 23:33:40 +02:00
}
2016-06-02 16:51:15 +02:00
entry . contentDiv = jQuery ( document . createElement ( "div" ) )
2011-08-10 16:36:31 +02:00
. addClass ( "et2_tabcntr" )
. appendTo ( this . tabContainer ) ;
2016-01-28 12:26:22 +01:00
if ( this . options . align _tabs == 'v' ) {
entry . flagDiv . unbind ( 'click' ) ;
entry . flagDiv . text ( "" ) ;
2016-06-02 16:51:15 +02:00
jQuery ( document . createElement ( 'div' ) )
2016-01-28 12:26:22 +01:00
. 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' ) ;
2016-04-26 18:38:38 +02:00
if ( e . data . tabs . selected _index != e . data . idx ) e . data . tabs . setActiveTab ( e . data . idx ) ;
2016-01-28 12:26:22 +01:00
} )
. appendTo ( entry . flagDiv ) ;
entry . contentDiv . appendTo ( entry . flagDiv ) ;
}
}
2016-02-03 16:20:50 +01:00
if ( this . options . align _tabs == 'v' ) {
2016-01-28 12:26:22 +01:00
this . container . addClass ( 'vertical' ) ;
this . tabContainer . hide ( ) ;
2011-08-10 16:36:31 +02:00
}
2013-09-18 20:01:52 +02:00
// 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 ;
}
}
}
2014-03-12 18:54:52 +01:00
2011-08-23 23:33:40 +02:00
this . setActiveTab ( this . selected _index ) ;
2011-08-17 11:07:09 +02:00
} ,
2014-06-16 17:16:59 +02:00
/ * *
* Gets the index of the currently active tab
2014-10-13 11:07:23 +02:00
*
2014-06-16 17:16:59 +02:00
* @ returns { number }
* /
get _active _tab : function ( ) {
return this . selected _index ;
} ,
/ * *
* Sets the currently active tab by index
2014-10-13 11:07:23 +02:00
*
2014-06-16 17:16:59 +02:00
* @ param { number } _idx
* /
2011-08-17 11:07:09 +02:00
setActiveTab : function ( _idx ) {
2011-08-23 23:33:40 +02:00
this . selected _index = _idx ;
2011-08-17 11:07:09 +02:00
// Remove the "active" flag from all tabs-flags
2016-06-02 16:51:15 +02:00
jQuery ( ".et2_tabflag" , this . flagContainer ) . removeClass ( "active" ) ;
2011-08-17 11:07:09 +02:00
// Hide all tab containers
this . tabContainer . children ( ) . hide ( ) ;
// Set the tab flag with the given index active and show the corresponding
// container
this . flagContainer . children ( ":eq(" + _idx + ")" ) . addClass ( "active" ) ;
this . tabContainer . children ( ":eq(" + _idx + ")" ) . show ( ) ;
2011-08-10 16:36:31 +02:00
} ,
2014-04-08 19:11:35 +02:00
/ * *
* Activate the tab containing the given widget
*
* @ param { et2 _widget } widget
* @ return { bool } widget was found in a tab
* /
activateTab : function ( widget ) {
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 ;
} ,
2011-08-10 16:36:31 +02:00
getDOMNode : function ( _sender ) {
if ( _sender == this )
{
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 ;
}
2011-08-19 18:00:44 +02:00
} ,
2015-10-26 22:13:34 +01:00
2014-12-05 18:29:41 +01:00
set _tab _height : function ( _height )
{
this . tab _height = _height ;
this . tabContainer . css ( "height" , _height ) ;
} ,
2015-10-26 22:13:34 +01:00
2011-08-19 18:00:44 +02:00
set _height : function ( _value ) {
this . height = _value ;
this . tabContainer . css ( "height" , _value ) ;
2013-05-10 22:39:12 +02:00
} ,
2011-08-10 16:36:31 +02:00
2013-05-10 22:39:12 +02:00
/ * *
* getValue has to return the value of the input widget
* /
getValue : function ( ) {
return this . tabData [ this . selected _index ] . id ;
} ,
/ * *
* Is dirty returns true if the value of the widget has changed since it
* was loaded .
* /
isDirty : function ( ) {
return this . selected _index != this . value ;
} ,
/ * *
* Causes the dirty flag to be reseted .
* /
2014-03-12 18:54:52 +01:00
resetDirty : function ( )
2013-05-10 22:39:12 +02:00
{
this . value = this . selected _index ;
2013-06-26 22:50:10 +02:00
} ,
isValid : function ( messages ) {
return true ;
2014-12-04 17:38:34 +01:00
} ,
2015-10-26 22:13:34 +01:00
2014-12-04 17:38:34 +01:00
resize : function ( _height )
{
if ( _height )
{
2014-12-05 18:29:41 +01:00
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 ( ) ) ;
2014-12-04 17:38:34 +01:00
}
2013-05-10 22:39:12 +02:00
}
2016-02-29 21:40:43 +01:00
} ) ; } ) . call ( this ) ;
2011-08-10 16:36:31 +02:00
et2 _register _widget ( et2 _tabbox , [ "tabbox" ] ) ;