2012-03-07 01:30:47 +01:00
/ * *
2013-02-08 14:23:58 +01:00
* EGroupware eTemplate2 - JS Tree object
2012-03-07 01:30:47 +01:00
*
2013-02-08 14:23:58 +01:00
* @ link http : //community.egroupware.org/egroupware/phpgwapi/js/dhtmlxtree/docsExplorer/dhtmlxtree/
2012-03-07 01:30:47 +01:00
* @ license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @ package etemplate
* @ subpackage api
* @ link http : //www.egroupware.org
* @ author Nathan Gray
2013-02-08 14:23:58 +01:00
* @ author Ralf Becker
2012-03-07 01:30:47 +01:00
* @ copyright Nathan Gray 2011
* @ version $Id$
* /
"use strict" ;
/ * e g w : u s e s
et2 _core _inputWidget ;
2013-02-15 16:52:49 +01:00
/ p h p g w a p i / j s / e g w _ a c t i o n / e g w _ d r a g d r o p _ d h t m l x _ t r e e . j s ;
2014-06-03 01:32:15 +02:00
/ p h p g w a p i / j s / d h t m l x t r e e / c o d e b a s e / d h t m l x c o m m o n . j s ;
2013-02-08 14:23:58 +01:00
// using debugable and fixed source of dhtmltree instead: /phpgwapi/js/dhtmlxtree/js/dhtmlXTree.js;
2014-06-03 01:32:15 +02:00
/ p h p g w a p i / j s / d h t m l x t r e e / s o u r c e s / d h t m l x t r e e . j s ;
/ p h p g w a p i / j s / d h t m l x t r e e / s o u r c e s / e x t / d h t m l x t r e e _ j s o n . j s ;
// /phpgwapi/js/dhtmlxtree/sources/ext/dhtmlxtree_start.js;
2012-03-07 01:30:47 +01:00
* /
2013-04-13 21:00:13 +02:00
/ * *
* Tree widget
2014-01-21 14:34:02 +01:00
*
2013-05-12 15:09:20 +02:00
* For syntax of nodes supplied via sel _optons or autoloading refer to etemplate _widget _tree class .
2014-01-21 14:34:02 +01:00
*
2013-04-13 21:00:13 +02:00
* @ augments et2 _inputWidget
* /
var et2 _tree = et2 _inputWidget . extend (
{
2012-03-07 01:30:47 +01:00
attributes : {
"multiple" : {
"name" : "multiple" ,
"type" : "boolean" ,
"default" : false ,
"description" : "Allow selecting multiple options"
} ,
"select_options" : {
"type" : "any" ,
"name" : "Select options" ,
"default" : { } ,
"description" : "Used to set the tree options."
} ,
2013-02-08 14:23:58 +01:00
"onclick" : {
"description" : "JS code which gets executed when clicks on text of a node"
} ,
"onselect" : {
"name" : "onSelect" ,
2013-10-09 16:35:03 +02:00
"type" : "js" ,
2013-10-10 15:17:07 +02:00
"default" : et2 _no _init ,
2012-03-07 01:30:47 +01:00
"description" : "Javascript executed when user selects a node"
} ,
"oncheck" : {
2013-02-08 14:23:58 +01:00
"name" : "onCheck" ,
2013-10-09 16:35:03 +02:00
"type" : "js" ,
2013-10-10 15:17:07 +02:00
"default" : et2 _no _init ,
2012-03-07 01:30:47 +01:00
"description" : "Javascript executed when user checks a node"
} ,
2013-02-08 14:23:58 +01:00
// onChange event is mapped depending on multiple to onCheck or onSelect
2014-01-21 14:39:27 +01:00
onopenstart : {
"name" : "onOpenStart" ,
"type" : "js" ,
"default" : et2 _no _init ,
"description" : "Javascript function executed when user opens a node: function(_id, _widget, _hasChildren) returning true to allow opening!"
} ,
onopenend : {
"name" : "onOpenEnd" ,
"type" : "js" ,
"default" : et2 _no _init ,
"description" : "Javascript function executed when opening a node is finished: function(_id, _widget, _hasChildren)"
} ,
2012-03-08 00:17:49 +01:00
"image_path" : {
"name" : "Image directory" ,
"type" : "string" ,
"default" : this . egw ( ) . webserverUrl + "/phpgwapi/templates/default/images/dhtmlxtree/" ,
"description" : "Directory for tree structure images"
} ,
2012-03-07 01:30:47 +01:00
"value" : {
"type" : "any" ,
"default" : { }
2013-02-15 16:52:49 +01:00
} ,
"actions" : {
"name" : "Actions array" ,
"type" : "any" ,
"default" : et2 _no _init ,
2013-02-20 13:03:21 +01:00
"description" : "List of egw actions that can be done on the tree. This includes context menu, drag and drop. TODO: Link to action documentation"
} ,
"autoloading" : {
"name" : "Autoloading" ,
"type" : "string" ,
"default" : "" ,
"description" : "JSON URL or menuaction to be called for nodes marked with child=1, but not having children, GET parameter selected contains node-id"
2013-07-02 20:24:32 +02:00
} ,
"std_images" : {
"name" : "Standard images" ,
"type" : "string" ,
"default" : "" ,
2013-08-14 20:01:22 +02:00
"description" : "comma-separated names of icons for a leaf, closed and opend folder (default: leaf.gif,folderClosed.gif,folderOpen.gif), images with extension get loaded from image_path, just 'image' or 'appname/image' are allowed too"
2015-08-10 17:35:44 +02:00
} ,
"multimarking" : {
"name" : "multimarking" ,
"type" : "any" ,
"default" : false ,
"description" : "Allow marking multiple nodes, default is false which means disabled multiselection, true or 'strict' activates it and 'strict' makes it strick to only same level marking"
} ,
2012-03-07 01:30:47 +01:00
} ,
2013-02-08 14:23:58 +01:00
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2014-01-21 14:34:02 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _tree
* /
2012-03-07 01:30:47 +01:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
this . input = null ;
this . div = $j ( document . createElement ( "div" ) ) . addClass ( "dhtmlxTree" ) ;
this . setDOMNode ( this . div [ 0 ] ) ;
} ,
2012-03-08 00:17:49 +01:00
destroy : function ( ) {
2013-03-18 21:52:41 +01:00
if ( this . input )
{
this . input . destructor ( ) ;
}
2012-03-08 00:17:49 +01:00
this . input = null ;
this . _super . apply ( this , arguments ) ;
} ,
2012-03-07 01:30:47 +01:00
/ * *
* Get tree items from the sel _options data array
2014-02-05 09:54:36 +01:00
*
* @ param { object } _attrs
2012-03-07 01:30:47 +01:00
* /
transformAttributes : function ( _attrs ) {
this . _super . apply ( this , arguments ) ;
// If select_options are already known, skip the rest
if ( this . options && this . options . select _options && ! jQuery . isEmptyObject ( this . options . select _options ) )
{
return ;
}
var name _parts = this . id . replace ( /]/g , '' ) . split ( '[' ) ;
// Try to find the options inside the "sel-options" array
if ( this . getArrayMgr ( "sel_options" ) )
{
// Select options tend to be defined once, at the top level, so try that first
var content _options = this . getArrayMgr ( "sel_options" ) . getRoot ( ) . getEntry ( name _parts [ name _parts . length - 1 ] ) ;
// Try again according to ID
if ( ! content _options ) content _options = this . getArrayMgr ( "sel_options" ) . getEntry ( this . id ) ;
2014-07-09 13:18:47 +02:00
if ( _attrs [ "select_options" ] && ! jQuery . isEmptyObject ( _attrs [ "select_options" ] ) && content _options )
2012-03-07 01:30:47 +01:00
{
_attrs [ "select_options" ] = jQuery . extend ( { } , _attrs [ "select_options" ] , content _options ) ;
} else if ( content _options ) {
_attrs [ "select_options" ] = content _options ;
}
}
// Check whether the options entry was found, if not read it from the
// content array.
if ( _attrs [ "select_options" ] == null )
{
// Again, try last name part at top level
var content _options = this . getArrayMgr ( 'content' ) . getRoot ( ) . getEntry ( name _parts [ name _parts . length - 1 ] ) ;
// If that didn't work, check according to ID
_attrs [ "select_options" ] = content _options ? content _options : this . getArrayMgr ( 'content' )
2013-04-13 21:00:13 +02:00
. getEntry ( "options-" + this . id ) ;
2012-03-07 01:30:47 +01:00
}
// Default to an empty object
if ( _attrs [ "select_options" ] == null )
{
_attrs [ "select_options" ] = { } ;
}
} ,
2013-02-08 14:23:58 +01:00
// overwrite default onclick to do nothing, as we install onclick via dhtmlxtree
2013-10-09 16:35:03 +02:00
click : function ( _node ) { } ,
2013-02-08 14:23:58 +01:00
2012-03-07 01:30:47 +01:00
createTree : function ( widget ) {
2013-05-29 21:12:14 +02:00
widget . input = new dhtmlXTreeObject ( {
parent : widget . div [ 0 ] ,
width : '100%' ,
height : '100%' ,
image _path : widget . options . image _path ,
2014-02-05 09:54:36 +01:00
checkbox : widget . options . multiple
2013-05-29 21:12:14 +02:00
} ) ;
2014-07-21 14:57:32 +02:00
// to allow "," in value, eg. folder-names, IF value is specified as array
widget . input . dlmtr = ':}-*(' ;
2013-07-02 20:24:32 +02:00
if ( widget . options . std _images )
{
widget . setImages . apply ( widget , widget . options . std _images . split ( ',' ) ) ;
}
2013-06-03 18:32:45 +02:00
// Add in the callback so we can keep the two in sync
2014-07-21 14:57:32 +02:00
widget . input . AJAX _callback = function ( dxmlObject ) {
2014-07-11 18:49:45 +02:00
widget . _dhtmlxtree _json _callback ( JSON . parse ( dxmlObject . xmlDoc . responseText ) , widget . input . lastLoadedXMLId ) ;
} ;
2013-06-03 18:32:45 +02:00
2013-05-29 21:12:14 +02:00
if ( widget . options . autoloading )
{
var url = widget . options . autoloading ;
2015-07-28 14:47:02 +02:00
//Set escaping mode to utf8, as url in
//autoloading needs to be utf8 encoded.
//For instance item id with umlaut.
widget . input . setEscapingMode ( 'utf8' ) ;
2013-05-29 21:12:14 +02:00
if ( url . charAt ( 0 ) != '/' && url . substr ( 0 , 4 ) != 'http' )
2013-02-20 13:03:21 +01:00
{
2013-05-29 21:12:14 +02:00
url = '/json.php?menuaction=' + url ;
2013-02-20 13:03:21 +01:00
}
2013-05-29 21:12:14 +02:00
this . autoloading _url = url ;
2014-01-21 14:34:02 +01:00
2013-12-18 18:54:28 +01:00
widget . input . setXMLAutoLoading ( egw . link ( url ) ) ;
2013-05-29 21:12:14 +02:00
widget . input . setDataMode ( 'JSON' ) ;
}
2015-08-10 17:35:44 +02:00
if ( widget . options . multimarking )
{
widget . input . enableMultiselection ( ! ! widget . options . multimarking , widget . options . multimarking === 'strict' ) ;
}
2012-03-07 01:30:47 +01:00
} ,
2014-01-21 14:34:02 +01:00
2013-10-09 16:35:03 +02:00
/ * *
* Install event handlers on tree
2014-01-21 14:34:02 +01:00
*
2013-10-09 16:35:03 +02:00
* @ param _name
* @ param _handler
* /
_install _handler : function ( _name , _handler )
{
if ( typeof _handler == 'function' )
{
if ( this . input == null ) this . createTree ( this ) ;
// automatic convert onChange event to oncheck or onSelect depending on multiple is used or not
if ( _name == 'onchange' ) _name = this . options . multiple ? 'oncheck' : 'onselect' ;
var handler = _handler ;
var widget = this ;
this . input . attachEvent ( _name , function ( _id ) {
var args = jQuery . makeArray ( arguments ) ;
// splice in widget as 2. parameter, 1. is new node-id, now 3. is old node id
args . splice ( 1 , 0 , widget ) ;
2014-01-21 14:34:02 +01:00
return handler . apply ( this , args ) ;
2013-10-09 16:35:03 +02:00
} ) ;
}
} ,
2014-01-21 14:34:02 +01:00
2013-10-09 16:35:03 +02:00
set _onchange : function ( _handler ) { this . _install _handler ( 'onchange' , _handler ) ; } ,
set _onclick : function ( _handler ) { this . _install _handler ( 'onclick' , _handler ) ; } ,
set _onselect : function ( _handler ) { this . _install _handler ( 'onselect' , _handler ) ; } ,
2014-01-21 14:34:02 +01:00
set _onopenstart : function ( _handler ) { this . _install _handler ( 'onOpenStart' , _handler ) ; } ,
set _onopenend : function ( _handler ) { this . _install _handler ( 'onOpenEnd' , _handler ) ; } ,
2012-03-07 01:30:47 +01:00
2014-01-21 14:34:02 +01:00
set _select _options : function ( options )
2013-07-02 20:24:32 +02:00
{
2012-03-08 00:17:49 +01:00
var custom _images = false ;
2013-02-15 16:52:49 +01:00
this . options . select _options = options ;
2012-03-08 00:17:49 +01:00
if ( this . input == null )
2012-03-07 01:30:47 +01:00
{
2012-03-08 00:17:49 +01:00
this . createTree ( this ) ;
2012-03-07 01:30:47 +01:00
}
2013-02-11 16:16:23 +01:00
// Structure data for category tree
2014-07-09 13:18:47 +02:00
if ( this . _type == 'tree-cat' )
{
2012-03-08 00:17:49 +01:00
var data = { id : 0 , item : [ ] } ;
2014-07-09 13:18:47 +02:00
var stack = { } ;
for ( var key = 0 ; key < options . length ; key ++ )
2012-03-08 00:17:49 +01:00
{
// See if item has an icon
2014-01-21 14:34:02 +01:00
if ( options [ key ] . data && typeof options [ key ] . data . icon !== 'undefined' && options [ key ] . data . icon )
2012-03-08 00:17:49 +01:00
{
var img = this . egw ( ) . image ( options [ key ] . data . icon , options [ key ] . appname ) ;
2014-01-21 14:34:02 +01:00
if ( img )
2012-03-08 00:17:49 +01:00
{
custom _images = true ;
options [ key ] . im0 = options [ key ] . im1 = options [ key ] . im2 = img ;
}
}
// Item color - not working
if ( options [ key ] . data && typeof options [ key ] . data . color !== 'undefined' && options [ key ] . data . color )
{
2014-02-05 13:23:04 +01:00
options [ key ] . style = options [ key ] . style || "" + "background-color:'" + options [ key ] . data . color + "';" ;
2014-01-21 14:34:02 +01:00
}
2012-03-08 00:17:49 +01:00
// Tooltip
if ( options [ key ] . description && ! options [ key ] . tooltip )
{
options [ key ] . tooltip = options [ key ] . description ;
}
var parent _id = parseInt ( options [ key ] [ 'parent' ] ) ;
2013-02-07 15:36:19 +01:00
if ( isNaN ( parent _id ) ) parent _id = 0 ;
2012-03-08 00:17:49 +01:00
if ( ! stack [ parent _id ] ) stack [ parent _id ] = [ ] ;
stack [ parent _id ] . push ( options [ key ] ) ;
}
if ( custom _images )
{
var path = this . input . iconURL ;
this . input . setIconPath ( "" ) ;
for ( var k = 0 ; k < this . input . imageArray . length ; k ++ )
this . input . imageArray [ k ] = path + this . input . imageArray [ k ] ;
}
var f = function ( data , f ) {
if ( stack [ data . id ] )
{
data . item = stack [ data . id ] ;
for ( var j = 0 ; j < data . item . length ; j ++ )
{
f ( data . item [ j ] , f ) ;
}
}
2013-04-13 21:00:13 +02:00
} ;
2012-03-08 00:17:49 +01:00
f ( data , f ) ;
options = data ;
}
2013-05-12 15:09:20 +02:00
// if no options given, but autoloading url, use that to load initial nodes
2013-05-13 13:59:09 +02:00
if ( typeof options . id == 'undefined' && this . input . XMLsource )
2013-05-12 15:09:20 +02:00
this . input . loadJSON ( this . input . XMLsource ) ;
else
2014-02-05 09:54:36 +01:00
this . input . loadJSONObject ( this . _htmlencode _node ( options ) ) ;
} ,
/ * *
2014-02-05 13:23:04 +01:00
* Regexp used by _htmlencode
2014-02-05 09:54:36 +01:00
* /
_lt _regexp : /</g ,
/ * *
2014-02-05 13:23:04 +01:00
* html encoding of text of node
2014-02-05 09:54:36 +01:00
*
2014-02-05 13:23:04 +01:00
* We only do a minimal html encoding by replacing opening bracket < with & lt ;
* as tree seems not to need more and we dont want to waste time .
*
* @ param { string } _text text to encode
* @ return { string }
2014-02-05 09:54:36 +01:00
* /
2014-02-05 13:23:04 +01:00
_htmlencode : function ( _text )
2014-02-05 09:54:36 +01:00
{
2014-02-05 13:23:04 +01:00
if ( _text && _text . indexOf ( '<' ) >= 0 )
2014-02-05 09:54:36 +01:00
{
2014-02-05 13:23:04 +01:00
_text = _text . replace ( this . _lt _regexp , '<' ) ;
2014-02-05 09:54:36 +01:00
}
2014-02-05 13:23:04 +01:00
return _text ;
} ,
/ * *
* html encoding of text of node incl . all children
*
* @ param { object } _item with required attributes text , id and optional tooltip and item
* @ return { object } encoded node
* /
_htmlencode _node : function ( _item )
{
_item . text = this . _htmlencode ( _item . text ) ;
if ( _item . item && jQuery . isArray ( _item . item ) )
2014-02-05 09:54:36 +01:00
{
for ( var i = 0 ; i < _item . item . length ; ++ i )
{
this . _htmlencode _node ( _item . item [ i ] ) ;
}
}
return _item ;
2012-03-08 00:17:49 +01:00
} ,
set _value : function ( new _value ) {
2013-02-08 14:23:58 +01:00
this . value = this . _oldValue = ( typeof new _value === 'string' && this . options . multiple ? new _value . split ( ',' ) : new _value ) ;
2012-03-08 00:17:49 +01:00
if ( this . input == null ) return ;
2014-01-21 14:34:02 +01:00
2013-02-08 14:23:58 +01:00
if ( this . options . multiple )
2012-03-07 01:30:47 +01:00
{
2013-02-08 14:23:58 +01:00
// Clear all checked
var checked = this . input . getAllChecked ( ) . split ( this . input . dlmtr ) ;
for ( var i = 0 ; i < checked . length ; i ++ )
{
this . input . setCheck ( checked [ i ] , false ) ;
}
2014-01-21 14:34:02 +01:00
2013-02-08 14:23:58 +01:00
// Check selected
for ( var i = 0 ; i < this . value . length ; i ++ )
{
this . input . setCheck ( this . value [ i ] , true ) ;
2015-07-23 12:33:01 +02:00
// autoloading openning needs to be absolutely based on user interaction
// or open flag in folder structure, therefore, We should
// not force it to open the node
if ( ! this . options . autoloading ) this . input . openItem ( this . value [ i ] ) ;
2013-02-08 14:23:58 +01:00
}
2012-03-07 01:30:47 +01:00
}
2013-02-08 14:23:58 +01:00
else
2012-03-08 00:17:49 +01:00
{
2013-02-08 14:23:58 +01:00
this . input . selectItem ( this . value , false ) ; // false = do not trigger onSelect
2013-12-09 17:00:15 +01:00
this . input . focusItem ( this . value ) ;
2013-02-13 16:33:00 +01:00
this . input . openItem ( this . value ) ;
2012-03-08 00:17:49 +01:00
}
} ,
2013-02-15 16:52:49 +01:00
/ * *
2013-07-20 15:45:22 +02:00
* Links actions to tree nodes
2013-02-15 16:52:49 +01:00
*
2014-02-05 09:54:36 +01:00
* @ param { object } actions [ { ID : attributes . . } + ] as for set _actions
2013-02-15 16:52:49 +01:00
* /
2014-01-21 14:34:02 +01:00
_link _actions : function ( actions )
2013-07-02 20:24:32 +02:00
{
2013-02-15 16:52:49 +01:00
// Get the top level element for the tree
2014-01-18 12:21:53 +01:00
// Only look 1 level deep for application object manager
var objectManager = egw _getObjectManager ( this . egw ( ) . appName , true , 1 ) ;
2013-02-15 16:52:49 +01:00
var treeObj = objectManager . getObjectById ( this . id ) ;
if ( treeObj == null ) {
// Add a new container to the object manager which will hold the tree
// objects
2014-04-01 23:32:06 +02:00
treeObj = objectManager . addObject (
new egwActionObject ( this . id , objectManager , null , this . _actionManager , EGW _AO _FLAG _IS _CONTAINER ) ,
null , EGW _AO _FLAG _IS _CONTAINER
) ;
2013-02-15 16:52:49 +01:00
}
// Delete all old objects
treeObj . clear ( ) ;
// Go over the tree parts & add links
2013-07-20 15:45:22 +02:00
var action _links = this . _get _action _links ( actions ) ;
2013-02-15 16:52:49 +01:00
if ( typeof this . options . select _options != 'undefined' )
{
// Iterate over the options (leaves) and add action to each one
var apply _actions = function ( treeObj , option )
{
// Add a new action object to the object manager
2013-08-01 11:54:32 +02:00
var obj = treeObj . addObject ( ( typeof option . id == 'number' ? String ( option . id ) : option . id ) , new dhtmlxtreeItemAOI ( this . input , option . id ) ) ;
2013-07-22 08:34:01 +02:00
obj . updateActionLinks ( action _links ) ;
2013-02-15 16:52:49 +01:00
if ( option . item && option . item . length > 0 )
{
for ( var i = 0 ; i < option . item . length ; i ++ )
{
apply _actions . call ( this , treeObj , option . item [ i ] ) ;
}
}
} ;
apply _actions . call ( this , treeObj , this . options . select _options ) ;
}
} ,
2013-02-13 13:50:18 +01:00
/ * *
* getValue , retrieves the Id of the selected Item
* @ return string or object or null
* /
2012-03-08 00:17:49 +01:00
getValue : function ( ) {
if ( this . input == null ) return null ;
2015-07-24 15:33:27 +02:00
if ( this . options . multiple )
{
var allChecked = this . input . getAllChecked ( ) . split ( this . input . dlmtr ) ;
var allUnchecked = this . input . getAllUnchecked ( ) . split ( this . input . dlmtr ) ;
if ( this . options . autoloading )
{
var res = { } ;
for ( var i = 0 ; i < allChecked . length ; i ++ )
{
res [ allChecked [ i ] ] = { value : true } ;
}
for ( var i = 0 ; i < allUnchecked . length ; i ++ )
{
res [ allUnchecked [ i ] ] = { value : false } ;
}
return res ;
}
else
{
return allChecked ;
}
}
return this . input . getSelectedItemId ( ) ;
2013-02-13 13:50:18 +01:00
} ,
/ * *
* getSelectedLabel , retrieves the Label of the selected Item
* @ return string or null
* /
getSelectedLabel : function ( ) {
if ( this . input == null ) return null ;
if ( this . options . multiple )
{
/ *
var out = [ ] ;
var checked = this . input . getAllChecked ( ) . split ( this . input . dlmtr ) ;
for ( var i = 0 ; i < checked . length ; i ++ )
{
out . push ( this . input . getItemText ( checked [ i ] ) ) ;
}
return out ;
* /
return null ; // not supported yet
}
else
{
return this . input . getSelectedItemText ( ) ;
}
} ,
2013-04-29 16:58:29 +02:00
/ * *
* renameItem , renames an item by id
2014-02-05 09:54:36 +01:00
*
* @ param { string } _id ID of the node
* @ param { string } _newItemId ID of the node
* @ param { string } _label label to set
2013-04-29 16:58:29 +02:00
* /
renameItem : function ( _id , _newItemId , _label ) {
if ( this . input == null ) return null ;
this . input . changeItemId ( _id , _newItemId ) ;
2013-05-06 22:03:14 +02:00
// Update action
// since the action ID has to = this.id, getObjectById() won't work
var treeObj = egw _getAppObjectManager ( ) . getObjectById ( this . id ) ;
for ( var i = 0 ; i < treeObj . children . length ; i ++ )
{
2014-02-12 21:12:02 +01:00
if ( treeObj . children [ i ] . id == _id )
2013-05-06 22:03:14 +02:00
{
2014-02-12 21:12:02 +01:00
treeObj . children [ i ] . id = _newItemId ;
if ( treeObj . children [ i ] . iface ) treeObj . children [ i ] . iface . id = _newItemId ;
break ;
2013-05-06 22:03:14 +02:00
}
}
2014-01-21 14:34:02 +01:00
2014-02-05 13:23:04 +01:00
if ( typeof _label != 'undefined' ) this . setLabel ( _newItemId , _label ) ;
2013-04-29 16:58:29 +02:00
} ,
2013-05-13 16:40:44 +02:00
/ * *
* deleteItem , deletes an item by id
* @ param _id ID of the node
* @ param _selectParent select the parent node true / false
* @ return void
* /
deleteItem : function ( _id , _selectParent ) {
if ( this . input == null ) return null ;
this . input . deleteItem ( _id , _selectParent ) ;
// Update action
// since the action ID has to = this.id, getObjectById() won't work
var treeObj = egw _getAppObjectManager ( ) . getObjectById ( this . id ) ;
2014-06-25 18:47:07 +02:00
for ( var i = 0 ; i < treeObj . children . length ; i ++ )
{
if ( treeObj . children [ i ] . id == _id )
2013-05-13 16:40:44 +02:00
{
2014-06-25 18:47:07 +02:00
treeObj . children . splice ( i , 1 ) ;
2013-05-13 16:40:44 +02:00
}
2014-06-25 18:47:07 +02:00
}
2013-05-13 16:40:44 +02:00
} ,
2013-05-21 10:33:16 +02:00
/ * *
2013-05-27 17:51:18 +02:00
* Updates a leaf of the tree by requesting new information from the server using the
* autoloading attribute .
*
2014-01-11 12:53:23 +01:00
* @ param { string } _id ID of the node
* @ param { Object } [ data ] If provided , the item is refreshed directly with
* the provided data instead of asking the server
2013-05-21 10:33:16 +02:00
* @ return void
* /
2014-01-11 12:53:23 +01:00
refreshItem : function ( _id , data ) {
2013-05-21 10:33:16 +02:00
if ( this . input == null ) return null ;
2013-05-27 17:51:18 +02:00
this . input . deleteChildItems ( _id ) ;
2013-05-21 10:33:16 +02:00
this . input . setDataMode ( 'JSON' ) ;
2013-05-28 17:43:35 +02:00
2013-05-27 17:51:18 +02:00
/ * C a n ' t u s e t h i s , i t d o e s n ' t a l l o w a c a l l b a c k
2013-05-21 10:33:16 +02:00
this . input . refreshItem ( _id ) ;
2013-05-27 17:51:18 +02:00
* /
2013-05-28 17:43:35 +02:00
2013-05-27 17:51:18 +02:00
var self = this ;
2014-01-11 12:53:23 +01:00
if ( typeof data != 'undefined' && data != null )
{
this . input . loadJSONObject ( data ,
function ( ) { self . _dhtmlxtree _json _callback ( data , _id ) ; }
) ;
}
else
{
this . input . loadJSON ( this . egw ( ) . link ( this . autoloading _url , { id : _id } ) ,
2014-07-11 18:49:45 +02:00
function ( dxmlObject ) { self . _dhtmlxtree _json _callback ( JSON . parse ( dxmlObject . xmlDoc . responseText ) , _id ) ; }
2014-01-11 12:53:23 +01:00
) ;
}
2013-05-28 17:43:35 +02:00
} ,
2013-12-09 17:00:15 +01:00
/ * *
* focus the item , and scrolls it into view
*
* @ param _id ID of the node
* @ return void
* /
focusItem : function ( _id ) {
if ( this . input == null ) return null ;
this . input . focusItem ( _id ) ;
} ,
/ * *
* hasChildren
*
* @ param _id ID of the node
* @ return the number of childelements
* /
hasChildren : function ( _id ) {
if ( this . input == null ) return null ;
return this . input . hasChildren ( _id ) ;
} ,
2013-05-28 17:43:35 +02:00
/ * *
* Callback for after using dhtmlxtree ' s AJAX loading
* The tree has visually already been updated at this point , we just need
* to update the internal data .
*
2014-02-05 09:54:36 +01:00
* @ param { object } new _data Fresh data for the tree
* @ param { string } update _option _id optional If provided , only update that node ( and children ) with the
2013-05-28 17:43:35 +02:00
* provided data instead of the whole thing . Allows for partial updates .
* @ return void
* /
_dhtmlxtree _json _callback : function ( new _data , update _option _id ) {
2013-07-02 20:24:32 +02:00
// not sure if it makes sense to try update_option_id, so far I only seen it to be -1
var parent _id = typeof update _option _id != 'undefined' && update _option _id != - 1 ? update _option _id : new _data . id ;
// find root of loaded data to merge it there
var option = this . _find _in _item ( parent _id , this . options . select _options ) ;
// if we found it, merge it
if ( option )
{
jQuery . extend ( option , new _data || { } ) ;
}
else // else store it in root
{
this . options . select _options = new _data ;
}
// Update actions by just re-setting them
this . set _actions ( this . options . actions || { } ) ;
} ,
2014-01-21 14:34:02 +01:00
2013-07-02 20:24:32 +02:00
/ * *
* Recursive search item object for given id
2014-01-21 14:34:02 +01:00
*
2014-02-05 09:54:36 +01:00
* @ param { string } _id
* @ param { object } _item
2013-07-02 20:24:32 +02:00
* @ returns
* /
_find _in _item : function ( _id , _item )
{
if ( _item && _item . id == _id )
2013-05-21 10:33:16 +02:00
{
2013-07-02 20:24:32 +02:00
return _item ;
}
if ( _item && typeof _item . item != 'undefined' )
{
for ( var i = 0 ; i < _item . item . length ; ++ i )
2013-05-28 17:43:35 +02:00
{
2013-07-02 20:24:32 +02:00
var found = this . _find _in _item ( _id , _item . item [ i ] ) ;
if ( found ) return found ;
2013-05-28 17:43:35 +02:00
}
}
2013-07-02 20:24:32 +02:00
return null ;
2013-05-21 10:33:16 +02:00
} ,
2014-02-11 16:56:52 +01:00
/ * *
* Get node data by id
*
* @ param { string } _id id of node
* @ return { object } object with attributes id , im0 - 2 , text , tooltip , ... as set via select _options or autoload url
* /
getNode : function ( _id )
{
return this . _find _in _item ( _id , this . options . select _options ) ;
} ,
2013-02-19 13:58:32 +01:00
/ * *
2014-02-05 13:23:04 +01:00
* Sets label of an item by id
*
2013-02-19 13:58:32 +01:00
* @ param _id ID of the node
* @ param _label label to set
* @ return void
* /
setLabel : function ( _id , _label ) {
if ( this . input == null ) return null ;
2014-02-05 13:23:04 +01:00
this . input . setItemText ( _id , this . _htmlencode ( _label ) ) ;
} ,
/ * *
* Sets a style for an item by id
*
* @ param { string } _id ID of node
* @ param { string } _style style to set
* @ return void
* /
setStyle : function ( _id , _style ) {
if ( this . input == null ) return null ;
this . input . setItemStyle ( _id , _style ) ;
2013-02-19 13:58:32 +01:00
} ,
2013-04-10 14:32:07 +02:00
/ * *
* getLabel , gets the Label of of an item by id
* @ param _id ID of the node
* @ return _label
* /
getLabel : function ( _id ) {
if ( this . input == null ) return null ;
return this . input . getItemText ( _id ) ;
} ,
2013-02-13 13:50:18 +01:00
/ * *
* getSelectedNode , retrieves the full node of the selected Item
* @ return string or null
* /
getSelectedNode : function ( ) {
if ( this . input == null ) return null ;
// no support for multiple selections
// as there is no get Method to return the full selected node, we use this
return this . options . multiple ? null : this . input . _selected [ 0 ] ;
2013-02-19 11:42:36 +01:00
} ,
/ * *
* getTreeNodeOpenItems
2014-01-21 14:34:02 +01:00
*
2014-02-05 09:54:36 +01:00
* @ param { string } _nodeID the nodeID where to start from ( initial node )
* @ param { string } mode the mode to run in : "forced" fakes the initial node openState to be open
* @ return { object } structured array of node ids : array ( message - ids )
2013-02-19 11:42:36 +01:00
* /
getTreeNodeOpenItems : function ( _nodeID , mode ) {
if ( this . input == null ) return null ;
2014-07-29 14:50:39 +02:00
var z = this . input . getSubItems ( _nodeID ) . split ( this . input . dlmtr ) ;
2013-02-19 11:42:36 +01:00
var oS ;
var PoS ;
var rv ;
var returnValue = [ _nodeID ] ;
var modetorun = "none" ;
2013-04-13 21:00:13 +02:00
if ( mode ) { modetorun = mode ; }
PoS = this . input . getOpenState ( _nodeID ) ;
2013-02-19 11:42:36 +01:00
if ( modetorun == "forced" ) PoS = 1 ;
if ( PoS == 1 ) {
for ( var i = 0 ; i < z . length ; i ++ ) {
2013-04-13 21:00:13 +02:00
oS = this . input . getOpenState ( z [ i ] ) ;
2013-02-19 11:42:36 +01:00
//alert(z[i]+' OpenState:'+oS);
2013-04-13 21:00:13 +02:00
if ( oS == - 1 ) { returnValue . push ( z [ i ] ) ; }
if ( oS == 0 ) { returnValue . push ( z [ i ] ) ; }
2013-02-19 11:42:36 +01:00
if ( oS == 1 ) {
//alert("got here")
rv = this . getTreeNodeOpenItems ( z [ i ] ) ;
//returnValue.concat(rv); // not working as expected; the following does
for ( var j = 0 ; j < rv . length ; j ++ ) { returnValue . push ( rv [ j ] ) ; }
2014-01-21 14:34:02 +01:00
}
2013-02-19 11:42:36 +01:00
}
}
//alert(returnValue.join('#,#'));
2013-04-13 21:00:13 +02:00
return returnValue ;
2013-07-01 19:07:07 +02:00
} ,
2014-01-21 14:34:02 +01:00
2013-07-01 19:07:07 +02:00
/ * *
* Fetch user - data stored in specified node under given name
2014-01-21 14:34:02 +01:00
*
2013-07-01 19:07:07 +02:00
* User - data need to be stored in json as follows :
2014-01-21 14:34:02 +01:00
*
2013-07-01 19:07:07 +02:00
* { "id" : "node-id" , "im0" : ... , "userdata" : [ { "name" : "user-name" , "content" : "user-value" } , ... ] }
2014-01-21 14:34:02 +01:00
*
2013-07-01 19:07:07 +02:00
* In above example getUserData ( "node-id" , "user-name" ) will return "user-value"
2014-01-21 14:34:02 +01:00
*
2013-07-01 19:07:07 +02:00
* @ param _nodeId
* @ param _name
* @ returns
* /
getUserData : function ( _nodeId , _name )
{
if ( this . input == null ) return null ;
return this . input . getUserData ( _nodeId , _name ) ;
} ,
2014-01-21 14:34:02 +01:00
2013-07-01 19:07:07 +02:00
/ * *
* Stores / updates user - data in specified node and name
2014-01-21 14:34:02 +01:00
*
2013-07-01 19:07:07 +02:00
* @ param _nodeId
* @ param _name
* @ param _value
* @ returns
* /
setUserData : function ( _nodeId , _name , _value )
{
if ( this . input == null ) return null ;
return this . input . setUserData ( _nodeId , _name , _value ) ;
} ,
2014-01-21 14:34:02 +01:00
2013-07-01 19:07:07 +02:00
/ * *
* Query nodes open state and optinal change it
2014-01-21 14:34:02 +01:00
*
2013-07-01 19:07:07 +02:00
* @ param _id node - id
* @ param _open specify to change true : open , false : close , everything else toggle
* @ returns true if open , false if closed
* /
openItem : function ( _id , _open )
{
if ( this . input == null ) return null ;
var is _open = this . input . getOpenState ( _id ) == 1 ;
2014-01-21 14:34:02 +01:00
2013-07-01 19:07:07 +02:00
if ( typeof _open != 'undefined' && is _open !== _open )
{
if ( is _open )
{
this . input . closeItem ( _id ) ;
}
else
{
this . input . openItem ( _id ) ;
}
}
return is _open ;
2013-07-02 20:24:32 +02:00
} ,
2014-01-21 14:34:02 +01:00
2014-01-21 18:10:48 +01:00
/ * *
* reSelectItem , reselects an item by id
* @ param _id ID of the node
* /
reSelectItem : function ( _id )
{
if ( this . input == null ) return null ;
this . input . selectItem ( _id , false , false ) ;
} ,
2013-07-02 20:24:32 +02:00
/ * *
* Set images for a specific node or all new nodes ( default )
2014-01-21 14:34:02 +01:00
*
2013-07-02 20:24:32 +02:00
* If images contain an extension eg . "leaf.gif" they are asumed to be in image path ( / p h p g w a p i / t e m p l a t e s / d e f a u l t / i m a g e s / d h t m l x t r e e / ) .
* Otherwise they get searched via egw . image ( ) in current app , phpgwapi or can be specified as "app/image" .
2014-01-21 14:34:02 +01:00
*
2014-02-05 09:54:36 +01:00
* @ param { string } _leaf leaf image , default "leaf.gif"
* @ param { string } _closed closed folder image , default "folderClosed.gif"
* @ param { string } _open opened folder image , default "folderOpen.gif"
* @ param { string } _id if not given , standard images for new nodes are set
2013-07-02 20:24:32 +02:00
* /
setImages : function ( _leaf , _closed , _open , _id )
{
var images = [ _leaf || 'leaf.gif' , _closed || 'folderClosed.gif' , _open || 'folderOpen.gif' ] ;
var image _extensions = /\.(gif|png|jpe?g)/i ;
for ( var i = 0 ; i < 3 ; ++ i )
{
var image = images [ i ] ;
if ( ! image . match ( image _extensions ) )
{
2014-03-25 13:23:06 +01:00
images [ i ] = this . _rel _url ( this . egw ( ) . image ( image ) || image ) ;
2013-07-02 20:24:32 +02:00
}
}
if ( typeof _id == 'undefined' )
{
this . input . setStdImages . apply ( this . input , images ) ;
}
else
{
images . unshift ( _id ) ;
this . input . setItemImage2 . apply ( this . input , images ) ;
}
} ,
2014-01-21 14:34:02 +01:00
2014-08-15 13:22:43 +02:00
/ * *
* Set state of node incl . it ' s children
*
* @ param { string } _id id of node
* @ param { boolean | string } _state or "toggle" to toggle state
* /
setSubChecked : function ( _id , _state )
{
if ( _state === "toggle" ) _state = ! this . input . isItemChecked ( _id ) ;
this . input . setSubChecked ( _id , _state ) ;
} ,
2013-07-02 20:24:32 +02:00
/ * *
* Get URL relative to image _path option
2014-01-21 14:34:02 +01:00
*
2013-07-02 20:24:32 +02:00
* Both URL start with EGroupware webserverUrl and image _path gets allways appended to images by tree .
2014-01-21 14:34:02 +01:00
*
2014-02-05 09:54:36 +01:00
* @ param { string } _url
* @ return { string } relativ url
2013-07-02 20:24:32 +02:00
* /
_rel _url : function ( _url )
{
var path _parts = this . options . image _path . split ( this . egw ( ) . webserverUrl ) ;
path _parts = path _parts [ 1 ] . split ( '/' ) ;
var url _parts = _url . split ( this . egw ( ) . webserverUrl ) ;
url _parts = url _parts [ 1 ] . split ( '/' ) ;
2014-01-21 14:34:02 +01:00
2013-07-02 20:24:32 +02:00
for ( var i = 0 ; i < path _parts . length ; ++ i )
{
if ( path _parts [ i ] != url _parts [ i ] )
{
while ( ++ i < path _parts . length ) url _parts . unshift ( '..' ) ;
break ;
}
url _parts . shift ( ) ;
}
return url _parts . join ( '/' ) ;
2012-03-07 01:30:47 +01:00
}
} ) ;
et2 _register _widget ( et2 _tree , [ "tree" , "tree-cat" ] ) ;