2019-07-17 20:08:50 +00:00
/ *
2019-07-17 20:16:19 +00:00
Copyright ( c ) 2009 , Yahoo ! Inc . All rights reserved .
2019-07-17 20:08:50 +00:00
Code licensed under the BSD License :
http : //developer.yahoo.net/yui/license.txt
2019-07-17 20:16:19 +00:00
version : 2.7 . 0
2019-07-17 20:08:50 +00:00
* /
( function ( ) {
2019-07-17 20:16:19 +00:00
var Dom = YAHOO . util . Dom ,
Event = YAHOO . util . Event ,
Lang = YAHOO . lang ,
Widget = YAHOO . widget ;
2019-07-17 20:08:50 +00:00
/ * *
* The treeview widget is a generic tree building tool .
* @ module treeview
* @ title TreeView Widget
* @ requires yahoo , event
2019-07-17 20:16:19 +00:00
* @ optional animation , json
2019-07-17 20:08:50 +00:00
* @ namespace YAHOO . widget
* /
/ * *
* Contains the tree view state data and the root node .
*
* @ class TreeView
* @ uses YAHOO . util . EventProvider
* @ constructor
* @ param { string | HTMLElement } id The id of the element , or the element itself that the tree will be inserted into . Existing markup in this element , if valid , will be used to build the tree
2019-07-17 20:16:19 +00:00
* @ param { Array | object | string } oConfig ( optional ) An array containing the definition of the tree . ( see buildTreeFromObject )
2019-07-17 20:08:50 +00:00
*
* /
YAHOO . widget . TreeView = function ( id , oConfig ) {
if ( id ) { this . init ( id ) ; }
2019-07-17 20:16:19 +00:00
if ( oConfig ) {
if ( ! Lang . isArray ( oConfig ) ) {
oConfig = [ oConfig ] ;
}
this . buildTreeFromObject ( oConfig ) ;
} else if ( Lang . trim ( this . _el . innerHTML ) ) {
this . buildTreeFromMarkup ( id ) ;
}
2019-07-17 20:08:50 +00:00
} ;
var TV = Widget . TreeView ;
TV . prototype = {
/ * *
* The id of tree container element
* @ property id
* @ type String
* /
id : null ,
/ * *
* The host element for this tree
* @ property _el
* @ private
* @ type HTMLelement
* /
_el : null ,
/ * *
* Flat collection of all nodes in this tree . This is a sparse
* array , so the length property can ' t be relied upon for a
* node count for the tree .
* @ property _nodes
* @ type Node [ ]
* @ private
* /
_nodes : null ,
/ * *
* We lock the tree control while waiting for the dynamic loader to return
* @ property locked
* @ type boolean
* /
locked : false ,
/ * *
* The animation to use for expanding children , if any
* @ property _expandAnim
* @ type string
* @ private
* /
_expandAnim : null ,
/ * *
* The animation to use for collapsing children , if any
* @ property _collapseAnim
* @ type string
* @ private
* /
_collapseAnim : null ,
/ * *
* The current number of animations that are executing
* @ property _animCount
* @ type int
* @ private
* /
_animCount : 0 ,
/ * *
* The maximum number of animations to run at one time .
* @ property maxAnim
* @ type int
* /
maxAnim : 2 ,
/ * *
* Whether there is any subscriber to dblClickEvent
* @ property _hasDblClickSubscriber
* @ type boolean
* @ private
* /
_hasDblClickSubscriber : false ,
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
/ * *
* Stores the timer used to check for double clicks
* @ property _dblClickTimer
* @ type window . timer object
* @ private
* /
_dblClickTimer : null ,
2019-07-17 20:16:19 +00:00
/ * *
* A reference to the Node currently having the focus or null if none .
* @ property currentFocus
* @ type YAHOO . widget . Node
* /
currentFocus : null ,
/ * *
* If true , only one Node can be highlighted at a time
* @ property singleNodeHighlight
* @ type boolean
* @ default false
* /
singleNodeHighlight : false ,
/ * *
* A reference to the Node that is currently highlighted .
* It is only meaningful if singleNodeHighlight is enabled
* @ property _currentlyHighlighted
* @ type YAHOO . widget . Node
* @ default null
* @ private
* /
_currentlyHighlighted : null ,
2019-07-17 20:08:50 +00:00
/ * *
* Sets up the animation for expanding children
* @ method setExpandAnim
* @ param { string } type the type of animation ( acceptable values defined
* in YAHOO . widget . TVAnim )
* /
setExpandAnim : function ( type ) {
this . _expandAnim = ( Widget . TVAnim . isValid ( type ) ) ? type : null ;
} ,
/ * *
* Sets up the animation for collapsing children
* @ method setCollapseAnim
* @ param { string } the type of animation ( acceptable values defined in
* YAHOO . widget . TVAnim )
* /
setCollapseAnim : function ( type ) {
this . _collapseAnim = ( Widget . TVAnim . isValid ( type ) ) ? type : null ;
} ,
/ * *
* Perform the expand animation if configured , or just show the
* element if not configured or too many animations are in progress
* @ method animateExpand
* @ param el { HTMLElement } the element to animate
* @ param node { YAHOO . util . Node } the node that was expanded
* @ return { boolean } true if animation could be invoked , false otherwise
* /
animateExpand : function ( el , node ) {
if ( this . _expandAnim && this . _animCount < this . maxAnim ) {
// this.locked = true;
var tree = this ;
var a = Widget . TVAnim . getAnim ( this . _expandAnim , el ,
function ( ) { tree . expandComplete ( node ) ; } ) ;
if ( a ) {
++ this . _animCount ;
this . fireEvent ( "animStart" , {
"node" : node ,
"type" : "expand"
} ) ;
a . animate ( ) ;
}
return true ;
}
return false ;
} ,
/ * *
* Perform the collapse animation if configured , or just show the
* element if not configured or too many animations are in progress
* @ method animateCollapse
* @ param el { HTMLElement } the element to animate
* @ param node { YAHOO . util . Node } the node that was expanded
* @ return { boolean } true if animation could be invoked , false otherwise
* /
animateCollapse : function ( el , node ) {
if ( this . _collapseAnim && this . _animCount < this . maxAnim ) {
// this.locked = true;
var tree = this ;
var a = Widget . TVAnim . getAnim ( this . _collapseAnim , el ,
function ( ) { tree . collapseComplete ( node ) ; } ) ;
if ( a ) {
++ this . _animCount ;
this . fireEvent ( "animStart" , {
"node" : node ,
"type" : "collapse"
} ) ;
a . animate ( ) ;
}
return true ;
}
return false ;
} ,
/ * *
* Function executed when the expand animation completes
* @ method expandComplete
* /
expandComplete : function ( node ) {
-- this . _animCount ;
this . fireEvent ( "animComplete" , {
"node" : node ,
"type" : "expand"
} ) ;
// this.locked = false;
} ,
/ * *
* Function executed when the collapse animation completes
* @ method collapseComplete
* /
collapseComplete : function ( node ) {
-- this . _animCount ;
this . fireEvent ( "animComplete" , {
"node" : node ,
"type" : "collapse"
} ) ;
// this.locked = false;
} ,
/ * *
* Initializes the tree
* @ method init
* @ parm { string | HTMLElement } id the id of the element that will hold the tree
* @ private
* /
init : function ( id ) {
2019-07-17 20:16:19 +00:00
this . _el = Dom . get ( id ) ;
this . id = Dom . generateId ( this . _el , "yui-tv-auto-id-" ) ;
2019-07-17 20:08:50 +00:00
/ * *
* When animation is enabled , this event fires when the animation
* starts
* @ event animStart
* @ type CustomEvent
* @ param { YAHOO . widget . Node } node the node that is expanding / collapsing
* @ parm { String } type the type of animation ( "expand" or "collapse" )
* /
this . createEvent ( "animStart" , this ) ;
/ * *
* When animation is enabled , this event fires when the animation
* completes
* @ event animComplete
* @ type CustomEvent
* @ param { YAHOO . widget . Node } node the node that is expanding / collapsing
* @ parm { String } type the type of animation ( "expand" or "collapse" )
* /
this . createEvent ( "animComplete" , this ) ;
/ * *
* Fires when a node is going to be collapsed . Return false to stop
* the collapse .
* @ event collapse
* @ type CustomEvent
* @ param { YAHOO . widget . Node } node the node that is collapsing
* /
this . createEvent ( "collapse" , this ) ;
/ * *
* Fires after a node is successfully collapsed . This event will not fire
* if the "collapse" event was cancelled .
* @ event collapseComplete
* @ type CustomEvent
* @ param { YAHOO . widget . Node } node the node that was collapsed
* /
this . createEvent ( "collapseComplete" , this ) ;
/ * *
* Fires when a node is going to be expanded . Return false to stop
* the collapse .
* @ event expand
* @ type CustomEvent
* @ param { YAHOO . widget . Node } node the node that is expanding
* /
this . createEvent ( "expand" , this ) ;
/ * *
* Fires after a node is successfully expanded . This event will not fire
* if the "expand" event was cancelled .
* @ event expandComplete
* @ type CustomEvent
* @ param { YAHOO . widget . Node } node the node that was expanded
* /
this . createEvent ( "expandComplete" , this ) ;
/ * *
* Fires when the Enter key is pressed on a node that has the focus
* @ event enterKeyPressed
* @ type CustomEvent
* @ param { YAHOO . widget . Node } node the node that has the focus
* /
this . createEvent ( "enterKeyPressed" , this ) ;
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
/ * *
* Fires when the label in a TextNode or MenuNode or content in an HTMLNode receives a Click .
2019-07-17 20:16:19 +00:00
* The listener may return false to cancel toggling and focusing on the node .
2019-07-17 20:08:50 +00:00
* @ event clickEvent
* @ type CustomEvent
* @ param oArgs . event { HTMLEvent } The event object
* @ param oArgs . node { YAHOO . widget . Node } node the node that was clicked
* /
this . createEvent ( "clickEvent" , this ) ;
2019-07-17 20:16:19 +00:00
/ * *
* Fires when the focus receives the focus , when it changes from a Node
* to another Node or when it is completely lost ( blurred )
* @ event focusChanged
* @ type CustomEvent
* @ param oArgs . oldNode { YAHOO . widget . Node } Node that had the focus or null if none
* @ param oArgs . newNode { YAHOO . widget . Node } Node that receives the focus or null if none
* /
this . createEvent ( 'focusChanged' , this ) ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
/ * *
2019-07-17 20:08:50 +00:00
* Fires when the label in a TextNode or MenuNode or content in an HTMLNode receives a double Click
* @ event dblClickEvent
* @ type CustomEvent
* @ param oArgs . event { HTMLEvent } The event object
* @ param oArgs . node { YAHOO . widget . Node } node the node that was clicked
* /
2019-07-17 20:16:19 +00:00
var self = this ;
2019-07-17 20:08:50 +00:00
this . createEvent ( "dblClickEvent" , {
2019-07-17 20:16:19 +00:00
scope : this ,
onSubscribeCallback : function ( ) {
self . _hasDblClickSubscriber = true ;
}
} ) ;
/ * *
2019-07-17 20:08:50 +00:00
* Custom event that is fired when the text node label is clicked .
* The node clicked is provided as an argument
*
* @ event labelClick
* @ type CustomEvent
* @ param { YAHOO . widget . Node } node the node clicked
2019-07-17 20:16:19 +00:00
* @ deprecated use clickEvent or dblClickEvent
2019-07-17 20:08:50 +00:00
* /
2019-07-17 20:16:19 +00:00
this . createEvent ( "labelClick" , this ) ;
/ * *
* Custom event fired when the highlight of a node changes .
* The node that triggered the change is provided as an argument :
* The status of the highlight can be checked in
* < a href = "YAHOO.widget.Node.html#property_highlightState" > nodeRef . highlightState < / a > .
* Depending on < a href = "YAHOO.widget.Node.html#property_propagateHighlight" > nodeRef . propagateHighlight < / a > , o t h e r n o d e s m i g h t h a v e c h a n g e d
* @ event highlightEvent
* @ type CustomEvent
* @ param node { YAHOO . widget . Node } the node that started the change in highlighting state
* /
this . createEvent ( "highlightEvent" , this ) ;
2019-07-17 20:08:50 +00:00
this . _nodes = [ ] ;
// store a global reference
TV . trees [ this . id ] = this ;
// Set up the root node
this . root = new Widget . RootNode ( this ) ;
var LW = Widget . LogWriter ;
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
// YAHOO.util.Event.onContentReady(this.id, this.handleAvailable, this, true);
// YAHOO.util.Event.on(this.id, "click", this.handleClick, this, true);
} ,
//handleAvailable: function() {
//var Event = YAHOO.util.Event;
//Event.on(this.id,
//},
/ * *
2019-07-17 20:16:19 +00:00
* Builds the TreeView from an object .
* This is the method called by the constructor to build the tree when it has a second argument .
* A tree can be described by an array of objects , each object corresponding to a node .
* Node descriptions may contain values for any property of a node plus the following extra properties : < ul >
* < li > type : can be one of the following : < ul >
* < li > A shortname for a node type ( < code > 'text' , 'menu' , 'html' < / c o d e > ) < / l i >
* < li > The name of a Node class under YAHOO . widget ( < code > 'TextNode' , 'MenuNode' , 'DateNode' < / c o d e > , e t c ) < / l i >
* < li > a reference to an actual class : < code > YAHOO . widget . DateNode < / c o d e > < / l i > < / u l > < / l i >
* < li > children : an array containing further node definitions < / l i > < / u l >
2019-07-17 20:08:50 +00:00
* @ method buildTreeFromObject
* @ param oConfig { Array } array containing a full description of the tree
*
* /
2019-07-17 20:16:19 +00:00
buildTreeFromObject : function ( oConfig ) {
var build = function ( parent , oConfig ) {
var i , item , node , children , type , NodeType , ThisType ;
for ( i = 0 ; i < oConfig . length ; i ++ ) {
item = oConfig [ i ] ;
if ( Lang . isString ( item ) ) {
node = new Widget . TextNode ( item , parent ) ;
} else if ( Lang . isObject ( item ) ) {
children = item . children ;
delete item . children ;
type = item . type || 'text' ;
delete item . type ;
switch ( Lang . isString ( type ) && type . toLowerCase ( ) ) {
case 'text' :
node = new Widget . TextNode ( item , parent ) ;
break ;
case 'menu' :
node = new Widget . MenuNode ( item , parent ) ;
break ;
case 'html' :
node = new Widget . HTMLNode ( item , parent ) ;
break ;
default :
if ( Lang . isString ( type ) ) {
NodeType = Widget [ type ] ;
} else {
NodeType = type ;
}
if ( Lang . isObject ( NodeType ) ) {
for ( ThisType = NodeType ; ThisType && ThisType !== Widget . Node ; ThisType = ThisType . superclass . constructor ) { }
if ( ThisType ) {
node = new NodeType ( item , parent ) ;
} else {
}
} else {
}
}
if ( children ) {
build ( node , children ) ;
}
} else {
}
}
} ;
build ( this . root , oConfig ) ;
} ,
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Builds the TreeView from existing markup . Markup should consist of & lt ; UL & gt ; or & lt ; OL & gt ; elements containing & lt ; LI & gt ; elements .
* Each & lt ; LI & gt ; can have one element used as label and a second optional element which is to be a & lt ; UL & gt ; or & lt ; OL & gt ;
* containing nested nodes .
* Depending on what the first element of the & lt ; LI & gt ; element is , the following Nodes will be created : < ul >
* < li > plain text : a regular TextNode < / l i >
* < li > anchor & lt ; A & gt ; : a TextNode with its < code > href < / c o d e > a n d < c o d e > t a r g e t < / c o d e > t a k e n f r o m t h e a n c h o r < / l i >
* < li > anything else : an HTMLNode < / l i > < / u l >
2019-07-17 20:08:50 +00:00
* Only the first outermost ( un - ) ordered list in the markup and its children will be parsed .
2019-07-17 20:16:19 +00:00
* Nodes will be collapsed unless an & lt ; LI & gt ; tag has a className called 'expanded' .
* All other className attributes will be copied over to the Node className property .
* If the & lt ; LI & gt ; element contains an attribute called < code > yuiConfig < / c o d e > , i t s c o n t e n t s s h o u l d b e a J S O N - e n c o d e d o b j e c t
* as the one used in method < a href = "#method_buildTreeFromObject" > buildTreeFromObject < / a > .
2019-07-17 20:08:50 +00:00
* @ method buildTreeFromMarkup
2019-07-17 20:16:19 +00:00
* @ param id { string | HTMLElement } The id of the element that contains the markup or a reference to it .
* /
buildTreeFromMarkup : function ( id ) {
var build = function ( markup ) {
var el , child , branch = [ ] , config = { } , label , yuiConfig ;
// Dom's getFirstChild and getNextSibling skip over text elements
for ( el = Dom . getFirstChild ( markup ) ; el ; el = Dom . getNextSibling ( el ) ) {
switch ( el . tagName . toUpperCase ( ) ) {
case 'LI' :
label = '' ;
config = {
expanded : Dom . hasClass ( el , 'expanded' ) ,
title : el . title || el . alt || null ,
className : Lang . trim ( el . className . replace ( /\bexpanded\b/ , '' ) ) || null
} ;
// I cannot skip over text elements here because I want them for labels
child = el . firstChild ;
if ( child . nodeType == 3 ) {
// nodes with only whitespace, tabs and new lines don't count, they are probably just formatting.
label = Lang . trim ( child . nodeValue . replace ( /[\n\t\r]*/g , '' ) ) ;
if ( label ) {
config . type = 'text' ;
config . label = label ;
} else {
child = Dom . getNextSibling ( child ) ;
}
}
if ( ! label ) {
if ( child . tagName . toUpperCase ( ) == 'A' ) {
config . type = 'text' ;
config . label = child . innerHTML ;
config . href = child . href ;
config . target = child . target ;
config . title = child . title || child . alt || config . title ;
} else {
config . type = 'html' ;
var d = document . createElement ( 'div' ) ;
d . appendChild ( child . cloneNode ( true ) ) ;
config . html = d . innerHTML ;
config . hasIcon = true ;
}
}
// see if after the label it has a further list which will become children of this node.
child = Dom . getNextSibling ( child ) ;
switch ( child && child . tagName . toUpperCase ( ) ) {
case 'UL' :
case 'OL' :
config . children = build ( child ) ;
break ;
}
// if there are further elements or text, it will be ignored.
if ( YAHOO . lang . JSON ) {
yuiConfig = el . getAttribute ( 'yuiConfig' ) ;
if ( yuiConfig ) {
yuiConfig = YAHOO . lang . JSON . parse ( yuiConfig ) ;
config = YAHOO . lang . merge ( config , yuiConfig ) ;
}
}
branch . push ( config ) ;
break ;
case 'UL' :
case 'OL' :
config = {
type : 'text' ,
label : '' ,
children : build ( child )
} ;
branch . push ( config ) ;
break ;
}
}
return branch ;
} ;
var markup = Dom . getChildrenBy ( Dom . get ( id ) , function ( el ) {
var tag = el . tagName . toUpperCase ( ) ;
return tag == 'UL' || tag == 'OL' ;
} ) ;
if ( markup . length ) {
this . buildTreeFromObject ( build ( markup [ 0 ] ) ) ;
} else {
}
} ,
/ * *
* Returns the TD element where the event has occurred
* @ method _getEventTargetTdEl
* @ private
* /
_getEventTargetTdEl : function ( ev ) {
var target = Event . getTarget ( ev ) ;
// go up looking for a TD with a className with a ygtv prefix
while ( target && ! ( target . tagName . toUpperCase ( ) == 'TD' && Dom . hasClass ( target . parentNode , 'ygtvrow' ) ) ) {
target = Dom . getAncestorByTagName ( target , 'td' ) ;
}
if ( Lang . isNull ( target ) ) { return null ; }
// If it is a spacer cell, do nothing
if ( /\bygtv(blank)?depthcell/ . test ( target . className ) ) { return null ; }
// If it has an id, search for the node number and see if it belongs to a node in this tree.
if ( target . id ) {
var m = target . id . match ( /\bygtv([^\d]*)(.*)/ ) ;
if ( m && m [ 2 ] && this . _nodes [ m [ 2 ] ] ) {
return target ;
}
}
return null ;
} ,
/ * *
* Event listener for click events
* @ method _onClickEvent
* @ private
* /
_onClickEvent : function ( ev ) {
var self = this ,
td = this . _getEventTargetTdEl ( ev ) ,
node ,
target ,
toggle = function ( ) {
node . toggle ( ) ;
node . focus ( ) ;
try {
Event . preventDefault ( ev ) ;
} catch ( e ) {
// @TODO
// For some reason IE8 is providing an event object with
// most of the fields missing, but only when clicking on
// the node's label, and only when working with inline
// editing. This generates a "Member not found" error
// in that browser. Determine if this is a browser
// bug, or a problem with this code. Already checked to
// see if the problem has to do with access the event
// in the outer scope, and that isn't the problem.
// Maybe the markup for inline editing is broken.
}
} ;
if ( ! td ) {
return ;
}
node = this . getNodeByElement ( td ) ;
if ( ! node ) {
return ;
}
// exception to handle deprecated event labelClick
// @TODO take another look at this deprecation. It is common for people to
// only be interested in the label click, so why make them have to test
// the node type to figure out whether the click was on the label?
target = Event . getTarget ( ev ) ;
if ( Dom . hasClass ( target , node . labelStyle ) || Dom . getAncestorByClassName ( target , node . labelStyle ) ) {
this . fireEvent ( 'labelClick' , node ) ;
}
// If it is a toggle cell, toggle
if ( /\bygtv[tl][mp]h?h?/ . test ( td . className ) ) {
toggle ( ) ;
} else {
if ( this . _dblClickTimer ) {
window . clearTimeout ( this . _dblClickTimer ) ;
this . _dblClickTimer = null ;
} else {
if ( this . _hasDblClickSubscriber ) {
this . _dblClickTimer = window . setTimeout ( function ( ) {
self . _dblClickTimer = null ;
if ( self . fireEvent ( 'clickEvent' , { event : ev , node : node } ) !== false ) {
toggle ( ) ;
}
} , 200 ) ;
} else {
if ( self . fireEvent ( 'clickEvent' , { event : ev , node : node } ) !== false ) {
toggle ( ) ;
}
}
}
}
} ,
/ * *
* Event listener for double - click events
* @ method _onDblClickEvent
* @ private
* /
_onDblClickEvent : function ( ev ) {
if ( ! this . _hasDblClickSubscriber ) { return ; }
var td = this . _getEventTargetTdEl ( ev ) ;
if ( ! td ) { return ; }
if ( ! ( /\bygtv[tl][mp]h?h?/ . test ( td . className ) ) ) {
this . fireEvent ( 'dblClickEvent' , { event : ev , node : this . getNodeByElement ( td ) } ) ;
if ( this . _dblClickTimer ) {
window . clearTimeout ( this . _dblClickTimer ) ;
this . _dblClickTimer = null ;
}
}
} ,
/ * *
* Event listener for mouse over events
* @ method _onMouseOverEvent
* @ private
* /
_onMouseOverEvent : function ( ev ) {
var target ;
if ( ( target = this . _getEventTargetTdEl ( ev ) ) && ( target = this . getNodeByElement ( target ) ) && ( target = target . getToggleEl ( ) ) ) {
target . className = target . className . replace ( /\bygtv([lt])([mp])\b/gi , 'ygtv$1$2h' ) ;
}
} ,
/ * *
* Event listener for mouse out events
* @ method _onMouseOutEvent
* @ private
* /
_onMouseOutEvent : function ( ev ) {
var target ;
if ( ( target = this . _getEventTargetTdEl ( ev ) ) && ( target = this . getNodeByElement ( target ) ) && ( target = target . getToggleEl ( ) ) ) {
target . className = target . className . replace ( /\bygtv([lt])([mp])h\b/gi , 'ygtv$1$2' ) ;
}
} ,
/ * *
* Event listener for key down events
* @ method _onKeyDownEvent
* @ private
* /
_onKeyDownEvent : function ( ev ) {
var target = Event . getTarget ( ev ) ,
node = this . getNodeByElement ( target ) ,
newNode = node ,
KEY = YAHOO . util . KeyListener . KEY ;
switch ( ev . keyCode ) {
case KEY . UP :
do {
if ( newNode . previousSibling ) {
newNode = newNode . previousSibling ;
} else {
newNode = newNode . parent ;
}
} while ( newNode && ! newNode . _canHaveFocus ( ) ) ;
if ( newNode ) { newNode . focus ( ) ; }
Event . preventDefault ( ev ) ;
break ;
case KEY . DOWN :
do {
if ( newNode . nextSibling ) {
newNode = newNode . nextSibling ;
} else {
newNode . expand ( ) ;
newNode = ( newNode . children . length || null ) && newNode . children [ 0 ] ;
}
} while ( newNode && ! newNode . _canHaveFocus ) ;
if ( newNode ) { newNode . focus ( ) ; }
Event . preventDefault ( ev ) ;
break ;
case KEY . LEFT :
do {
if ( newNode . parent ) {
newNode = newNode . parent ;
} else {
newNode = newNode . previousSibling ;
}
} while ( newNode && ! newNode . _canHaveFocus ( ) ) ;
if ( newNode ) { newNode . focus ( ) ; }
Event . preventDefault ( ev ) ;
break ;
case KEY . RIGHT :
do {
newNode . expand ( ) ;
if ( newNode . children . length ) {
newNode = newNode . children [ 0 ] ;
} else {
newNode = newNode . nextSibling ;
}
} while ( newNode && ! newNode . _canHaveFocus ( ) ) ;
if ( newNode ) { newNode . focus ( ) ; }
Event . preventDefault ( ev ) ;
break ;
case KEY . ENTER :
if ( node . href ) {
if ( node . target ) {
window . open ( node . href , node . target ) ;
} else {
window . location ( node . href ) ;
}
} else {
node . toggle ( ) ;
}
this . fireEvent ( 'enterKeyPressed' , node ) ;
Event . preventDefault ( ev ) ;
break ;
case KEY . HOME :
newNode = this . getRoot ( ) ;
if ( newNode . children . length ) { newNode = newNode . children [ 0 ] ; }
if ( newNode . _canHaveFocus ( ) ) { newNode . focus ( ) ; }
Event . preventDefault ( ev ) ;
break ;
case KEY . END :
newNode = newNode . parent . children ;
newNode = newNode [ newNode . length - 1 ] ;
if ( newNode . _canHaveFocus ( ) ) { newNode . focus ( ) ; }
Event . preventDefault ( ev ) ;
break ;
// case KEY.PAGE_UP:
// break;
// case KEY.PAGE_DOWN:
// break;
case 107 : // plus key
if ( ev . shiftKey ) {
node . parent . expandAll ( ) ;
} else {
node . expand ( ) ;
}
break ;
case 109 : // minus key
if ( ev . shiftKey ) {
node . parent . collapseAll ( ) ;
} else {
node . collapse ( ) ;
}
break ;
default :
break ;
}
} ,
2019-07-17 20:08:50 +00:00
/ * *
* Renders the tree boilerplate and visible nodes
* @ method render
* /
render : function ( ) {
2019-07-17 20:16:19 +00:00
var html = this . root . getHtml ( ) ,
el = this . getEl ( ) ;
el . innerHTML = html ;
if ( ! this . _hasEvents ) {
Event . on ( el , 'click' , this . _onClickEvent , this , true ) ;
Event . on ( el , 'dblclick' , this . _onDblClickEvent , this , true ) ;
Event . on ( el , 'mouseover' , this . _onMouseOverEvent , this , true ) ;
Event . on ( el , 'mouseout' , this . _onMouseOutEvent , this , true ) ;
Event . on ( el , 'keydown' , this . _onKeyDownEvent , this , true ) ;
}
this . _hasEvents = true ;
} ,
2019-07-17 20:08:50 +00:00
/ * *
* Returns the tree ' s host element
* @ method getEl
* @ return { HTMLElement } the host element
* /
getEl : function ( ) {
if ( ! this . _el ) {
this . _el = Dom . get ( this . id ) ;
}
return this . _el ;
} ,
/ * *
* Nodes register themselves with the tree instance when they are created .
* @ method regNode
* @ param node { Node } the node to register
* @ private
* /
regNode : function ( node ) {
this . _nodes [ node . index ] = node ;
} ,
/ * *
* Returns the root node of this tree
* @ method getRoot
* @ return { Node } the root node
* /
getRoot : function ( ) {
return this . root ;
} ,
/ * *
* Configures this tree to dynamically load all child data
* @ method setDynamicLoad
* @ param { function } fnDataLoader the function that will be called to get the data
* @ param iconMode { int } configures the icon that is displayed when a dynamic
* load node is expanded the first time without children . By default , the
* "collapse" icon will be used . If set to 1 , the leaf node icon will be
* displayed .
* /
setDynamicLoad : function ( fnDataLoader , iconMode ) {
this . root . setDynamicLoad ( fnDataLoader , iconMode ) ;
} ,
/ * *
* Expands all child nodes . Note : this conflicts with the "multiExpand"
* node property . If expand all is called in a tree with nodes that
* do not allow multiple siblings to be displayed , only the last sibling
* will be expanded .
* @ method expandAll
* /
expandAll : function ( ) {
if ( ! this . locked ) {
this . root . expandAll ( ) ;
}
} ,
/ * *
* Collapses all expanded child nodes in the entire tree .
* @ method collapseAll
* /
collapseAll : function ( ) {
if ( ! this . locked ) {
this . root . collapseAll ( ) ;
}
} ,
/ * *
* Returns a node in the tree that has the specified index ( this index
* is created internally , so this function probably will only be used
* in html generated for a given node . )
* @ method getNodeByIndex
* @ param { int } nodeIndex the index of the node wanted
* @ return { Node } the node with index = nodeIndex , null if no match
* /
getNodeByIndex : function ( nodeIndex ) {
var n = this . _nodes [ nodeIndex ] ;
return ( n ) ? n : null ;
} ,
/ * *
* Returns a node that has a matching property and value in the data
* object that was passed into its constructor .
* @ method getNodeByProperty
* @ param { object } property the property to search ( usually a string )
* @ param { object } value the value we want to find ( usuall an int or string )
* @ return { Node } the matching node , null if no match
* /
getNodeByProperty : function ( property , value ) {
for ( var i in this . _nodes ) {
2019-07-17 20:16:19 +00:00
if ( this . _nodes . hasOwnProperty ( i ) ) {
var n = this . _nodes [ i ] ;
if ( ( property in n && n [ property ] == value ) || ( n . data && value == n . data [ property ] ) ) {
return n ;
}
}
2019-07-17 20:08:50 +00:00
}
return null ;
} ,
/ * *
* Returns a collection of nodes that have a matching property
* and value in the data object that was passed into its constructor .
* @ method getNodesByProperty
* @ param { object } property the property to search ( usually a string )
* @ param { object } value the value we want to find ( usuall an int or string )
* @ return { Array } the matching collection of nodes , null if no match
* /
getNodesByProperty : function ( property , value ) {
var values = [ ] ;
for ( var i in this . _nodes ) {
2019-07-17 20:16:19 +00:00
if ( this . _nodes . hasOwnProperty ( i ) ) {
var n = this . _nodes [ i ] ;
if ( ( property in n && n [ property ] == value ) || ( n . data && value == n . data [ property ] ) ) {
values . push ( n ) ;
}
}
2019-07-17 20:08:50 +00:00
}
return ( values . length ) ? values : null ;
} ,
/ * *
* Returns the treeview node reference for an anscestor element
* of the node , or null if it is not contained within any node
* in this tree .
* @ method getNodeByElement
* @ param { HTMLElement } the element to test
* @ return { YAHOO . widget . Node } a node reference or null
* /
getNodeByElement : function ( el ) {
var p = el , m , re = /ygtv([^\d]*)(.*)/ ;
do {
if ( p && p . id ) {
m = p . id . match ( re ) ;
if ( m && m [ 2 ] ) {
return this . getNodeByIndex ( m [ 2 ] ) ;
}
}
p = p . parentNode ;
if ( ! p || ! p . tagName ) {
break ;
}
}
while ( p . id !== this . id && p . tagName . toLowerCase ( ) !== "body" ) ;
return null ;
} ,
/ * *
* Removes the node and its children , and optionally refreshes the
* branch of the tree that was affected .
* @ method removeNode
* @ param { Node } The node to remove
* @ param { boolean } autoRefresh automatically refreshes branch if true
* @ return { boolean } False is there was a problem , true otherwise .
* /
removeNode : function ( node , autoRefresh ) {
// Don't delete the root node
if ( node . isRoot ( ) ) {
return false ;
}
// Get the branch that we may need to refresh
var p = node . parent ;
if ( p . parent ) {
p = p . parent ;
}
// Delete the node and its children
this . _deleteNode ( node ) ;
// Refresh the parent of the parent
if ( autoRefresh && p && p . childrenRendered ) {
p . refresh ( ) ;
}
return true ;
} ,
/ * *
* wait until the animation is complete before deleting
* to avoid javascript errors
* @ method _removeChildren _animComplete
* @ param o the custom event payload
* @ private
* /
_removeChildren _animComplete : function ( o ) {
this . unsubscribe ( this . _removeChildren _animComplete ) ;
this . removeChildren ( o . node ) ;
} ,
/ * *
* Deletes this nodes child collection , recursively . Also collapses
* the node , and resets the dynamic load flag . The primary use for
* this method is to purge a node and allow it to fetch its data
* dynamically again .
* @ method removeChildren
* @ param { Node } node the node to purge
* /
removeChildren : function ( node ) {
if ( node . expanded ) {
// wait until the animation is complete before deleting to
// avoid javascript errors
if ( this . _collapseAnim ) {
this . subscribe ( "animComplete" ,
this . _removeChildren _animComplete , this , true ) ;
Widget . Node . prototype . collapse . call ( node ) ;
return ;
}
node . collapse ( ) ;
}
while ( node . children . length ) {
this . _deleteNode ( node . children [ 0 ] ) ;
}
if ( node . isRoot ( ) ) {
Widget . Node . prototype . expand . call ( node ) ;
}
node . childrenRendered = false ;
node . dynamicLoadComplete = false ;
node . updateIcon ( ) ;
} ,
/ * *
* Deletes the node and recurses children
* @ method _deleteNode
* @ private
* /
_deleteNode : function ( node ) {
// Remove all the child nodes first
this . removeChildren ( node ) ;
// Remove the node from the tree
this . popNode ( node ) ;
} ,
/ * *
* Removes the node from the tree , preserving the child collection
* to make it possible to insert the branch into another part of the
* tree , or another tree .
* @ method popNode
* @ param { Node } the node to remove
* /
popNode : function ( node ) {
var p = node . parent ;
// Update the parent's collection of children
var a = [ ] ;
for ( var i = 0 , len = p . children . length ; i < len ; ++ i ) {
if ( p . children [ i ] != node ) {
a [ a . length ] = p . children [ i ] ;
}
}
p . children = a ;
// reset the childrenRendered flag for the parent
p . childrenRendered = false ;
// Update the sibling relationship
if ( node . previousSibling ) {
node . previousSibling . nextSibling = node . nextSibling ;
}
if ( node . nextSibling ) {
node . nextSibling . previousSibling = node . previousSibling ;
}
node . parent = null ;
node . previousSibling = null ;
node . nextSibling = null ;
node . tree = null ;
// Update the tree's node collection
delete this . _nodes [ node . index ] ;
} ,
2019-07-17 20:16:19 +00:00
/ * *
* Nulls out the entire TreeView instance and related objects , removes attached
* event listeners , and clears out DOM elements inside the container . After
* calling this method , the instance reference should be expliclitly nulled by
* implementer , as in myDataTable = null . Use with caution !
*
* @ method destroy
* /
destroy : function ( ) {
// Since the label editor can be separated from the main TreeView control
// the destroy method for it might not be there.
if ( this . _destroyEditor ) { this . _destroyEditor ( ) ; }
var el = this . getEl ( ) ;
Event . removeListener ( el , 'click' ) ;
Event . removeListener ( el , 'dblclick' ) ;
Event . removeListener ( el , 'mouseover' ) ;
Event . removeListener ( el , 'mouseout' ) ;
Event . removeListener ( el , 'keydown' ) ;
for ( var i = 0 ; i < this . _nodes . length ; i ++ ) {
var node = this . _nodes [ i ] ;
if ( node && node . destroy ) { node . destroy ( ) ; }
}
el . innerHTML = '' ;
this . _hasEvents = false ;
} ,
2019-07-17 20:08:50 +00:00
/ * *
* TreeView instance toString
* @ method toString
* @ return { string } string representation of the tree
* /
toString : function ( ) {
return "TreeView " + this . id ;
} ,
/ * *
* Count of nodes in tree
* @ method getNodeCount
* @ return { int } number of nodes in the tree
* /
getNodeCount : function ( ) {
return this . getRoot ( ) . getNodeCount ( ) ;
} ,
/ * *
* Returns an object which could be used to rebuild the tree .
* It can be passed to the tree constructor to reproduce the same tree .
* It will return false if any node loads dynamically , regardless of whether it is loaded or not .
* @ method getTreeDefinition
* @ return { Object | false } definition of the tree or false if any node is defined as dynamic
* /
getTreeDefinition : function ( ) {
return this . getRoot ( ) . getNodeDefinition ( ) ;
} ,
/ * *
* Abstract method that is executed when a node is expanded
* @ method onExpand
* @ param node { Node } the node that was expanded
* @ deprecated use treeobj . subscribe ( "expand" ) instead
* /
onExpand : function ( node ) { } ,
/ * *
* Abstract method that is executed when a node is collapsed .
* @ method onCollapse
* @ param node { Node } the node that was collapsed .
* @ deprecated use treeobj . subscribe ( "collapse" ) instead
* /
2019-07-17 20:16:19 +00:00
onCollapse : function ( node ) { } ,
/ * *
* Sets the value of a property for all loaded nodes in the tree .
* @ method setNodesProperty
* @ param name { string } Name of the property to be set
* @ param value { any } value to be set
* @ param refresh { boolean } if present and true , it does a refresh
* /
setNodesProperty : function ( name , value , refresh ) {
this . root . setNodesProperty ( name , value ) ;
if ( refresh ) {
this . root . refresh ( ) ;
}
} ,
/ * *
* Event listener to toggle node highlight .
* Can be assigned as listener to clickEvent , dblClickEvent and enterKeyPressed .
* It returns false to prevent the default action .
* @ method onEventToggleHighlight
* @ param oArgs { any } it takes the arguments of any of the events mentioned above
* @ return { false } Always cancels the default action for the event
* /
onEventToggleHighlight : function ( oArgs ) {
var node ;
if ( 'node' in oArgs && oArgs . node instanceof Widget . Node ) {
node = oArgs . node ;
} else if ( oArgs instanceof Widget . Node ) {
node = oArgs ;
} else {
return false ;
}
node . toggleHighlight ( ) ;
return false ;
}
2019-07-17 20:08:50 +00:00
} ;
/* Backwards compatibility aliases */
var PROT = TV . prototype ;
/ * *
* Renders the tree boilerplate and visible nodes .
* Alias for render
* @ method draw
* @ deprecated Use render instead
* /
PROT . draw = PROT . render ;
/* end backwards compatibility aliases */
YAHOO . augment ( TV , YAHOO . util . EventProvider ) ;
/ * *
* Running count of all nodes created in all trees . This is
* used to provide unique identifies for all nodes . Deleting
* nodes does not change the nodeCount .
* @ property YAHOO . widget . TreeView . nodeCount
* @ type int
* @ static
* /
TV . nodeCount = 0 ;
/ * *
* Global cache of tree instances
* @ property YAHOO . widget . TreeView . trees
* @ type Array
* @ static
* @ private
* /
TV . trees = [ ] ;
/ * *
* Global method for getting a tree by its id . Used in the generated
* tree html .
* @ method YAHOO . widget . TreeView . getTree
* @ param treeId { String } the id of the tree instance
* @ return { TreeView } the tree instance requested , null if not found .
* @ static
* /
TV . getTree = function ( treeId ) {
var t = TV . trees [ treeId ] ;
return ( t ) ? t : null ;
} ;
/ * *
* Global method for getting a node by its id . Used in the generated
* tree html .
* @ method YAHOO . widget . TreeView . getNode
* @ param treeId { String } the id of the tree instance
* @ param nodeIndex { String } the index of the node to return
* @ return { Node } the node instance requested , null if not found
* @ static
* /
TV . getNode = function ( treeId , nodeIndex ) {
var t = TV . getTree ( treeId ) ;
return ( t ) ? t . getNodeByIndex ( nodeIndex ) : null ;
} ;
/ * *
* Class name assigned to elements that have the focus
*
* @ property TreeView . FOCUS _CLASS _NAME
* @ type String
* @ static
* @ final
* @ default "ygtvfocus"
2019-07-17 20:16:19 +00:00
* /
2019-07-17 20:08:50 +00:00
TV . FOCUS _CLASS _NAME = 'ygtvfocus' ;
/ * *
* Attempts to preload the images defined in the styles used to draw the tree by
* rendering off - screen elements that use the styles .
* @ method YAHOO . widget . TreeView . preload
* @ param { string } prefix the prefix to use to generate the names of the
* images to preload , default is ygtv
* @ static
* /
TV . preload = function ( e , prefix ) {
prefix = prefix || "ygtv" ;
var styles = [ "tn" , "tm" , "tmh" , "tp" , "tph" , "ln" , "lm" , "lmh" , "lp" , "lph" , "loading" ] ;
// var styles = ["tp"];
var sb = [ ] ;
// save the first one for the outer container
for ( var i = 1 ; i < styles . length ; i = i + 1 ) {
sb [ sb . length ] = '<span class="' + prefix + styles [ i ] + '"> </span>' ;
}
var f = document . createElement ( "div" ) ;
var s = f . style ;
s . className = prefix + styles [ 0 ] ;
s . position = "absolute" ;
s . height = "1px" ;
s . width = "1px" ;
s . top = "-1000px" ;
s . left = "-1000px" ;
f . innerHTML = sb . join ( "" ) ;
document . body . appendChild ( f ) ;
Event . removeListener ( window , "load" , TV . preload ) ;
} ;
Event . addListener ( window , "load" , TV . preload ) ;
} ) ( ) ;
( function ( ) {
2019-07-17 20:16:19 +00:00
var Dom = YAHOO . util . Dom ,
Lang = YAHOO . lang ,
Event = YAHOO . util . Event ;
2019-07-17 20:08:50 +00:00
/ * *
* The base class for all tree nodes . The node ' s presentation and behavior in
* response to mouse events is handled in Node subclasses .
* @ namespace YAHOO . widget
* @ class Node
* @ uses YAHOO . util . EventProvider
* @ param oData { object } a string or object containing the data that will
* be used to render this node , and any custom attributes that should be
* stored with the node ( which is available in noderef . data ) .
* All values in oData will be used to set equally named properties in the node
2019-07-17 20:16:19 +00:00
* as long as the node does have such properties , they are not undefined , private or functions ,
* the rest of the values will be stored in noderef . data
2019-07-17 20:08:50 +00:00
* @ param oParent { Node } this node ' s parent node
* @ param expanded { boolean } the initial expanded / collapsed state ( deprecated , use oData . expanded )
* @ constructor
* /
YAHOO . widget . Node = function ( oData , oParent , expanded ) {
if ( oData ) { this . init ( oData , oParent , expanded ) ; }
} ;
YAHOO . widget . Node . prototype = {
/ * *
* The index for this instance obtained from global counter in YAHOO . widget . TreeView .
* @ property index
* @ type int
* /
index : 0 ,
/ * *
* This node ' s child node collection .
* @ property children
* @ type Node [ ]
* /
children : null ,
/ * *
* Tree instance this node is part of
* @ property tree
* @ type TreeView
* /
tree : null ,
/ * *
* The data linked to this node . This can be any object or primitive
* value , and the data can be used in getNodeHtml ( ) .
* @ property data
* @ type object
* /
data : null ,
/ * *
* Parent node
* @ property parent
* @ type Node
* /
parent : null ,
/ * *
* The depth of this node . We start at - 1 for the root node .
* @ property depth
* @ type int
* /
depth : - 1 ,
/ * *
* The node ' s expanded / collapsed state
* @ property expanded
* @ type boolean
* /
expanded : false ,
/ * *
* Can multiple children be expanded at once ?
* @ property multiExpand
* @ type boolean
* /
multiExpand : true ,
/ * *
* Should we render children for a collapsed node ? It is possible that the
* implementer will want to render the hidden data ... @ todo verify that we
* need this , and implement it if we do .
* @ property renderHidden
* @ type boolean
* /
renderHidden : false ,
/ * *
* This flag is set to true when the html is generated for this node ' s
* children , and set to false when new children are added .
* @ property childrenRendered
* @ type boolean
* /
childrenRendered : false ,
/ * *
* Dynamically loaded nodes only fetch the data the first time they are
* expanded . This flag is set to true once the data has been fetched .
* @ property dynamicLoadComplete
* @ type boolean
* /
dynamicLoadComplete : false ,
/ * *
* This node ' s previous sibling
* @ property previousSibling
* @ type Node
* /
previousSibling : null ,
/ * *
* This node ' s next sibling
* @ property nextSibling
* @ type Node
* /
nextSibling : null ,
/ * *
* We can set the node up to call an external method to get the child
* data dynamically .
* @ property _dynLoad
* @ type boolean
* @ private
* /
_dynLoad : false ,
/ * *
* Function to execute when we need to get this node ' s child data .
* @ property dataLoader
* @ type function
* /
dataLoader : null ,
/ * *
* This is true for dynamically loading nodes while waiting for the
* callback to return .
* @ property isLoading
* @ type boolean
* /
isLoading : false ,
/ * *
* The toggle / branch icon will not show if this is set to false . This
* could be useful if the implementer wants to have the child contain
* extra info about the parent , rather than an actual node .
* @ property hasIcon
* @ type boolean
* /
hasIcon : true ,
/ * *
* Used to configure what happens when a dynamic load node is expanded
* and we discover that it does not have children . By default , it is
* treated as if it still could have children ( plus / minus icon ) . Set
* iconMode to have it display like a leaf node instead .
* @ property iconMode
* @ type int
* /
iconMode : 0 ,
/ * *
* Specifies whether or not the content area of the node should be allowed
* to wrap .
* @ property nowrap
* @ type boolean
* @ default false
* /
nowrap : false ,
/ * *
* If true , the node will alway be rendered as a leaf node . This can be
* used to override the presentation when dynamically loading the entire
* tree . Setting this to true also disables the dynamic load call for the
* node .
* @ property isLeaf
* @ type boolean
* @ default false
* /
isLeaf : false ,
/ * *
* The CSS class for the html content container . Defaults to ygtvhtml , but
* can be overridden to provide a custom presentation for a specific node .
* @ property contentStyle
* @ type string
* /
contentStyle : "" ,
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
/ * *
* The generated id that will contain the data passed in by the implementer .
* @ property contentElId
* @ type string
* /
contentElId : null ,
2019-07-17 20:16:19 +00:00
/ * *
* Enables node highlighting . If true , the node can be highlighted and / or propagate highlighting
* @ property enableHighlight
* @ type boolean
* @ default true
* /
enableHighlight : true ,
/ * *
* Stores the highlight state . Can be any of :
* < ul >
* < li > 0 - not highlighted < / l i >
* < li > 1 - highlighted < / l i >
* < li > 2 - some children highlighted < / l i >
* < / u l >
* @ property highlightState
* @ type integer
* @ default 0
* /
highlightState : 0 ,
/ * *
* Tells whether highlighting will be propagated up to the parents of the clicked node
* @ property propagateHighlightUp
* @ type boolean
* @ default false
* /
propagateHighlightUp : false ,
/ * *
* Tells whether highlighting will be propagated down to the children of the clicked node
* @ property propagateHighlightDown
* @ type boolean
* @ default false
* /
propagateHighlightDown : false ,
/ * *
* User - defined className to be added to the Node
* @ property className
* @ type string
* @ default null
* /
className : null ,
2019-07-17 20:08:50 +00:00
/ * *
* The node type
* @ property _type
* @ private
* @ type string
* @ default "Node"
* /
_type : "Node" ,
/ *
spacerPath : "http://us.i1.yimg.com/us.yimg.com/i/space.gif" ,
expandedText : "Expanded" ,
collapsedText : "Collapsed" ,
loadingText : "Loading" ,
* /
/ * *
* Initializes this node , gets some of the properties from the parent
* @ method init
* @ param oData { object } a string or object containing the data that will
* be used to render this node
* @ param oParent { Node } this node ' s parent node
* @ param expanded { boolean } the initial expanded / collapsed state
* /
init : function ( oData , oParent , expanded ) {
2019-07-17 20:16:19 +00:00
this . data = { } ;
2019-07-17 20:08:50 +00:00
this . children = [ ] ;
this . index = YAHOO . widget . TreeView . nodeCount ;
++ YAHOO . widget . TreeView . nodeCount ;
2019-07-17 20:16:19 +00:00
this . contentElId = "ygtvcontentel" + this . index ;
if ( Lang . isObject ( oData ) ) {
for ( var property in oData ) {
if ( oData . hasOwnProperty ( property ) ) {
if ( property . charAt ( 0 ) != '_' && ! Lang . isUndefined ( this [ property ] ) && ! Lang . isFunction ( this [ property ] ) ) {
this [ property ] = oData [ property ] ;
} else {
this . data [ property ] = oData [ property ] ;
}
}
}
}
if ( ! Lang . isUndefined ( expanded ) ) { this . expanded = expanded ; }
2019-07-17 20:08:50 +00:00
/ * *
* The parentChange event is fired when a parent element is applied
* to the node . This is useful if you need to apply tree - level
* properties to a tree that need to happen if a node is moved from
* one tree to another .
*
* @ event parentChange
* @ type CustomEvent
* /
this . createEvent ( "parentChange" , this ) ;
// oParent should never be null except when we create the root node.
if ( oParent ) {
oParent . appendChild ( this ) ;
}
} ,
/ * *
* Certain properties for the node cannot be set until the parent
* is known . This is called after the node is inserted into a tree .
* the parent is also applied to this node ' s children in order to
* make it possible to move a branch from one tree to another .
* @ method applyParent
* @ param { Node } parentNode this node ' s parent node
* @ return { boolean } true if the application was successful
* /
applyParent : function ( parentNode ) {
if ( ! parentNode ) {
return false ;
}
this . tree = parentNode . tree ;
this . parent = parentNode ;
this . depth = parentNode . depth + 1 ;
// @todo why was this put here. This causes new nodes added at the
// root level to lose the menu behavior.
// if (! this.multiExpand) {
// this.multiExpand = parentNode.multiExpand;
// }
this . tree . regNode ( this ) ;
parentNode . childrenRendered = false ;
// cascade update existing children
for ( var i = 0 , len = this . children . length ; i < len ; ++ i ) {
this . children [ i ] . applyParent ( this ) ;
}
this . fireEvent ( "parentChange" ) ;
return true ;
} ,
/ * *
* Appends a node to the child collection .
* @ method appendChild
* @ param childNode { Node } the new node
* @ return { Node } the child node
* @ private
* /
appendChild : function ( childNode ) {
if ( this . hasChildren ( ) ) {
var sib = this . children [ this . children . length - 1 ] ;
sib . nextSibling = childNode ;
childNode . previousSibling = sib ;
}
this . children [ this . children . length ] = childNode ;
childNode . applyParent ( this ) ;
// part of the IE display issue workaround. If child nodes
// are added after the initial render, and the node was
// instantiated with expanded = true, we need to show the
// children div now that the node has a child.
if ( this . childrenRendered && this . expanded ) {
this . getChildrenEl ( ) . style . display = "" ;
}
return childNode ;
} ,
/ * *
* Appends this node to the supplied node ' s child collection
* @ method appendTo
* @ param parentNode { Node } the node to append to .
* @ return { Node } The appended node
* /
appendTo : function ( parentNode ) {
return parentNode . appendChild ( this ) ;
} ,
/ * *
* Inserts this node before this supplied node
* @ method insertBefore
* @ param node { Node } the node to insert this node before
* @ return { Node } the inserted node
* /
insertBefore : function ( node ) {
var p = node . parent ;
if ( p ) {
if ( this . tree ) {
this . tree . popNode ( this ) ;
}
var refIndex = node . isChildOf ( p ) ;
p . children . splice ( refIndex , 0 , this ) ;
if ( node . previousSibling ) {
node . previousSibling . nextSibling = this ;
}
this . previousSibling = node . previousSibling ;
this . nextSibling = node ;
node . previousSibling = this ;
this . applyParent ( p ) ;
}
return this ;
} ,
/ * *
* Inserts this node after the supplied node
* @ method insertAfter
* @ param node { Node } the node to insert after
* @ return { Node } the inserted node
* /
insertAfter : function ( node ) {
var p = node . parent ;
if ( p ) {
if ( this . tree ) {
this . tree . popNode ( this ) ;
}
var refIndex = node . isChildOf ( p ) ;
if ( ! node . nextSibling ) {
this . nextSibling = null ;
return this . appendTo ( p ) ;
}
p . children . splice ( refIndex + 1 , 0 , this ) ;
node . nextSibling . previousSibling = this ;
this . previousSibling = node ;
this . nextSibling = node . nextSibling ;
node . nextSibling = this ;
this . applyParent ( p ) ;
}
return this ;
} ,
/ * *
* Returns true if the Node is a child of supplied Node
* @ method isChildOf
* @ param parentNode { Node } the Node to check
* @ return { boolean } The node index if this Node is a child of
* supplied Node , else - 1.
* @ private
* /
isChildOf : function ( parentNode ) {
if ( parentNode && parentNode . children ) {
for ( var i = 0 , len = parentNode . children . length ; i < len ; ++ i ) {
if ( parentNode . children [ i ] === this ) {
return i ;
}
}
}
return - 1 ;
} ,
/ * *
* Returns a node array of this node ' s siblings , null if none .
* @ method getSiblings
* @ return Node [ ]
* /
getSiblings : function ( ) {
2019-07-17 20:16:19 +00:00
var sib = this . parent . children . slice ( 0 ) ;
for ( var i = 0 ; i < sib . length && sib [ i ] != this ; i ++ ) { }
sib . splice ( i , 1 ) ;
if ( sib . length ) { return sib ; }
return null ;
2019-07-17 20:08:50 +00:00
} ,
/ * *
* Shows this node ' s children
* @ method showChildren
* /
showChildren : function ( ) {
if ( ! this . tree . animateExpand ( this . getChildrenEl ( ) , this ) ) {
if ( this . hasChildren ( ) ) {
this . getChildrenEl ( ) . style . display = "" ;
}
}
} ,
/ * *
* Hides this node ' s children
* @ method hideChildren
* /
hideChildren : function ( ) {
if ( ! this . tree . animateCollapse ( this . getChildrenEl ( ) , this ) ) {
this . getChildrenEl ( ) . style . display = "none" ;
}
} ,
/ * *
* Returns the id for this node ' s container div
* @ method getElId
* @ return { string } the element id
* /
getElId : function ( ) {
return "ygtv" + this . index ;
} ,
/ * *
* Returns the id for this node ' s children div
* @ method getChildrenElId
* @ return { string } the element id for this node ' s children div
* /
getChildrenElId : function ( ) {
return "ygtvc" + this . index ;
} ,
/ * *
* Returns the id for this node ' s toggle element
* @ method getToggleElId
* @ return { string } the toggel element id
* /
getToggleElId : function ( ) {
return "ygtvt" + this . index ;
} ,
/ *
* Returns the id for this node ' s spacer image . The spacer is positioned
* over the toggle and provides feedback for screen readers .
* @ method getSpacerId
* @ return { string } the id for the spacer image
* /
/ *
getSpacerId : function ( ) {
return "ygtvspacer" + this . index ;
} ,
* /
/ * *
* Returns this node ' s container html element
* @ method getEl
* @ return { HTMLElement } the container html element
* /
getEl : function ( ) {
return Dom . get ( this . getElId ( ) ) ;
} ,
/ * *
* Returns the div that was generated for this node ' s children
* @ method getChildrenEl
* @ return { HTMLElement } this node ' s children div
* /
getChildrenEl : function ( ) {
return Dom . get ( this . getChildrenElId ( ) ) ;
} ,
/ * *
* Returns the element that is being used for this node ' s toggle .
* @ method getToggleEl
* @ return { HTMLElement } this node ' s toggle html element
* /
getToggleEl : function ( ) {
return Dom . get ( this . getToggleElId ( ) ) ;
} ,
/ * *
2019-07-17 20:16:19 +00:00
* Returns the outer html element for this node ' s content
* @ method getContentEl
* @ return { HTMLElement } the element
* /
2019-07-17 20:08:50 +00:00
getContentEl : function ( ) {
return Dom . get ( this . contentElId ) ;
} ,
/ *
* Returns the element that is being used for this node ' s spacer .
* @ method getSpacer
* @ return { HTMLElement } this node ' s spacer html element
* /
/ *
getSpacer : function ( ) {
return document . getElementById ( this . getSpacerId ( ) ) || { } ;
} ,
* /
/ *
getStateText : function ( ) {
if ( this . isLoading ) {
return this . loadingText ;
} else if ( this . hasChildren ( true ) ) {
if ( this . expanded ) {
return this . expandedText ;
} else {
return this . collapsedText ;
}
} else {
return "" ;
}
} ,
* /
/ * *
* Hides this nodes children ( creating them if necessary ) , changes the toggle style .
* @ method collapse
* /
collapse : function ( ) {
// Only collapse if currently expanded
if ( ! this . expanded ) { return ; }
// fire the collapse event handler
var ret = this . tree . onCollapse ( this ) ;
if ( false === ret ) {
return ;
}
ret = this . tree . fireEvent ( "collapse" , this ) ;
if ( false === ret ) {
return ;
}
if ( ! this . getEl ( ) ) {
this . expanded = false ;
} else {
// hide the child div
this . hideChildren ( ) ;
this . expanded = false ;
this . updateIcon ( ) ;
}
// this.getSpacer().title = this.getStateText();
ret = this . tree . fireEvent ( "collapseComplete" , this ) ;
} ,
/ * *
* Shows this nodes children ( creating them if necessary ) , changes the
* toggle style , and collapses its siblings if multiExpand is not set .
* @ method expand
* /
expand : function ( lazySource ) {
// Only expand if currently collapsed.
if ( this . expanded && ! lazySource ) {
return ;
}
var ret = true ;
// When returning from the lazy load handler, expand is called again
// in order to render the new children. The "expand" event already
// fired before fething the new data, so we need to skip it now.
if ( ! lazySource ) {
// fire the expand event handler
ret = this . tree . onExpand ( this ) ;
if ( false === ret ) {
return ;
}
ret = this . tree . fireEvent ( "expand" , this ) ;
}
if ( false === ret ) {
return ;
}
if ( ! this . getEl ( ) ) {
this . expanded = true ;
return ;
}
if ( ! this . childrenRendered ) {
this . getChildrenEl ( ) . innerHTML = this . renderChildren ( ) ;
} else {
}
this . expanded = true ;
this . updateIcon ( ) ;
// this.getSpacer().title = this.getStateText();
// We do an extra check for children here because the lazy
// load feature can expose nodes that have no children.
// if (!this.hasChildren()) {
if ( this . isLoading ) {
this . expanded = false ;
return ;
}
if ( ! this . multiExpand ) {
var sibs = this . getSiblings ( ) ;
for ( var i = 0 ; sibs && i < sibs . length ; ++ i ) {
if ( sibs [ i ] != this && sibs [ i ] . expanded ) {
sibs [ i ] . collapse ( ) ;
}
}
}
this . showChildren ( ) ;
ret = this . tree . fireEvent ( "expandComplete" , this ) ;
} ,
updateIcon : function ( ) {
if ( this . hasIcon ) {
var el = this . getToggleEl ( ) ;
if ( el ) {
2019-07-17 20:16:19 +00:00
el . className = el . className . replace ( /\bygtv(([tl][pmn]h?)|(loading))\b/gi , this . getStyle ( ) ) ;
2019-07-17 20:08:50 +00:00
}
}
} ,
/ * *
* Returns the css style name for the toggle
* @ method getStyle
* @ return { string } the css class for this node ' s toggle
* /
getStyle : function ( ) {
if ( this . isLoading ) {
return "ygtvloading" ;
} else {
// location top or bottom, middle nodes also get the top style
var loc = ( this . nextSibling ) ? "t" : "l" ;
// type p=plus(expand), m=minus(collapase), n=none(no children)
var type = "n" ;
if ( this . hasChildren ( true ) || ( this . isDynamic ( ) && ! this . getIconMode ( ) ) ) {
// if (this.hasChildren(true)) {
type = ( this . expanded ) ? "m" : "p" ;
}
return "ygtv" + loc + type ;
}
} ,
/ * *
* Returns the hover style for the icon
* @ return { string } the css class hover state
* @ method getHoverStyle
* /
getHoverStyle : function ( ) {
var s = this . getStyle ( ) ;
if ( this . hasChildren ( true ) && ! this . isLoading ) {
s += "h" ;
}
return s ;
} ,
/ * *
* Recursively expands all of this node ' s children .
* @ method expandAll
* /
expandAll : function ( ) {
2019-07-17 20:16:19 +00:00
var l = this . children . length ;
for ( var i = 0 ; i < l ; ++ i ) {
2019-07-17 20:08:50 +00:00
var c = this . children [ i ] ;
if ( c . isDynamic ( ) ) {
break ;
} else if ( ! c . multiExpand ) {
break ;
} else {
c . expand ( ) ;
c . expandAll ( ) ;
}
}
} ,
/ * *
* Recursively collapses all of this node ' s children .
* @ method collapseAll
* /
collapseAll : function ( ) {
for ( var i = 0 ; i < this . children . length ; ++ i ) {
this . children [ i ] . collapse ( ) ;
this . children [ i ] . collapseAll ( ) ;
}
} ,
/ * *
* Configures this node for dynamically obtaining the child data
* when the node is first expanded . Calling it without the callback
* will turn off dynamic load for the node .
* @ method setDynamicLoad
* @ param fmDataLoader { function } the function that will be used to get the data .
* @ param iconMode { int } configures the icon that is displayed when a dynamic
* load node is expanded the first time without children . By default , the
* "collapse" icon will be used . If set to 1 , the leaf node icon will be
* displayed .
* /
setDynamicLoad : function ( fnDataLoader , iconMode ) {
if ( fnDataLoader ) {
this . dataLoader = fnDataLoader ;
this . _dynLoad = true ;
} else {
this . dataLoader = null ;
this . _dynLoad = false ;
}
if ( iconMode ) {
this . iconMode = iconMode ;
}
} ,
/ * *
* Evaluates if this node is the root node of the tree
* @ method isRoot
* @ return { boolean } true if this is the root node
* /
isRoot : function ( ) {
return ( this == this . tree . root ) ;
} ,
/ * *
* Evaluates if this node ' s children should be loaded dynamically . Looks for
* the property both in this instance and the root node . If the tree is
* defined to load all children dynamically , the data callback function is
* defined in the root node
* @ method isDynamic
* @ return { boolean } true if this node ' s children are to be loaded dynamically
* /
isDynamic : function ( ) {
if ( this . isLeaf ) {
return false ;
} else {
return ( ! this . isRoot ( ) && ( this . _dynLoad || this . tree . root . _dynLoad ) ) ;
// return lazy;
}
} ,
/ * *
* Returns the current icon mode . This refers to the way childless dynamic
* load nodes appear ( this comes into play only after the initial dynamic
* load request produced no children ) .
* @ method getIconMode
* @ return { int } 0 for collapse style , 1 for leaf node style
* /
getIconMode : function ( ) {
return ( this . iconMode || this . tree . root . iconMode ) ;
} ,
/ * *
* Checks if this node has children . If this node is lazy - loading and the
* children have not been rendered , we do not know whether or not there
* are actual children . In most cases , we need to assume that there are
* children ( for instance , the toggle needs to show the expandable
* presentation state ) . In other times we want to know if there are rendered
* children . For the latter , "checkForLazyLoad" should be false .
* @ method hasChildren
* @ param checkForLazyLoad { boolean } should we check for unloaded children ?
* @ return { boolean } true if this has children or if it might and we are
* checking for this condition .
* /
hasChildren : function ( checkForLazyLoad ) {
if ( this . isLeaf ) {
return false ;
} else {
return ( this . children . length > 0 ||
( checkForLazyLoad && this . isDynamic ( ) && ! this . dynamicLoadComplete ) ) ;
}
} ,
/ * *
* Expands if node is collapsed , collapses otherwise .
* @ method toggle
* /
toggle : function ( ) {
if ( ! this . tree . locked && ( this . hasChildren ( true ) || this . isDynamic ( ) ) ) {
if ( this . expanded ) { this . collapse ( ) ; } else { this . expand ( ) ; }
}
} ,
/ * *
* Returns the markup for this node and its children .
* @ method getHtml
* @ return { string } the markup for this node and its expanded children .
* /
getHtml : function ( ) {
this . childrenRendered = false ;
2019-07-17 20:16:19 +00:00
return [ '<div class="ygtvitem" id="' , this . getElId ( ) , '">' , this . getNodeHtml ( ) , this . getChildrenHtml ( ) , '</div>' ] . join ( "" ) ;
2019-07-17 20:08:50 +00:00
} ,
/ * *
* Called when first rendering the tree . We always build the div that will
* contain this nodes children , but we don ' t render the children themselves
* unless this node is expanded .
* @ method getChildrenHtml
* @ return { string } the children container div html and any expanded children
* @ private
* /
getChildrenHtml : function ( ) {
var sb = [ ] ;
2019-07-17 20:16:19 +00:00
sb [ sb . length ] = '<div class="ygtvchildren" id="' + this . getChildrenElId ( ) + '"' ;
2019-07-17 20:08:50 +00:00
// This is a workaround for an IE rendering issue, the child div has layout
// in IE, creating extra space if a leaf node is created with the expanded
// property set to true.
if ( ! this . expanded || ! this . hasChildren ( ) ) {
sb [ sb . length ] = ' style="display:none;"' ;
}
sb [ sb . length ] = '>' ;
// Don't render the actual child node HTML unless this node is expanded.
if ( ( this . hasChildren ( true ) && this . expanded ) ||
( this . renderHidden && ! this . isDynamic ( ) ) ) {
sb [ sb . length ] = this . renderChildren ( ) ;
}
sb [ sb . length ] = '</div>' ;
return sb . join ( "" ) ;
} ,
/ * *
* Generates the markup for the child nodes . This is not done until the node
* is expanded .
* @ method renderChildren
* @ return { string } the html for this node ' s children
* @ private
* /
renderChildren : function ( ) {
var node = this ;
if ( this . isDynamic ( ) && ! this . dynamicLoadComplete ) {
this . isLoading = true ;
this . tree . locked = true ;
if ( this . dataLoader ) {
setTimeout (
function ( ) {
node . dataLoader ( node ,
function ( ) {
node . loadComplete ( ) ;
} ) ;
} , 10 ) ;
} else if ( this . tree . root . dataLoader ) {
setTimeout (
function ( ) {
node . tree . root . dataLoader ( node ,
function ( ) {
node . loadComplete ( ) ;
} ) ;
} , 10 ) ;
} else {
return "Error: data loader not found or not specified." ;
}
return "" ;
} else {
return this . completeRender ( ) ;
}
} ,
/ * *
* Called when we know we have all the child data .
* @ method completeRender
* @ return { string } children html
* /
completeRender : function ( ) {
var sb = [ ] ;
for ( var i = 0 ; i < this . children . length ; ++ i ) {
// this.children[i].childrenRendered = false;
sb [ sb . length ] = this . children [ i ] . getHtml ( ) ;
}
this . childrenRendered = true ;
return sb . join ( "" ) ;
} ,
/ * *
* Load complete is the callback function we pass to the data provider
* in dynamic load situations .
* @ method loadComplete
* /
loadComplete : function ( ) {
this . getChildrenEl ( ) . innerHTML = this . completeRender ( ) ;
this . dynamicLoadComplete = true ;
this . isLoading = false ;
this . expand ( true ) ;
this . tree . locked = false ;
} ,
/ * *
* Returns this node ' s ancestor at the specified depth .
* @ method getAncestor
* @ param { int } depth the depth of the ancestor .
* @ return { Node } the ancestor
* /
getAncestor : function ( depth ) {
if ( depth >= this . depth || depth < 0 ) {
return null ;
}
var p = this . parent ;
while ( p . depth > depth ) {
p = p . parent ;
}
return p ;
} ,
/ * *
* Returns the css class for the spacer at the specified depth for
* this node . If this node ' s ancestor at the specified depth
* has a next sibling the presentation is different than if it
* does not have a next sibling
* @ method getDepthStyle
* @ param { int } depth the depth of the ancestor .
* @ return { string } the css class for the spacer
* /
getDepthStyle : function ( depth ) {
return ( this . getAncestor ( depth ) . nextSibling ) ?
"ygtvdepthcell" : "ygtvblankdepthcell" ;
} ,
/ * *
* Get the markup for the node . This may be overrided so that we can
* support different types of nodes .
* @ method getNodeHtml
* @ return { string } The HTML that will render this node .
* /
getNodeHtml : function ( ) {
var sb = [ ] ;
2019-07-17 20:16:19 +00:00
sb [ sb . length ] = '<table id="ygtvtableel' + this . index + '"border="0" cellpadding="0" cellspacing="0" class="ygtvtable ygtvdepth' + this . depth ;
if ( this . enableHighlight ) {
sb [ sb . length ] = ' ygtv-highlight' + this . highlightState ;
}
if ( this . className ) {
sb [ sb . length ] = ' ' + this . className ;
}
sb [ sb . length ] = '"><tr class="ygtvrow">' ;
2019-07-17 20:08:50 +00:00
for ( var i = 0 ; i < this . depth ; ++ i ) {
2019-07-17 20:16:19 +00:00
sb [ sb . length ] = '<td class="ygtvcell ' + this . getDepthStyle ( i ) + '"><div class="ygtvspacer"></div></td>' ;
2019-07-17 20:08:50 +00:00
}
if ( this . hasIcon ) {
2019-07-17 20:16:19 +00:00
sb [ sb . length ] = '<td id="' + this . getToggleElId ( ) ;
sb [ sb . length ] = '" class="ygtvcell ' ;
sb [ sb . length ] = this . getStyle ( ) ;
sb [ sb . length ] = '"><a href="#" class="ygtvspacer"> </a></td>' ;
2019-07-17 20:08:50 +00:00
}
2019-07-17 20:16:19 +00:00
sb [ sb . length ] = '<td id="' + this . contentElId ;
sb [ sb . length ] = '" class="ygtvcell ' ;
sb [ sb . length ] = this . contentStyle + ' ygtvcontent" ' ;
2019-07-17 20:08:50 +00:00
sb [ sb . length ] = ( this . nowrap ) ? ' nowrap="nowrap" ' : '' ;
sb [ sb . length ] = ' >' ;
2019-07-17 20:16:19 +00:00
sb [ sb . length ] = this . getContentHtml ( ) ;
sb [ sb . length ] = '</td></tr></table>' ;
2019-07-17 20:08:50 +00:00
return sb . join ( "" ) ;
} ,
2019-07-17 20:16:19 +00:00
/ * *
2019-07-17 20:08:50 +00:00
* Get the markup for the contents of the node . This is designed to be overrided so that we can
* support different types of nodes .
* @ method getContentHtml
* @ return { string } The HTML that will render the content of this node .
* /
2019-07-17 20:16:19 +00:00
getContentHtml : function ( ) {
return "" ;
} ,
2019-07-17 20:08:50 +00:00
/ * *
* Regenerates the html for this node and its children . To be used when the
* node is expanded and new children have been added .
* @ method refresh
* /
refresh : function ( ) {
// this.loadComplete();
this . getChildrenEl ( ) . innerHTML = this . completeRender ( ) ;
if ( this . hasIcon ) {
var el = this . getToggleEl ( ) ;
if ( el ) {
2019-07-17 20:16:19 +00:00
el . className = el . className . replace ( /\bygtv[lt][nmp]h*\b/gi , this . getStyle ( ) ) ;
2019-07-17 20:08:50 +00:00
}
}
} ,
/ * *
* Node toString
* @ method toString
* @ return { string } string representation of the node
* /
toString : function ( ) {
return this . _type + " (" + this . index + ")" ;
} ,
2019-07-17 20:16:19 +00:00
/ * *
* array of items that had the focus set on them
* so that they can be cleaned when focus is lost
* @ property _focusHighlightedItems
* @ type Array of DOM elements
* @ private
* /
_focusHighlightedItems : [ ] ,
/ * *
* DOM element that actually got the browser focus
* @ property _focusedItem
* @ type DOM element
* @ private
* /
_focusedItem : null ,
/ * *
* Returns true if there are any elements in the node that can
* accept the real actual browser focus
* @ method _canHaveFocus
* @ return { boolean } success
* @ private
* /
_canHaveFocus : function ( ) {
return this . getEl ( ) . getElementsByTagName ( 'a' ) . length > 0 ;
} ,
/ * *
* Removes the focus of previously selected Node
* @ method _removeFocus
* @ private
* /
_removeFocus : function ( ) {
if ( this . _focusedItem ) {
Event . removeListener ( this . _focusedItem , 'blur' ) ;
this . _focusedItem = null ;
}
var el ;
while ( ( el = this . _focusHighlightedItems . shift ( ) ) ) { // yes, it is meant as an assignment, really
Dom . removeClass ( el , YAHOO . widget . TreeView . FOCUS _CLASS _NAME ) ;
}
} ,
/ * *
* Sets the focus on the node element .
* It will only be able to set the focus on nodes that have anchor elements in it .
* Toggle or branch icons have anchors and can be focused on .
* If will fail in nodes that have no anchor
* @ method focus
* @ return { boolean } success
* /
focus : function ( ) {
var focused = false , self = this ;
if ( this . tree . currentFocus ) {
this . tree . currentFocus . _removeFocus ( ) ;
}
var expandParent = function ( node ) {
if ( node . parent ) {
expandParent ( node . parent ) ;
node . parent . expand ( ) ;
}
} ;
expandParent ( this ) ;
Dom . getElementsBy (
function ( el ) {
return /ygtv(([tl][pmn]h?)|(content))/ . test ( el . className ) ;
} ,
'td' ,
self . getEl ( ) . firstChild ,
function ( el ) {
Dom . addClass ( el , YAHOO . widget . TreeView . FOCUS _CLASS _NAME ) ;
if ( ! focused ) {
var aEl = el . getElementsByTagName ( 'a' ) ;
if ( aEl . length ) {
aEl = aEl [ 0 ] ;
aEl . focus ( ) ;
self . _focusedItem = aEl ;
Event . on ( aEl , 'blur' , function ( ) {
//console.log('f1');
self . tree . fireEvent ( 'focusChanged' , { oldNode : self . tree . currentFocus , newNode : null } ) ;
self . tree . currentFocus = null ;
self . _removeFocus ( ) ;
} ) ;
focused = true ;
}
}
self . _focusHighlightedItems . push ( el ) ;
}
) ;
if ( focused ) {
//console.log('f2');
this . tree . fireEvent ( 'focusChanged' , { oldNode : this . tree . currentFocus , newNode : this } ) ;
this . tree . currentFocus = this ;
} else {
//console.log('f3');
this . tree . fireEvent ( 'focusChanged' , { oldNode : self . tree . currentFocus , newNode : null } ) ;
this . tree . currentFocus = null ;
this . _removeFocus ( ) ;
}
return focused ;
} ,
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Count of nodes in a branch
2019-07-17 20:08:50 +00:00
* @ method getNodeCount
2019-07-17 20:16:19 +00:00
* @ return { int } number of nodes in the branch
2019-07-17 20:08:50 +00:00
* /
getNodeCount : function ( ) {
2019-07-17 20:16:19 +00:00
for ( var i = 0 , count = 0 ; i < this . children . length ; i ++ ) {
count += this . children [ i ] . getNodeCount ( ) ;
}
2019-07-17 20:08:50 +00:00
return count + 1 ;
} ,
2019-07-17 20:16:19 +00:00
/ * *
2019-07-17 20:08:50 +00:00
* Returns an object which could be used to build a tree out of this node and its children .
* It can be passed to the tree constructor to reproduce this node as a tree .
* It will return false if the node or any children loads dynamically , regardless of whether it is loaded or not .
* @ method getNodeDefinition
* @ return { Object | false } definition of the tree or false if the node or any children is defined as dynamic
* /
getNodeDefinition : function ( ) {
2019-07-17 20:16:19 +00:00
if ( this . isDynamic ( ) ) { return false ; }
var def , defs = Lang . merge ( this . data ) , children = [ ] ;
if ( this . expanded ) { defs . expanded = this . expanded ; }
if ( ! this . multiExpand ) { defs . multiExpand = this . multiExpand ; }
if ( ! this . renderHidden ) { defs . renderHidden = this . renderHidden ; }
if ( ! this . hasIcon ) { defs . hasIcon = this . hasIcon ; }
if ( this . nowrap ) { defs . nowrap = this . nowrap ; }
if ( this . className ) { defs . className = this . className ; }
if ( this . editable ) { defs . editable = this . editable ; }
if ( this . enableHighlight ) { defs . enableHighlight = this . enableHighlight ; }
if ( this . highlightState ) { defs . highlightState = this . highlightState ; }
if ( this . propagateHighlightUp ) { defs . propagateHighlightUp = this . propagateHighlightUp ; }
if ( this . propagateHighlightDown ) { defs . propagateHighlightDown = this . propagateHighlightDown ; }
defs . type = this . _type ;
for ( var i = 0 ; i < this . children . length ; i ++ ) {
def = this . children [ i ] . getNodeDefinition ( ) ;
if ( def === false ) { return false ; }
children . push ( def ) ;
}
if ( children . length ) { defs . children = children ; }
return defs ;
2019-07-17 20:08:50 +00:00
} ,
/ * *
* Generates the link that will invoke this node ' s toggle method
* @ method getToggleLink
* @ return { string } the javascript url for toggling this node
* /
getToggleLink : function ( ) {
return 'return false;' ;
2019-07-17 20:16:19 +00:00
} ,
/ * *
* Sets the value of property for this node and all loaded descendants .
* Only public and defined properties can be set , not methods .
* Values for unknown properties will be assigned to the refNode . data object
* @ method setNodesProperty
* @ param name { string } Name of the property to be set
* @ param value { any } value to be set
* @ param refresh { boolean } if present and true , it does a refresh
* /
setNodesProperty : function ( name , value , refresh ) {
if ( name . charAt ( 0 ) != '_' && ! Lang . isUndefined ( this [ name ] ) && ! Lang . isFunction ( this [ name ] ) ) {
this [ name ] = value ;
} else {
this . data [ name ] = value ;
}
for ( var i = 0 ; i < this . children . length ; i ++ ) {
this . children [ i ] . setNodesProperty ( name , value ) ;
}
if ( refresh ) {
this . refresh ( ) ;
}
} ,
/ * *
* Toggles the highlighted state of a Node
* @ method toggleHighlight
* /
toggleHighlight : function ( ) {
if ( this . enableHighlight ) {
// unhighlights only if fully highligthed. For not or partially highlighted it will highlight
if ( this . highlightState == 1 ) {
this . unhighlight ( ) ;
} else {
this . highlight ( ) ;
}
}
} ,
/ * *
* Turns highlighting on node .
* @ method highlight
* @ param _silent { boolean } optional , don ' t fire the highlightEvent
* /
highlight : function ( _silent ) {
if ( this . enableHighlight ) {
if ( this . tree . singleNodeHighlight ) {
if ( this . tree . _currentlyHighlighted ) {
this . tree . _currentlyHighlighted . unhighlight ( ) ;
}
this . tree . _currentlyHighlighted = this ;
}
this . highlightState = 1 ;
this . _setHighlightClassName ( ) ;
if ( this . propagateHighlightDown ) {
for ( var i = 0 ; i < this . children . length ; i ++ ) {
this . children [ i ] . highlight ( true ) ;
}
}
if ( this . propagateHighlightUp ) {
if ( this . parent ) {
this . parent . _childrenHighlighted ( ) ;
}
}
if ( ! _silent ) {
this . tree . fireEvent ( 'highlightEvent' , this ) ;
}
}
} ,
/ * *
* Turns highlighting off a node .
* @ method unhighlight
* @ param _silent { boolean } optional , don ' t fire the highlightEvent
* /
unhighlight : function ( _silent ) {
if ( this . enableHighlight ) {
this . highlightState = 0 ;
this . _setHighlightClassName ( ) ;
if ( this . propagateHighlightDown ) {
for ( var i = 0 ; i < this . children . length ; i ++ ) {
this . children [ i ] . unhighlight ( true ) ;
}
}
if ( this . propagateHighlightUp ) {
if ( this . parent ) {
this . parent . _childrenHighlighted ( ) ;
}
}
if ( ! _silent ) {
this . tree . fireEvent ( 'highlightEvent' , this ) ;
}
}
} ,
/ * *
* Checks whether all or part of the children of a node are highlighted and
* sets the node highlight to full , none or partial highlight .
* If set to propagate it will further call the parent
* @ method _childrenHighlighted
* @ private
* /
_childrenHighlighted : function ( ) {
var yes = false , no = false ;
if ( this . enableHighlight ) {
for ( var i = 0 ; i < this . children . length ; i ++ ) {
switch ( this . children [ i ] . highlightState ) {
case 0 :
no = true ;
break ;
case 1 :
yes = true ;
break ;
case 2 :
yes = no = true ;
break ;
}
}
if ( yes && no ) {
this . highlightState = 2 ;
} else if ( yes ) {
this . highlightState = 1 ;
} else {
this . highlightState = 0 ;
}
this . _setHighlightClassName ( ) ;
if ( this . propagateHighlightUp ) {
if ( this . parent ) {
this . parent . _childrenHighlighted ( ) ;
}
}
}
} ,
/ * *
* Changes the classNames on the toggle and content containers to reflect the current highlighting
* @ method _setHighlightClassName
* @ private
* /
_setHighlightClassName : function ( ) {
var el = Dom . get ( 'ygtvtableel' + this . index ) ;
if ( el ) {
el . className = el . className . replace ( /\bygtv-highlight\d\b/gi , 'ygtv-highlight' + this . highlightState ) ;
}
}
} ;
YAHOO . augment ( YAHOO . widget . Node , YAHOO . util . EventProvider ) ;
} ) ( ) ;
/ * *
* A custom YAHOO . widget . Node that handles the unique nature of
* the virtual , presentationless root node .
* @ namespace YAHOO . widget
* @ class RootNode
* @ extends YAHOO . widget . Node
* @ param oTree { YAHOO . widget . TreeView } The tree instance this node belongs to
* @ constructor
* /
YAHOO . widget . RootNode = function ( oTree ) {
// Initialize the node with null params. The root node is a
// special case where the node has no presentation. So we have
// to alter the standard properties a bit.
this . init ( null , null , true ) ;
/ *
* For the root node , we get the tree reference from as a param
* to the constructor instead of from the parent element .
* /
this . tree = oTree ;
} ;
YAHOO . extend ( YAHOO . widget . RootNode , YAHOO . widget . Node , {
/ * *
* The node type
* @ property _type
* @ type string
* @ private
* @ default "RootNode"
* /
_type : "RootNode" ,
// overrides YAHOO.widget.Node
getNodeHtml : function ( ) {
return "" ;
} ,
toString : function ( ) {
return this . _type ;
} ,
loadComplete : function ( ) {
this . tree . draw ( ) ;
} ,
/ * *
* Count of nodes in tree .
* It overrides Nodes . getNodeCount because the root node should not be counted .
* @ method getNodeCount
* @ return { int } number of nodes in the tree
* /
getNodeCount : function ( ) {
for ( var i = 0 , count = 0 ; i < this . children . length ; i ++ ) {
count += this . children [ i ] . getNodeCount ( ) ;
}
return count ;
} ,
/ * *
* Returns an object which could be used to build a tree out of this node and its children .
* It can be passed to the tree constructor to reproduce this node as a tree .
* Since the RootNode is automatically created by treeView ,
* its own definition is excluded from the returned node definition
* which only contains its children .
* @ method getNodeDefinition
* @ return { Object | false } definition of the tree or false if any child node is defined as dynamic
* /
getNodeDefinition : function ( ) {
for ( var def , defs = [ ] , i = 0 ; i < this . children . length ; i ++ ) {
def = this . children [ i ] . getNodeDefinition ( ) ;
if ( def === false ) { return false ; }
defs . push ( def ) ;
}
return defs ;
} ,
collapse : function ( ) { } ,
expand : function ( ) { } ,
getSiblings : function ( ) { return null ; } ,
focus : function ( ) { }
} ) ;
( function ( ) {
var Dom = YAHOO . util . Dom ,
Lang = YAHOO . lang ,
Event = YAHOO . util . Event ;
/ * *
* The default node presentation . The first parameter should be
* either a string that will be used as the node ' s label , or an object
* that has at least a string property called label . By default , clicking the
* label will toggle the expanded / collapsed state of the node . By
* setting the href property of the instance , this behavior can be
* changed so that the label will go to the specified href .
* @ namespace YAHOO . widget
* @ class TextNode
* @ extends YAHOO . widget . Node
* @ constructor
* @ param oData { object } a string or object containing the data that will
* be used to render this node .
* Providing a string is the same as providing an object with a single property named label .
* All values in the oData will be used to set equally named properties in the node
* as long as the node does have such properties , they are not undefined , private or functions .
* All attributes are made available in noderef . data , which
* can be used to store custom attributes . TreeView . getNode ( s ) ByProperty
* can be used to retrieve a node by one of the attributes .
* @ param oParent { YAHOO . widget . Node } this node ' s parent node
* @ param expanded { boolean } the initial expanded / collapsed state ( deprecated ; use oData . expanded )
* /
YAHOO . widget . TextNode = function ( oData , oParent , expanded ) {
if ( oData ) {
if ( Lang . isString ( oData ) ) {
oData = { label : oData } ;
2019-07-17 20:08:50 +00:00
}
this . init ( oData , oParent , expanded ) ;
this . setUpLabel ( oData ) ;
}
} ;
YAHOO . extend ( YAHOO . widget . TextNode , YAHOO . widget . Node , {
/ * *
* The CSS class for the label href . Defaults to ygtvlabel , but can be
* overridden to provide a custom presentation for a specific node .
* @ property labelStyle
* @ type string
* /
labelStyle : "ygtvlabel" ,
/ * *
* The derived element id of the label for this node
* @ property labelElId
* @ type string
* /
labelElId : null ,
/ * *
* The text for the label . It is assumed that the oData parameter will
* either be a string that will be used as the label , or an object that
* has a property called "label" that we will use .
* @ property label
* @ type string
* /
label : null ,
/ * *
* The text for the title ( tooltip ) for the label element
* @ property title
* @ type string
* /
title : null ,
2019-07-17 20:16:19 +00:00
/ * *
* The href for the node ' s label . If one is not specified , the href will
* be set so that it toggles the node .
* @ property href
* @ type string
* /
href : null ,
/ * *
* The label href target , defaults to current window
* @ property target
* @ type string
* /
target : "_self" ,
/ * *
2019-07-17 20:08:50 +00:00
* The node type
* @ property _type
* @ private
* @ type string
* @ default "TextNode"
* /
_type : "TextNode" ,
/ * *
* Sets up the node label
* @ method setUpLabel
* @ param oData string containing the label , or an object with a label property
* /
setUpLabel : function ( oData ) {
if ( Lang . isString ( oData ) ) {
oData = {
label : oData
} ;
} else {
2019-07-17 20:16:19 +00:00
if ( oData . style ) {
this . labelStyle = oData . style ;
}
2019-07-17 20:08:50 +00:00
}
this . label = oData . label ;
this . labelElId = "ygtvlabelel" + this . index ;
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
} ,
/ * *
* Returns the label element
* @ for YAHOO . widget . TextNode
* @ method getLabelEl
* @ return { object } the element
* /
getLabelEl : function ( ) {
return Dom . get ( this . labelElId ) ;
} ,
// overrides YAHOO.widget.Node
2019-07-17 20:16:19 +00:00
getContentHtml : function ( ) {
2019-07-17 20:08:50 +00:00
var sb = [ ] ;
sb [ sb . length ] = this . href ? '<a' : '<span' ;
sb [ sb . length ] = ' id="' + this . labelElId + '"' ;
2019-07-17 20:16:19 +00:00
sb [ sb . length ] = ' class="' + this . labelStyle + '"' ;
if ( this . href ) {
sb [ sb . length ] = ' href="' + this . href + '"' ;
sb [ sb . length ] = ' target="' + this . target + '"' ;
}
2019-07-17 20:08:50 +00:00
if ( this . title ) {
sb [ sb . length ] = ' title="' + this . title + '"' ;
}
sb [ sb . length ] = ' >' ;
sb [ sb . length ] = this . label ;
sb [ sb . length ] = this . href ? '</a>' : '</span>' ;
return sb . join ( "" ) ;
} ,
/ * *
* Returns an object which could be used to build a tree out of this node and its children .
* It can be passed to the tree constructor to reproduce this node as a tree .
* It will return false if the node or any descendant loads dynamically , regardless of whether it is loaded or not .
* @ method getNodeDefinition
* @ return { Object | false } definition of the tree or false if this node or any descendant is defined as dynamic
* /
getNodeDefinition : function ( ) {
2019-07-17 20:16:19 +00:00
var def = YAHOO . widget . TextNode . superclass . getNodeDefinition . call ( this ) ;
if ( def === false ) { return false ; }
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
// Node specific properties
def . label = this . label ;
if ( this . labelStyle != 'ygtvlabel' ) { def . style = this . labelStyle ; }
if ( this . title ) { def . title = this . title ; }
if ( this . href ) { def . href = this . href ; }
if ( this . target != '_self' ) { def . target = this . target ; }
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
return def ;
} ,
2019-07-17 20:08:50 +00:00
toString : function ( ) {
return YAHOO . widget . TextNode . superclass . toString . call ( this ) + ": " + this . label ;
} ,
// deprecated
onLabelClick : function ( ) {
2019-07-17 20:16:19 +00:00
return false ;
} ,
refresh : function ( ) {
YAHOO . widget . TextNode . superclass . refresh . call ( this ) ;
var label = this . getLabelEl ( ) ;
label . innerHTML = this . label ;
if ( label . tagName . toUpperCase ( ) == 'A' ) {
label . href = this . href ;
label . target = this . target ;
}
2019-07-17 20:08:50 +00:00
}
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
} ) ;
} ) ( ) ;
/ * *
2019-07-17 20:16:19 +00:00
* A menu - specific implementation that differs from TextNode in that only
* one sibling can be expanded at a time .
2019-07-17 20:08:50 +00:00
* @ namespace YAHOO . widget
2019-07-17 20:16:19 +00:00
* @ class MenuNode
* @ extends YAHOO . widget . TextNode
* @ param oData { object } a string or object containing the data that will
* be used to render this node .
* Providing a string is the same as providing an object with a single property named label .
* All values in the oData will be used to set equally named properties in the node
* as long as the node does have such properties , they are not undefined , private or functions .
* All attributes are made available in noderef . data , which
* can be used to store custom attributes . TreeView . getNode ( s ) ByProperty
* can be used to retrieve a node by one of the attributes .
* @ param oParent { YAHOO . widget . Node } this node ' s parent node
* @ param expanded { boolean } the initial expanded / collapsed state ( deprecated ; use oData . expanded )
2019-07-17 20:08:50 +00:00
* @ constructor
* /
2019-07-17 20:16:19 +00:00
YAHOO . widget . MenuNode = function ( oData , oParent , expanded ) {
YAHOO . widget . MenuNode . superclass . constructor . call ( this , oData , oParent , expanded ) ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
/ *
* Menus usually allow only one branch to be open at a time .
2019-07-17 20:08:50 +00:00
* /
2019-07-17 20:16:19 +00:00
this . multiExpand = false ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
} ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
YAHOO . extend ( YAHOO . widget . MenuNode , YAHOO . widget . TextNode , {
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
/ * *
* The node type
* @ property _type
* @ private
* @ default "MenuNode"
2019-07-17 20:08:50 +00:00
* /
2019-07-17 20:16:19 +00:00
_type : "MenuNode"
2019-07-17 20:08:50 +00:00
} ) ;
( function ( ) {
2019-07-17 20:16:19 +00:00
var Dom = YAHOO . util . Dom ,
Lang = YAHOO . lang ,
Event = YAHOO . util . Event ;
2019-07-17 20:08:50 +00:00
/ * *
* This implementation takes either a string or object for the
* oData argument . If is it a string , it will use it for the display
* of this node ( and it can contain any html code ) . If the parameter
* is an object , it looks for a parameter called "html" that will be
* used for this node ' s display .
* @ namespace YAHOO . widget
* @ class HTMLNode
* @ extends YAHOO . widget . Node
* @ constructor
* @ param oData { object } a string or object containing the data that will
* be used to render this node .
* Providing a string is the same as providing an object with a single property named html .
* All values in the oData will be used to set equally named properties in the node
* as long as the node does have such properties , they are not undefined , private or functions .
* All other attributes are made available in noderef . data , which
* can be used to store custom attributes . TreeView . getNode ( s ) ByProperty
* can be used to retrieve a node by one of the attributes .
* @ param oParent { YAHOO . widget . Node } this node ' s parent node
* @ param expanded { boolean } the initial expanded / collapsed state ( deprecated ; use oData . expanded )
* @ param hasIcon { boolean } specifies whether or not leaf nodes should
* be rendered with or without a horizontal line line and / or toggle icon . If the icon
* is not displayed , the content fills the space it would have occupied .
* This option operates independently of the leaf node presentation logic
* for dynamic nodes .
* ( deprecated ; use oData . hasIcon )
* /
YAHOO . widget . HTMLNode = function ( oData , oParent , expanded , hasIcon ) {
if ( oData ) {
this . init ( oData , oParent , expanded ) ;
this . initContent ( oData , hasIcon ) ;
}
} ;
YAHOO . extend ( YAHOO . widget . HTMLNode , YAHOO . widget . Node , {
/ * *
* The CSS class for the html content container . Defaults to ygtvhtml , but
* can be overridden to provide a custom presentation for a specific node .
* @ property contentStyle
* @ type string
* /
contentStyle : "ygtvhtml" ,
/ * *
* The HTML content to use for this node ' s display
* @ property html
* @ type string
* /
html : null ,
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
/ * *
* The node type
* @ property _type
* @ private
* @ type string
* @ default "HTMLNode"
* /
_type : "HTMLNode" ,
/ * *
* Sets up the node label
* @ property initContent
* @ param oData { object } An html string or object containing an html property
* @ param hasIcon { boolean } determines if the node will be rendered with an
* icon or not
* /
initContent : function ( oData , hasIcon ) {
this . setHtml ( oData ) ;
this . contentElId = "ygtvcontentel" + this . index ;
2019-07-17 20:16:19 +00:00
if ( ! Lang . isUndefined ( hasIcon ) ) { this . hasIcon = hasIcon ; }
2019-07-17 20:08:50 +00:00
} ,
/ * *
* Synchronizes the node . data , node . html , and the node ' s content
* @ property setHtml
* @ param o { object } An html string or object containing an html property
* /
setHtml : function ( o ) {
this . html = ( typeof o === "string" ) ? o : o . html ;
var el = this . getContentEl ( ) ;
if ( el ) {
el . innerHTML = this . html ;
}
} ,
// overrides YAHOO.widget.Node
getContentHtml : function ( ) {
return this . html ;
} ,
2019-07-17 20:16:19 +00:00
/ * *
2019-07-17 20:08:50 +00:00
* Returns an object which could be used to build a tree out of this node and its children .
* It can be passed to the tree constructor to reproduce this node as a tree .
* It will return false if any node loads dynamically , regardless of whether it is loaded or not .
* @ method getNodeDefinition
* @ return { Object | false } definition of the tree or false if any node is defined as dynamic
* /
getNodeDefinition : function ( ) {
2019-07-17 20:16:19 +00:00
var def = YAHOO . widget . HTMLNode . superclass . getNodeDefinition . call ( this ) ;
if ( def === false ) { return false ; }
def . html = this . html ;
return def ;
}
2019-07-17 20:08:50 +00:00
} ) ;
} ) ( ) ;
( function ( ) {
2019-07-17 20:16:19 +00:00
var Dom = YAHOO . util . Dom ,
Lang = YAHOO . lang ,
Event = YAHOO . util . Event ,
Calendar = YAHOO . widget . Calendar ;
2019-07-17 20:08:50 +00:00
/ * *
* A Date - specific implementation that differs from TextNode in that it uses
* YAHOO . widget . Calendar as an in - line editor , if available
* If Calendar is not available , it behaves as a plain TextNode .
* @ namespace YAHOO . widget
* @ class DateNode
* @ extends YAHOO . widget . TextNode
* @ param oData { object } a string or object containing the data that will
* be used to render this node .
* Providing a string is the same as providing an object with a single property named label .
* All values in the oData will be used to set equally named properties in the node
* as long as the node does have such properties , they are not undefined , private nor functions .
* All attributes are made available in noderef . data , which
* can be used to store custom attributes . TreeView . getNode ( s ) ByProperty
* can be used to retrieve a node by one of the attributes .
* @ param oParent { YAHOO . widget . Node } this node ' s parent node
* @ param expanded { boolean } the initial expanded / collapsed state ( deprecated ; use oData . expanded )
* @ constructor
* /
YAHOO . widget . DateNode = function ( oData , oParent , expanded ) {
2019-07-17 20:16:19 +00:00
YAHOO . widget . DateNode . superclass . constructor . call ( this , oData , oParent , expanded ) ;
2019-07-17 20:08:50 +00:00
} ;
YAHOO . extend ( YAHOO . widget . DateNode , YAHOO . widget . TextNode , {
/ * *
* The node type
* @ property _type
* @ type string
* @ private
* @ default "DateNode"
* /
_type : "DateNode" ,
2019-07-17 20:16:19 +00:00
/ * *
* Configuration object for the Calendar editor , if used .
* See < a href = "http://developer.yahoo.com/yui/calendar/#internationalization" > http : //developer.yahoo.com/yui/calendar/#internationalization</a>
* @ property calendarConfig
* /
calendarConfig : null ,
/ * *
* If YAHOO . widget . Calendar is available , it will pop up a Calendar to enter a new date . Otherwise , it falls back to a plain & lt ; input & gt ; textbox
* @ method fillEditorContainer
* @ param editorData { YAHOO . widget . TreeView . editorData } a shortcut to the static object holding editing information
* @ return void
* /
fillEditorContainer : function ( editorData ) {
var cal , container = editorData . inputContainer ;
if ( Lang . isUndefined ( Calendar ) ) {
Dom . replaceClass ( editorData . editorPanel , 'ygtv-edit-DateNode' , 'ygtv-edit-TextNode' ) ;
YAHOO . widget . DateNode . superclass . fillEditorContainer . call ( this , editorData ) ;
return ;
}
if ( editorData . nodeType != this . _type ) {
editorData . nodeType = this . _type ;
editorData . saveOnEnter = false ;
editorData . node . destroyEditorContents ( editorData ) ;
editorData . inputObject = cal = new Calendar ( container . appendChild ( document . createElement ( 'div' ) ) ) ;
if ( this . calendarConfig ) {
cal . cfg . applyConfig ( this . calendarConfig , true ) ;
cal . cfg . fireQueue ( ) ;
}
cal . selectEvent . subscribe ( function ( ) {
this . tree . _closeEditor ( true ) ;
} , this , true ) ;
} else {
cal = editorData . inputObject ;
}
cal . cfg . setProperty ( "selected" , this . label , false ) ;
var delim = cal . cfg . getProperty ( 'DATE_FIELD_DELIMITER' ) ;
var pageDate = this . label . split ( delim ) ;
cal . cfg . setProperty ( 'pagedate' , pageDate [ cal . cfg . getProperty ( 'MDY_MONTH_POSITION' ) - 1 ] + delim + pageDate [ cal . cfg . getProperty ( 'MDY_YEAR_POSITION' ) - 1 ] ) ;
cal . cfg . fireQueue ( ) ;
cal . render ( ) ;
cal . oDomContainer . focus ( ) ;
} ,
/ * *
* Saves the date entered in the editor into the DateNode label property and displays it .
* Overrides Node . saveEditorValue
* @ method saveEditorValue
* @ param editorData { YAHOO . widget . TreeView . editorData } a shortcut to the static object holding editing information
* /
saveEditorValue : function ( editorData ) {
var node = editorData . node ,
validator = node . tree . validator ,
value ;
if ( Lang . isUndefined ( Calendar ) ) {
value = editorData . inputElement . value ;
} else {
var cal = editorData . inputObject ,
date = cal . getSelectedDates ( ) [ 0 ] ,
dd = [ ] ;
dd [ cal . cfg . getProperty ( 'MDY_DAY_POSITION' ) - 1 ] = date . getDate ( ) ;
dd [ cal . cfg . getProperty ( 'MDY_MONTH_POSITION' ) - 1 ] = date . getMonth ( ) + 1 ;
dd [ cal . cfg . getProperty ( 'MDY_YEAR_POSITION' ) - 1 ] = date . getFullYear ( ) ;
value = dd . join ( cal . cfg . getProperty ( 'DATE_FIELD_DELIMITER' ) ) ;
}
if ( Lang . isFunction ( validator ) ) {
value = validator ( value , node . label , node ) ;
if ( Lang . isUndefined ( value ) ) { return false ; }
}
node . label = value ;
node . getLabelEl ( ) . innerHTML = value ;
} ,
/ * *
* Returns an object which could be used to build a tree out of this node and its children .
* It can be passed to the tree constructor to reproduce this node as a tree .
* It will return false if the node or any descendant loads dynamically , regardless of whether it is loaded or not .
* @ method getNodeDefinition
* @ return { Object | false } definition of the node or false if this node or any descendant is defined as dynamic
* /
getNodeDefinition : function ( ) {
var def = YAHOO . widget . DateNode . superclass . getNodeDefinition . call ( this ) ;
if ( def === false ) { return false ; }
if ( this . calendarConfig ) { def . calendarConfig = this . calendarConfig ; }
return def ;
}
2019-07-17 20:08:50 +00:00
} ) ;
} ) ( ) ;
( function ( ) {
2019-07-17 20:16:19 +00:00
var Dom = YAHOO . util . Dom ,
Lang = YAHOO . lang ,
Event = YAHOO . util . Event ,
TV = YAHOO . widget . TreeView ,
TVproto = TV . prototype ;
/ * *
* An object to store information used for in - line editing
* for all Nodes of all TreeViews . It contains :
* < ul >
* < li > active { boolean } , whether there is an active cell editor < / l i >
* < li > whoHasIt { YAHOO . widget . TreeView } TreeView instance that is currently using the editor < / l i >
* < li > nodeType { string } value of static Node . _type property , allows reuse of input element if node is of the same type . < / l i >
* < li > editorPanel { HTMLelement ( & lt ; div & gt ; ) } element holding the in - line editor < / l i >
* < li > inputContainer { HTMLelement ( & lt ; div & gt ; ) } element which will hold the type - specific input element ( s ) to be filled by the fillEditorContainer method < / l i >
* < li > buttonsContainer { HTMLelement ( & lt ; div & gt ; ) } element which holds the & lt ; button & gt ; elements for Ok / Cancel . If you don 't want any of the buttons, hide it via CSS styles, don' t destroy it < / l i >
* < li > node { YAHOO . widget . Node } reference to the Node being edited < / l i >
* < li > saveOnEnter { boolean } , whether the Enter key should be accepted as a Save command ( Esc . is always taken as Cancel ) , disable for multi - line input elements < / l i >
* < / u l >
* Editors are free to use this object to store additional data .
* @ property editorData
* @ static
* @ for YAHOO . widget . TreeView
* /
TV . editorData = {
active : false ,
whoHasIt : null , // which TreeView has it
nodeType : null ,
editorPanel : null ,
inputContainer : null ,
buttonsContainer : null ,
node : null , // which Node is being edited
saveOnEnter : true
// Each node type is free to add its own properties to this as it sees fit.
} ;
/ * *
* Validator function for edited data , called from the TreeView instance scope ,
* receives the arguments ( newValue , oldValue , nodeInstance )
* and returns either the validated ( or type - converted ) value or undefined .
* An undefined return will prevent the editor from closing
* @ property validator
* @ default null
* @ for YAHOO . widget . TreeView
* /
TVproto . validator = null ;
/ * *
* Entry point of the editing plug - in .
* TreeView will call this method if it exists when a node label is clicked
* @ method _nodeEditing
* @ param node { YAHOO . widget . Node } the node to be edited
* @ return { Boolean } true to indicate that the node is editable and prevent any further bubbling of the click .
* @ for YAHOO . widget . TreeView
* @ private
* /
TVproto . _nodeEditing = function ( node ) {
if ( node . fillEditorContainer && node . editable ) {
var ed , topLeft , buttons , button , editorData = TV . editorData ;
editorData . active = true ;
editorData . whoHasIt = this ;
if ( ! editorData . nodeType ) {
editorData . editorPanel = ed = document . body . appendChild ( document . createElement ( 'div' ) ) ;
Dom . addClass ( ed , 'ygtv-label-editor' ) ;
buttons = editorData . buttonsContainer = ed . appendChild ( document . createElement ( 'div' ) ) ;
Dom . addClass ( buttons , 'ygtv-button-container' ) ;
button = buttons . appendChild ( document . createElement ( 'button' ) ) ;
Dom . addClass ( button , 'ygtvok' ) ;
button . innerHTML = ' ' ;
button = buttons . appendChild ( document . createElement ( 'button' ) ) ;
Dom . addClass ( button , 'ygtvcancel' ) ;
button . innerHTML = ' ' ;
Event . on ( buttons , 'click' , function ( ev ) {
var target = Event . getTarget ( ev ) ;
var node = TV . editorData . node ;
if ( Dom . hasClass ( target , 'ygtvok' ) ) {
Event . stopEvent ( ev ) ;
this . _closeEditor ( true ) ;
}
if ( Dom . hasClass ( target , 'ygtvcancel' ) ) {
Event . stopEvent ( ev ) ;
this . _closeEditor ( false ) ;
}
} , this , true ) ;
editorData . inputContainer = ed . appendChild ( document . createElement ( 'div' ) ) ;
Dom . addClass ( editorData . inputContainer , 'ygtv-input' ) ;
Event . on ( ed , 'keydown' , function ( ev ) {
var editorData = TV . editorData ,
KEY = YAHOO . util . KeyListener . KEY ;
switch ( ev . keyCode ) {
case KEY . ENTER :
Event . stopEvent ( ev ) ;
if ( editorData . saveOnEnter ) {
this . _closeEditor ( true ) ;
}
break ;
case KEY . ESCAPE :
Event . stopEvent ( ev ) ;
this . _closeEditor ( false ) ;
break ;
}
} , this , true ) ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
} else {
2019-07-17 20:16:19 +00:00
ed = editorData . editorPanel ;
2019-07-17 20:08:50 +00:00
}
2019-07-17 20:16:19 +00:00
editorData . node = node ;
if ( editorData . nodeType ) {
Dom . removeClass ( ed , 'ygtv-edit-' + editorData . nodeType ) ;
}
Dom . addClass ( ed , ' ygtv-edit-' + node . _type ) ;
topLeft = Dom . getXY ( node . getContentEl ( ) ) ;
Dom . setStyle ( ed , 'left' , topLeft [ 0 ] + 'px' ) ;
Dom . setStyle ( ed , 'top' , topLeft [ 1 ] + 'px' ) ;
Dom . setStyle ( ed , 'display' , 'block' ) ;
ed . focus ( ) ;
node . fillEditorContainer ( editorData ) ;
return true ; // If inline editor available, don't do anything else.
2019-07-17 20:08:50 +00:00
}
} ;
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Method to be associated with an event ( clickEvent , dblClickEvent or enterKeyPressed ) to pop up the contents editor
* It calls the corresponding node editNode method .
* @ method onEventEditNode
* @ param oArgs { object } Object passed as arguments to TreeView event listeners
* @ for YAHOO . widget . TreeView
* /
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
TVproto . onEventEditNode = function ( oArgs ) {
if ( oArgs instanceof YAHOO . widget . Node ) {
oArgs . editNode ( ) ;
} else if ( oArgs . node instanceof YAHOO . widget . Node ) {
oArgs . node . editNode ( ) ;
}
} ;
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Method to be called when the inline editing is finished and the editor is to be closed
* @ method _closeEditor
* @ param save { Boolean } true if the edited value is to be saved , false if discarded
* @ private
* @ for YAHOO . widget . TreeView
* /
TVproto . _closeEditor = function ( save ) {
var ed = TV . editorData ,
node = ed . node ,
close = true ;
if ( save ) {
close = ed . node . saveEditorValue ( ed ) !== false ;
}
if ( close ) {
Dom . setStyle ( ed . editorPanel , 'display' , 'none' ) ;
ed . active = false ;
node . focus ( ) ;
}
} ;
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Entry point for TreeView ' s destroy method to destroy whatever the editing plug - in has created
* @ method _destroyEditor
* @ private
* @ for YAHOO . widget . TreeView
* /
TVproto . _destroyEditor = function ( ) {
var ed = TV . editorData ;
if ( ed && ed . nodeType && ( ! ed . active || ed . whoHasIt === this ) ) {
Event . removeListener ( ed . editorPanel , 'keydown' ) ;
Event . removeListener ( ed . buttonContainer , 'click' ) ;
ed . node . destroyEditorContents ( ed ) ;
document . body . removeChild ( ed . editorPanel ) ;
ed . nodeType = ed . editorPanel = ed . inputContainer = ed . buttonsContainer = ed . whoHasIt = ed . node = null ;
ed . active = false ;
}
} ;
var Nproto = YAHOO . widget . Node . prototype ;
/ * *
* Signals if the label is editable . ( Ignored on TextNodes with href set . )
* @ property editable
* @ type boolean
* @ for YAHOO . widget . Node
* /
Nproto . editable = false ;
/ * *
* pops up the contents editor , if there is one and the node is declared editable
* @ method editNode
* @ for YAHOO . widget . Node
* /
Nproto . editNode = function ( ) {
this . tree . _nodeEditing ( this ) ;
} ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
/ * * P l a c e h o l d e r f o r a f u n c t i o n t h a t s h o u l d p r o v i d e t h e i n l i n e n o d e l a b e l e d i t o r .
* Leaving it set to null will indicate that this node type is not editable .
* It should be overridden by nodes that provide inline editing .
* The Node - specific editing element ( input box , textarea or whatever ) should be inserted into editorData . inputContainer .
* @ method fillEditorContainer
* @ param editorData { YAHOO . widget . TreeView . editorData } a shortcut to the static object holding editing information
* @ return void
* @ for YAHOO . widget . Node
2019-07-17 20:08:50 +00:00
* /
2019-07-17 20:16:19 +00:00
Nproto . fillEditorContainer = null ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Node - specific destroy function to empty the contents of the inline editor panel
* This function is the worst case alternative that will purge all possible events and remove the editor contents
* Method Event . purgeElement is somewhat costly so if it can be replaced by specifc Event . removeListeners , it is better to do so .
* @ method destroyEditorContents
* @ param editorData { YAHOO . widget . TreeView . editorData } a shortcut to the static object holding editing information
* @ for YAHOO . widget . Node
* /
Nproto . destroyEditorContents = function ( editorData ) {
// In the worst case, if the input editor (such as the Calendar) has no destroy method
// we can only try to remove all possible events on it.
Event . purgeElement ( editorData . inputContainer , true ) ;
editorData . inputContainer . innerHTML = '' ;
} ;
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Saves the value entered into the editor .
* Should be overridden by each node type
* @ method saveEditorValue
* @ param editorData { YAHOO . widget . TreeView . editorData } a shortcut to the static object holding editing information
* @ return a return of exactly false will prevent the editor from closing
* @ for YAHOO . widget . Node
2019-07-17 20:08:50 +00:00
* /
2019-07-17 20:16:19 +00:00
Nproto . saveEditorValue = function ( editorData ) {
} ;
var TNproto = YAHOO . widget . TextNode . prototype ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
/ * *
* Places an & lt ; input & gt ; textbox in the input container and loads the label text into it
* @ method fillEditorContainer
* @ param editorData { YAHOO . widget . TreeView . editorData } a shortcut to the static object holding editing information
* @ return void
* @ for YAHOO . widget . TextNode
2019-07-17 20:08:50 +00:00
* /
2019-07-17 20:16:19 +00:00
TNproto . fillEditorContainer = function ( editorData ) {
var input ;
// If last node edited is not of the same type as this one, delete it and fill it with our editor
if ( editorData . nodeType != this . _type ) {
editorData . nodeType = this . _type ;
editorData . saveOnEnter = true ;
editorData . node . destroyEditorContents ( editorData ) ;
editorData . inputElement = input = editorData . inputContainer . appendChild ( document . createElement ( 'input' ) ) ;
} else {
// if the last node edited was of the same time, reuse the input element.
input = editorData . inputElement ;
}
2019-07-17 20:08:50 +00:00
2019-07-17 20:16:19 +00:00
input . value = this . label ;
input . focus ( ) ;
input . select ( ) ;
} ;
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Saves the value entered in the editor into the TextNode label property and displays it
* Overrides Node . saveEditorValue
* @ method saveEditorValue
* @ param editorData { YAHOO . widget . TreeView . editorData } a shortcut to the static object holding editing information
* @ for YAHOO . widget . TextNode
2019-07-17 20:08:50 +00:00
* /
2019-07-17 20:16:19 +00:00
TNproto . saveEditorValue = function ( editorData ) {
var node = editorData . node ,
value = editorData . inputElement . value ,
validator = node . tree . validator ;
if ( Lang . isFunction ( validator ) ) {
value = validator ( value , node . label , node ) ;
if ( Lang . isUndefined ( value ) ) { return false ; }
}
node . label = value ;
node . getLabelEl ( ) . innerHTML = value ;
} ;
2019-07-17 20:08:50 +00:00
/ * *
2019-07-17 20:16:19 +00:00
* Destroys the contents of the inline editor panel
* Overrides Node . destroyEditorContent
* Since we didn ' t set any event listeners on this inline editor , it is more efficient to avoid the generic method in Node
* @ method destroyEditorContents
* @ param editorData { YAHOO . widget . TreeView . editorData } a shortcut to the static object holding editing information
* @ for YAHOO . widget . TextNode
2019-07-17 20:08:50 +00:00
* /
2019-07-17 20:16:19 +00:00
TNproto . destroyEditorContents = function ( editorData ) {
editorData . inputContainer . innerHTML = '' ;
} ;
} ) ( ) ;
YAHOO . register ( "treeview" , YAHOO . widget . TreeView , { version : "2.7.0" , build : "1799" } ) ;