Вход Регистрация
Файл: concrete5.7.5.6/concrete/js/build/vendor/dynatree/dynatree.js
Строк: 4202
<?php
/*! jQuery Dynatree Plugin - v1.2.4 - 2013-02-12
* http://dynatree.googlecode.com/
* Copyright (c) 2013 Martin Wendt; Licensed MIT, GPL */

/* jsHint options*/
// Note: We currently allow eval() to parse the 'data' attribtes, when initializing from HTML.
// TODO: pass jsHint with the options given in grunt.js only.
//       The following should not be required:
/*global alert */
/*jshint nomen:false, smarttabs:true, eqeqeq:false, evil:true, regexp:false */

/*************************************************************************
 *    Debug functions
 */

var _canLog true;

function 
_log(modemsg) {
    
/**
     * Usage: logMsg("%o was toggled", this);
     */
    
if( !_canLog ){
        return;
    }
    
// Remove first argument
    
var args = Array.prototype.slice.apply(arguments, [1]);
    
// Prepend timestamp
    
var dt = new Date();
    var 
tag dt.getHours()+":"+dt.getMinutes()+":"+dt.getSeconds()+"."+dt.getMilliseconds();
    
args[0] = tag " - " args[0];

    try {
        switch( 
mode ) {
        case 
"info":
            
window.console.info.apply(window.consoleargs);
            break;
        case 
"warn":
            
window.console.warn.apply(window.consoleargs);
            break;
        default:
            
window.console.log.apply(window.consoleargs);
            break;
        }
    } catch(
e) {
        if( !
window.console ){
            
_canLog false// Permanently disable, when logging is not supported by the browser
        
}else if(e.number === -2146827850){
            
// fix for IE8, where window.console.log() exists, but does not support .apply()
            
window.console.log(args.join(", "));
        }
    }
}

/* Check browser version, since $.browser was removed in jQuery 1.9 */
function _checkBrowser(){
    var 
matchedbrowser;
    function 
uaMatchua ) {
        
ua ua.toLowerCase();
        var 
match = /(chrome)[ /]([w.]+)/.execua ) ||
             /(
webkit)[ /]([w.]+)/.execua ) ||
             /(
opera)(?:.*version|)[ /]([w.]+)/.execua ) ||
             /(
msie) ([w.]+)/.execua ) ||
             
ua.indexOf("compatible") < && /(mozilla)(?:.*? rv:([w.]+)|)/.execua ) ||
             [];
        return {
            
browsermatch] || "",
            
versionmatch] || "0"
        
};
    }
    
matched uaMatchnavigator.userAgent );
    
browser = {};
     if ( 
matched.browser ) {
         
browsermatched.browser ] = true;
         
browser.version matched.version;
     }
     if ( 
browser.chrome ) {
         
browser.webkit true;
     } else if ( 
browser.webkit ) {
         
browser.safari true;
     }
     return 
browser;
}
var 
BROWSER jQuery.browser || _checkBrowser();

function 
logMsg(msg) {
    Array.
prototype.unshift.apply(arguments, ["debug"]);
    
_log.apply(thisarguments);
}


// Forward declaration
var getDynaTreePersistData null;



/*************************************************************************
 *    Constants
 */
var DTNodeStatus_Error   = -1;
var 
DTNodeStatus_Loading 1;
var 
DTNodeStatus_Ok      0;


// Start of local namespace
(function($) {

/*************************************************************************
 *    Common tool functions.
 */

var Class = {
    
create: function() {
        return function() {
            
this.initialize.apply(thisarguments);
        };
    }
};

// Tool function to get dtnode from the event target:
function getDtNodeFromElement(el) {
    
alert("getDtNodeFromElement is deprecated");
    return $.
ui.dynatree.getNode(el);
/*
    var iMax = 5;
    while( el && iMax-- ) {
        if(el.dtnode) { return el.dtnode; }
        el = el.parentNode;
    }
    return null;
*/
}

function 
noop() {
}

/** Compare two dotted version strings (like '10.2.3').
 * @returns {Integer} 0: v1 == v2, -1: v1 < v2, 1: v1 > v2
 */
function versionCompare(v1v2) {
    var 
v1parts = ("" v1).split("."),
        
v2parts = ("" v2).split("."),
        
minLength Math.min(v1parts.lengthv2parts.length),
        
p1p2i;
    
// Compare tuple pair-by-pair.
    
for(0minLengthi++) {
        
// Convert to integer if possible, because "8" > "10".
        
p1 parseInt(v1parts[i], 10);
        
p2 parseInt(v2parts[i], 10);
        if (
isNaN(p1)){ p1 v1parts[i]; }
        if (
isNaN(p2)){ p2 v2parts[i]; }
        if (
p1 == p2) {
            continue;
        }else if (
p1 p2) {
            return 
1;
        }else if (
p1 p2) {
            return -
1;
        }
        
// one operand is NaN
        
return NaN;
    }
    
// The longer tuple is always considered 'greater'
    
if (v1parts.length === v2parts.length) {
        return 
0;
    }
    return (
v1parts.length v2parts.length) ? -1;
}


/*************************************************************************
 *    Class DynaTreeNode
 */
var DynaTreeNode = Class.create();

DynaTreeNode.prototype = {
    
initialize: function(parenttreedata) {
        
/**
         * @constructor
         */
        
this.parent parent;
        
this.tree tree;
        if ( 
typeof data === "string" ){
            
data = { titledata };
        }
        if( !
data.key ){
            
data.key "_" tree._nodeCount++;
        }else{
            
data.key "" data.key// issue 371
        
}
        
this.data = $.extend({}, $.ui.dynatree.nodedatadefaultsdata);
        
this.li null// not yet created
        
this.span null// not yet created
        
this.ul null// not yet created
        
this.childList null// no subnodes yet
        
this._isLoading false// Lazy content is being loaded
        
this.hasSubSel false;
        
this.bExpanded false;
        
this.bSelected false;

    },

    
toString: function() {
        return 
"DynaTreeNode<" this.data.key ">: '" this.data.title "'";
    },

    
toDict: function(recursivecallback) {
        var 
dict = $.extend({}, this.data);
        
dict.activate = ( this.tree.activeNode === this );
        
dict.focus = ( this.tree.focusNode === this );
        
dict.expand this.bExpanded;
        
dict.select this.bSelected;
        if( 
callback ){
            
callback(dict);
        }
        if( 
recursive && this.childList ) {
            
dict.children = [];
            for(var 
i=0l=this.childList.lengthi<li++ ){
                
dict.children.push(this.childList[i].toDict(truecallback));
            }
        } else {
            
delete dict.children;
        }
        return 
dict;
    },

    
fromDict: function(dict) {
        
/**
         * Update node data. If dict contains 'children', then also replace
         * the hole sub tree.
         */
        
var children dict.children;
        if(
children === undefined){
            
this.data = $.extend(this.datadict);
            
this.render();
            return;
        }
        
dict = $.extend({}, dict);
        
dict.children undefined;
        
this.data = $.extend(this.datadict);
        
this.removeChildren();
        
this.addChild(children);
    },

    
_getInnerHtml: function() {
        var 
tree this.tree,
            
opts tree.options,
            
cache tree.cache,
            
level this.getLevel(),
            
data this.data,
            
res "",
            
imageSrc;
        
// connector (expanded, expandable or simple)
        
if( level opts.minExpandLevel ) {
            if(
level 1){
                
res += cache.tagConnector;
            }
            
// .. else (i.e. for root level) skip expander/connector altogether
        
} else if( this.hasChildren() !== false ) {
            
res += cache.tagExpander;
        } else {
            
res += cache.tagConnector;
        }
        
// Checkbox mode
        
if( opts.checkbox && data.hideCheckbox !== true && !data.isStatusNode ) {
            
res += cache.tagCheckbox;
        }
        
// folder or doctype icon
        
if ( data.icon ) {
            if (
data.iconHTML) {
                
res += data.iconHTML;
            } else {
                if (
data.icon.charAt(0) === "/") {
                    
imageSrc data.icon;
                } else {
                    
imageSrc opts.imagePath data.icon;
                }
                
res += "<img src='" imageSrc "' alt='' />";
            }
        } else if ( 
data.icon === false ) {
            
// icon == false means 'no icon'
//            noop(); // keep JSLint happy
        
} else if ( data.iconClass ) {
            
res +=  "<span class='" " " data.iconClass +  "'></span>";
        } else {
            
// icon == null means 'default icon'
            
res += cache.tagNodeIcon;
        }
        
// node title
        
var nodeTitle "";
        if ( 
opts.onCustomRender ){
            
nodeTitle opts.onCustomRender.call(treethis) || "";
        }
        if(!
nodeTitle){
            var 
tooltip data.tooltip ' title="' data.tooltip.replace(/"/g, '&quot;') + '"' : '',
                href = data.href || "#";
            if( opts.noLink || data.noLink ) {
                nodeTitle = '
<span style="display:inline-block;" class="' + opts.classNames.title + '"' + tooltip + '>' + data.title + '</span>';
//                this.tree.logDebug("nodeTitle: " + nodeTitle);
            } else {
                nodeTitle = '
<a href="' + href + '" class="' + opts.classNames.title + '"' + tooltip + '>' + data.title + '</a>';
            }
        }
        res += nodeTitle;
        return res;
    },


    _fixOrder: function() {
        /**
         * Make sure, that <li> order matches childList order.
         */
        var cl = this.childList;
        if( !cl || !this.ul ){
            return;
        }
        var childLI = this.ul.firstChild;
        for(var i=0, l=cl.length-1; i<l; i++) {
            var childNode1 = cl[i];
            var childNode2 = childLI.dtnode;
            if( childNode1 !== childNode2 ) {
                this.tree.logDebug("_fixOrder: mismatch at index " + i + ": " + childNode1 + " != " + childNode2);
                this.ul.insertBefore(childNode1.li, childNode2.li);
            } else {
                childLI = childLI.nextSibling;
            }
        }
    },


    render: function(useEffects, includeInvisible) {
        /**
         * Create <li><span>..</span> .. </li> tags for this node.
         *
         * <li id='
KEY' dtnode=NODE> // This div contains the node's span and list of child div's.
         *   <span class='
title'>S S S A</span> // Span contains graphic spans and title <a> tag
         *   <ul> // only present, when node has children
         *       <li id='
KEY' dtnode=NODE>child1</li>
         *       <li id='
KEY' dtnode=NODE>child2</li>
         *   </ul>
         * </li>
         */
//        this.tree.logDebug("%s.render(%s)", this, useEffects);
        // ---
        var tree = this.tree,
            parent = this.parent,
            data = this.data,
            opts = tree.options,
            cn = opts.classNames,
            isLastSib = this.isLastSibling(),
            firstTime = false;

        if( !parent && !this.ul ) {
            // Root node has only a <ul>
            this.li = this.span = null;
            this.ul = document.createElement("ul");
            if( opts.minExpandLevel > 1 ){
                this.ul.className = cn.container + " " + cn.noConnector;
            }else{
                this.ul.className = cn.container;
            }
        } else if( parent ) {
            // Create <li><span /> </li>
            if( ! this.li ) {
                firstTime = true;
                this.li = document.createElement("li");
                this.li.dtnode = this;
                if( data.key && opts.generateIds ){
                    this.li.id = opts.idPrefix + data.key;
                }
                this.span = document.createElement("span");
                this.span.className = cn.title;
                this.li.appendChild(this.span);

                if( !parent.ul ) {
                    // This is the parent'
s first childcreate UL tag
                    
// (Hidden, because it will be
                    
parent.ul document.createElement("ul");
                    
parent.ul.style.display "none";
                    
parent.li.appendChild(parent.ul);
//                    if( opts.minExpandLevel > this.getLevel() ){
//                        parent.ul.className = cn.noConnector;
//                    }
                
}
                
// set node connector images, links and text
//                this.span.innerHTML = this._getInnerHtml();

                
parent.ul.appendChild(this.li);
            }
            
// set node connector images, links and text
            
this.span.innerHTML this._getInnerHtml();
            
// Set classes for current status
            
var cnList = [];
            
cnList.push(cn.node);
            if( 
data.isFolder ){
                
cnList.push(cn.folder);
            }
            if( 
this.bExpanded ){
                
cnList.push(cn.expanded);
            }
            if( 
this.hasChildren() !== false ){
                
cnList.push(cn.hasChildren);
            }
            if( 
data.isLazy && this.childList === null ){
                
cnList.push(cn.lazy);
            }
            if( 
isLastSib ){
                
cnList.push(cn.lastsib);
            }
            if( 
this.bSelected ){
                
cnList.push(cn.selected);
            }
            if( 
this.hasSubSel ){
                
cnList.push(cn.partsel);
            }
            if( 
tree.activeNode === this ){
                
cnList.push(cn.active);
            }
            if( 
data.addClass ){
                
cnList.push(data.addClass);
            }
            
// IE6 doesn't correctly evaluate multiple class names,
            // so we create combined class names that can be used in the CSS
            
cnList.push(cn.combinedExpanderPrefix
                    
+ (this.bExpanded "e" "c")
                    + (
data.isLazy && this.childList === null "d" "")
                    + (
isLastSib "l" "")
                    );
            
cnList.push(cn.combinedIconPrefix
                    
+ (this.bExpanded "e" "c")
                    + (
data.isFolder "f" "")
                    );
            
this.span.className cnList.join(" ");

            
// TODO: we should not set this in the <span> tag also, if we set it here:
            
this.li.className isLastSib cn.lastsib "";

            
// Allow tweaking, binding, after node was created for the first time
            
if(firstTime && opts.onCreate){
                
opts.onCreate.call(treethisthis.span);
            }
            
// Hide children, if node is collapsed
//            this.ul.style.display = ( this.bExpanded || !parent ) ? "" : "none";
            // Allow tweaking after node state was rendered
            
if(opts.onRender){
                
opts.onRender.call(treethisthis.span);
            }
        }
        
// Visit child nodes
        
if( (this.bExpanded || includeInvisible === true) && this.childList ) {
            for(var 
i=0l=this.childList.lengthi<li++) {
                
this.childList[i].render(falseincludeInvisible);
            }
            
// Make sure the tag order matches the child array
            
this._fixOrder();
        }
        
// Hide children, if node is collapsed
        
if( this.ul ) {
            var 
isHidden = (this.ul.style.display === "none");
            var 
isExpanded = !!this.bExpanded;
//            logMsg("isHidden:%s", isHidden);
            
if( useEffects && opts.fx && (isHidden === isExpanded) ) {
                var 
duration opts.fx.duration || 200;
                $(
this.ul).animate(opts.fxduration);
            } else {
                
this.ul.style.display = ( this.bExpanded || !parent ) ? "" "none";
            }
        }
    },
    
/** Return '/id1/id2/id3'. */
    
getKeyPath: function(excludeSelf) {
        var 
path = [];
        
this.visitParents(function(node){
            if(
node.parent){
                
path.unshift(node.data.key);
            }
        }, !
excludeSelf);
        return 
"/" path.join(this.tree.options.keyPathSeparator);
    },

    
getParent: function() {
        return 
this.parent;
    },

    
getChildren: function() {
        if(
this.hasChildren() === undefined){
            return 
undefined// Lazy node: unloaded, currently loading, or load error
        
}
        return 
this.childList;
    },

    
/** Check if node has children (returns undefined, if not sure). */
    
hasChildren: function() {
        if(
this.data.isLazy){
            if(
this.childList === null || this.childList === undefined){
                
// Not yet loaded
                
return undefined;
            }else if(
this.childList.length === 0){
                
// Loaded, but response was empty
                
return false;
            }else if(
this.childList.length === && this.childList[0].isStatusNode()){
                
// Currently loading or load error
                
return undefined;
            }
            return 
true;
        }
        return !!
this.childList;
    },

    
isFirstSibling: function() {
        var 
this.parent;
        return !
|| p.childList[0] === this;
    },

    
isLastSibling: function() {
        var 
this.parent;
        return !
|| p.childList[p.childList.length-1] === this;
    },

    
isLoading: function() {
        return !!
this._isLoading;
    },

    
getPrevSibling: function() {
        if( !
this.parent ){
            return 
null;
        }
        var 
ac this.parent.childList;
        for(var 
i=1l=ac.lengthi<li++){ // start with 1, so prev(first) = null
            
if( ac[i] === this ){
                return 
ac[i-1];
            }
        }
        return 
null;
    },

    
getNextSibling: function() {
        if( !
this.parent ){
            return 
null;
        }
        var 
ac this.parent.childList;
        for(var 
i=0l=ac.length-1i<li++){ // up to length-2, so next(last) = null
            
if( ac[i] === this ){
                return 
ac[i+1];
            }
        }
        return 
null;
    },

    
isStatusNode: function() {
        return (
this.data.isStatusNode === true);
    },

    
isChildOf: function(otherNode) {
        return (
this.parent && this.parent === otherNode);
    },

    
isDescendantOf: function(otherNode) {
        if(!
otherNode){
            return 
false;
        }
        var 
this.parent;
        while( 
) {
            if( 
=== otherNode ){
                return 
true;
            }
            
p.parent;
        }
        return 
false;
    },

    
countChildren: function() {
        var 
cl this.childList;
        if( !
cl ){
            return 
0;
        }
        var 
cl.length;
        for(var 
i=0l=ni<li++){
            var 
child cl[i];
            
+= child.countChildren();
        }
        return 
n;
    },

    
/**Sort child list by title.
     * cmd: optional compare function.
     * deep: optional: pass true to sort all descendant nodes.
     */
    
sortChildren: function(cmpdeep) {
        var 
cl this.childList;
        if( !
cl ){
            return;
        }
        
cmp cmp || function(ab) {
//            return a.data.title === b.data.title ? 0 : a.data.title > b.data.title ? 1 : -1;
            
var a.data.title.toLowerCase(),
                
b.data.title.toLowerCase();
            return 
=== : -1;
            };
        
cl.sort(cmp);
        if( 
deep ){
            for(var 
i=0l=cl.lengthi<li++){
                if( 
cl[i].childList ){
                    
cl[i].sortChildren(cmp"$norender$");
                }
            }
        }
        if( 
deep !== "$norender$" ){
            
this.render();
        }
    },

    
_setStatusNode: function(data) {
        
// Create, modify or remove the status child node (pass 'null', to remove it).
        
var firstChild = ( this.childList this.childList[0] : null );
        if( !
data ) {
            if ( 
firstChild && firstChild.isStatusNode()) {
                try{
                    
// I've seen exceptions here with loadKeyPath...
                    
if(this.ul){
                        
this.ul.removeChild(firstChild.li);
                        
firstChild.li null// avoid leaks (issue 215)
                    
}
                }catch(
e){}
                if( 
this.childList.length === ){
                    
this.childList = [];
                }else{
                    
this.childList.shift();
                }
            }
        } else if ( 
firstChild ) {
            
data.isStatusNode true;
            
data.key "_statusNode";
            
firstChild.data data;
            
firstChild.render();
        } else {
            
data.isStatusNode true;
            
data.key "_statusNode";
            
firstChild this.addChild(data);
        }
    },

    
setLazyNodeStatus: function(ltsopts) {
        var 
tooltip = (opts && opts.tooltip) ? opts.tooltip null,
            
info = (opts && opts.info) ? " (" opts.info ")" "";
        switch( 
lts ) {
            case 
DTNodeStatus_Ok:
                
this._setStatusNode(null);
                $(
this.span).removeClass(this.tree.options.classNames.nodeLoading);
                
this._isLoading false;
//                this.render();
                
if( this.tree.options.autoFocus ) {
                    if( 
this === this.tree.tnRoot && this.childList && this.childList.length 0) {
                        
// special case: using ajaxInit
                        
this.childList[0].focus();
                    } else {
                        
this.focus();
                    }
                }
                break;
            case 
DTNodeStatus_Loading:
                
this._isLoading true;
                $(
this.span).addClass(this.tree.options.classNames.nodeLoading);
                
// The root is hidden, so we set a temporary status child
                
if(!this.parent){
                    
this._setStatusNode({
                        
titlethis.tree.options.strings.loading info,
                        
tooltiptooltip,
                        
addClassthis.tree.options.classNames.nodeWait
                    
});
                }
                break;
            case 
DTNodeStatus_Error:
                
this._isLoading false;
//                $(this.span).addClass(this.tree.options.classNames.nodeError);
                
this._setStatusNode({
                    
titlethis.tree.options.strings.loadError info,
                    
tooltiptooltip,
                    
addClassthis.tree.options.classNames.nodeError
                
});
                break;
            default:
                throw 
"Bad LazyNodeStatus: '" lts "'.";
        }
    },

    
_parentList: function(includeRootincludeSelf) {
        var 
= [];
        var 
dtn includeSelf this this.parent;
        while( 
dtn ) {
            if( 
includeRoot || dtn.parent ){
                
l.unshift(dtn);
            }
            
dtn dtn.parent;
        }
        return 
l;
    },
    
getLevel: function() {
        
/**
         * Return node depth. 0: System root node, 1: visible top-level node.
         */
        
var level 0;
        var 
dtn this.parent;
        while( 
dtn ) {
            
level++;
            
dtn dtn.parent;
        }
        return 
level;
    },

    
_getTypeForOuterNodeEvent: function(event) {
        
/** Return the inner node span (title, checkbox or expander) if
         *  event.target points to the outer span.
         *  This function should fix issue #93:
         *  FF2 ignores empty spans, when generating events (returning the parent instead).
         */
        
var cns this.tree.options.classNames;
        var 
target event.target;
        
// Only process clicks on an outer node span (probably due to a FF2 event handling bug)
        
if( target.className.indexOf(cns.node) < ) {
            return 
null;
        }
        
// Event coordinates, relative to outer node span:
        
var eventX event.pageX target.offsetLeft;
        var 
eventY event.pageY target.offsetTop;

        for(var 
i=0l=target.childNodes.lengthi<li++) {
            var 
cn target.childNodes[i];
            var 
cn.offsetLeft target.offsetLeft;
            var 
cn.offsetTop target.offsetTop;
            var 
nx cn.clientWidthny cn.clientHeight;
//            alert (cn.className + ": " + x + ", " + y + ", s:" + nx + ", " + ny);
            
if( eventX >= && eventX <= (x+nx) && eventY >= && eventY <= (y+ny) ) {
//                alert("HIT "+ cn.className);
                
if( cn.className==cns.title ){
                    return 
"title";
                }else if( 
cn.className==cns.expander ){
                    return 
"expander";
                }else if( 
cn.className==cns.checkbox ){
                    return 
"checkbox";
                }else if( 
cn.className==cns.nodeIcon ){
                    return 
"icon";
                }
            }
        }
        return 
"prefix";
    },

    
getEventTargetType: function(event) {
        
// Return the part of a node, that a click event occured on.
        // Note: there is no check, if the event was fired on THIS node.
        
var tcn event && event.target event.target.className "",
            
cns this.tree.options.classNames;

        if( 
tcn === cns.title ){
            return 
"title";
        }else if( 
tcn === cns.expander ){
            return 
"expander";
        }else if( 
tcn === cns.checkbox ){
            return 
"checkbox";
        }else if( 
tcn === cns.nodeIcon ){
            return 
"icon";
        }else if( 
tcn === cns.empty || tcn === cns.vline || tcn === cns.connector ){
            return 
"prefix";
        }else if( 
tcn.indexOf(cns.node) >= ){
            
// FIX issue #93
            
return this._getTypeForOuterNodeEvent(event);
        }
        return 
null;
    },

    
isVisible: function() {
        
// Return true, if all parents are expanded.
        
var parents this._parentList(truefalse);
        for(var 
i=0l=parents.lengthi<li++){
            if( ! 
parents[i].bExpanded ){ return false; }
        }
        return 
true;
    },

    
makeVisible: function() {
        
// Make sure, all parents are expanded
        
var parents this._parentList(truefalse);
        for(var 
i=0l=parents.lengthi<li++){
            
parents[i]._expand(true);
        }
    },

    
focus: function() {
        
// TODO: check, if we already have focus
//        this.tree.logDebug("dtnode.focus(): %o", this);
        
this.makeVisible();
        try {
            $(
this.span).find(">a").focus();
        } catch(
e) { }
    },

    
isFocused: function() {
        return (
this.tree.tnFocused === this);
    },

    
_activate: function(flagfireEvents) {
        
// (De)Activate - but not focus - this node.
        
this.tree.logDebug("dtnode._activate(%o, fireEvents=%o) - %o"flagfireEventsthis);
        var 
opts this.tree.options;
        if( 
this.data.isStatusNode ){
            return;
        }
        if ( 
fireEvents && opts.onQueryActivate && opts.onQueryActivate.call(this.treeflagthis) === false ){
            return; 
// Callback returned false
        
}
        if( 
flag ) {
            
// Activate
            
if( this.tree.activeNode ) {
                if( 
this.tree.activeNode === this ){
                    return;
                }
                
this.tree.activeNode.deactivate();
            }
            if( 
opts.activeVisible ){
                
this.makeVisible();
            }
            
this.tree.activeNode this;
            if( 
opts.persist ){
                $.
cookie(opts.cookieId+"-active"this.data.keyopts.cookie);
            }
            
this.tree.persistence.activeKey this.data.key;
            $(
this.span).addClass(opts.classNames.active);
            if ( 
fireEvents && opts.onActivate ){
                
opts.onActivate.call(this.treethis);
            }
        } else {
            
// Deactivate
            
if( this.tree.activeNode === this ) {
                if ( 
opts.onQueryActivate && opts.onQueryActivate.call(this.treefalsethis) === false ){
                    return; 
// Callback returned false
                
}
                $(
this.span).removeClass(opts.classNames.active);
                if( 
opts.persist ) {
                    
// Note: we don't pass null, but ''. So the cookie is not deleted.
                    // If we pass null, we also have to pass a COPY of opts, because $cookie will override opts.expires (issue 84)
                    
$.cookie(opts.cookieId+"-active"""opts.cookie);
                }
                
this.tree.persistence.activeKey null;
                
this.tree.activeNode null;
                if ( 
fireEvents && opts.onDeactivate ){
                    
opts.onDeactivate.call(this.treethis);
                }
            }
        }
    },

    
activate: function() {
        
// Select - but not focus - this node.
//        this.tree.logDebug("dtnode.activate(): %o", this);
        
this._activate(truetrue);
    },

    
activateSilently: function() {
        
this._activate(truefalse);
    },

    
deactivate: function() {
//        this.tree.logDebug("dtnode.deactivate(): %o", this);
        
this._activate(falsetrue);
    },

    
isActive: function() {
        return (
this.tree.activeNode === this);
    },

    
_userActivate: function() {
        
// Handle user click / [space] / [enter], according to clickFolderMode.
        
var activate true;
        var 
expand false;
        if ( 
this.data.isFolder ) {
            switch( 
this.tree.options.clickFolderMode ) {
            case 
2:
                
activate false;
                
expand true;
                break;
            case 
3:
                
activate expand true;
                break;
            }
        }
        if( 
this.parent === null ) {
            
expand false;
        }
        if( 
expand ) {
            
this.toggleExpand();
            
this.focus();
        }
        if( 
activate ) {
            
this.activate();
        }
    },

    
_setSubSel: function(hasSubSel) {
        if( 
hasSubSel ) {
            
this.hasSubSel true;
            $(
this.span).addClass(this.tree.options.classNames.partsel);
        } else {
            
this.hasSubSel false;
            $(
this.span).removeClass(this.tree.options.classNames.partsel);
        }
    },
    
/**
     * Fix selection and partsel status, of parent nodes, according to current status of
     * end nodes.
     */
    
_updatePartSelectionState: function() {
//        alert("_updatePartSelectionState " + this);
//        this.tree.logDebug("_updatePartSelectionState() - %o", this);
        
var sel;
        
// Return `true` or `false` for end nodes and remove part-sel flag
        
if( ! this.hasChildren() ){
            
sel = (this.bSelected && !this.data.unselectable && !this.data.isStatusNode);
            
this._setSubSel(false);
            return 
sel;
        }
        
// Return `true`, `false`, or `undefined` for parent nodes
        
var il,
            
cl this.childList,
            
allSelected true,
            
allDeselected true;
        for(
i=0l=cl.lengthi<l;  i++) {
            var 
cl[i],
                
n._updatePartSelectionState();
            if( 
!== false){
                
allDeselected false;
            }
            if( 
!== true){
                
allSelected false;
            }
        }
        if( 
allSelected ){
            
sel true;
        } else if ( 
allDeselected ){
            
sel false;
        } else {
            
sel undefined;
        }
        
this._setSubSel(sel === undefined);
        
this.bSelected = (sel === true);
        return 
sel;
    },

    
/**
     * Fix selection status, after this node was (de)selected in multi-hier mode.
     * This includes (de)selecting all children.
     */
    
_fixSelectionState: function() {
//        alert("_fixSelectionState " + this);
//        this.tree.logDebug("_fixSelectionState(%s) - %o", this.bSelected, this);
        
var pil;
        if( 
this.bSelected ) {
            
// Select all children
            
this.visit(function(node){
                
node.parent._setSubSel(true);
                if(!
node.data.unselectable){
                    
node._select(truefalsefalse);
                }
            });
            
// Select parents, if all children are selected
            
this.parent;
            while( 
) {
                
p._setSubSel(true);
                var 
allChildsSelected true;
                for(
i=0l=p.childList.lengthi<l;  i++) {
                    var 
p.childList[i];
                    if( !
n.bSelected && !n.data.isStatusNode && !n.data.unselectable) {
                    
// issue 305 proposes this:
//                    if( !n.bSelected && !n.data.isStatusNode ) {
                        
allChildsSelected false;
                        break;
                    }
                }
                if( 
allChildsSelected ){
                    
p._select(truefalsefalse);
                }
                
p.parent;
            }
        } else {
            
// Deselect all children
            
this._setSubSel(false);
            
this.visit(function(node){
                
node._setSubSel(false);
                
node._select(falsefalsefalse);
            });
            
// Deselect parents, and recalc hasSubSel
            
this.parent;
            while( 
) {
                
p._select(falsefalsefalse);
                var 
isPartSel false;
                for(
i=0l=p.childList.lengthi<l;  i++) {
                    if( 
p.childList[i].bSelected || p.childList[i].hasSubSel ) {
                        
isPartSel true;
                        break;
                    }
                }
                
p._setSubSel(isPartSel);
                
p.parent;
            }
        }
    },

    
_select: function(selfireEventsdeep) {
        
// Select - but not focus - this node.
//        this.tree.logDebug("dtnode._select(%o) - %o", sel, this);
        
var opts this.tree.options;
        if( 
this.data.isStatusNode ){
            return;
        }
        
//
        
if( this.bSelected === sel ) {
//            this.tree.logDebug("dtnode._select(%o) IGNORED - %o", sel, this);
            
return;
        }
        
// Allow event listener to abort selection
        
if ( fireEvents && opts.onQuerySelect && opts.onQuerySelect.call(this.treeselthis) === false ){
            return; 
// Callback returned false
        
}
        
// Force single-selection
        
if( opts.selectMode==&& sel ) {
            
this.tree.visit(function(node){
                if( 
node.bSelected ) {
                    
// Deselect; assuming that in selectMode:1 there's max. one other selected node
                    
node._select(falsefalsefalse);
                    return 
false;
                }
            });
        }

        
this.bSelected sel;
//        this.tree._changeNodeList("select", this, sel);

        
if( sel ) {
            if( 
opts.persist ){
                
this.tree.persistence.addSelect(this.data.key);
            }
            $(
this.span).addClass(opts.classNames.selected);

            if( 
deep && opts.selectMode === ){
                
this._fixSelectionState();
            }
            if ( 
fireEvents && opts.onSelect ){
                
opts.onSelect.call(this.treetruethis);
            }
        } else {
            if( 
opts.persist ){
                
this.tree.persistence.clearSelect(this.data.key);
            }
            $(
this.span).removeClass(opts.classNames.selected);

            if( 
deep && opts.selectMode === ){
                
this._fixSelectionState();
            }
            if ( 
fireEvents && opts.onSelect ){
                
opts.onSelect.call(this.treefalsethis);
            }
        }
    },

    
select: function(sel) {
        
// Select - but not focus - this node.
//        this.tree.logDebug("dtnode.select(%o) - %o", sel, this);
        
if( this.data.unselectable ){
            return 
this.bSelected;
        }
        return 
this._select(sel!==falsetruetrue);
    },

    
toggleSelect: function() {
//        this.tree.logDebug("dtnode.toggleSelect() - %o", this);
        
return this.select(!this.bSelected);
    },

    
isSelected: function() {
        return 
this.bSelected;
    },

    
isLazy: function() {
        return !!
this.data.isLazy;
    },

    
_loadContent: function() {
        try {
            var 
opts this.tree.options;
            
this.tree.logDebug("_loadContent: start - %o"this);
            
this.setLazyNodeStatus(DTNodeStatus_Loading);
            if( 
true === opts.onLazyRead.call(this.treethis) ) {
                
// If function returns 'true', we assume that the loading is done:
                
this.setLazyNodeStatus(DTNodeStatus_Ok);
                
// Otherwise (i.e. if the loading was started as an asynchronous process)
                // the onLazyRead(dtnode) handler is expected to call dtnode.setLazyNodeStatus(DTNodeStatus_Ok/_Error) when done.
                
this.tree.logDebug("_loadContent: succeeded - %o"this);
            }
        } catch(
e) {
            
this.tree.logWarning("_loadContent: failed - %o"e);
            
this.setLazyNodeStatus(DTNodeStatus_Error, {tooltip""+e});
        }
    },

    
_expand: function(bExpandforceSync) {
        if( 
this.bExpanded === bExpand ) {
            
this.tree.logDebug("dtnode._expand(%o) IGNORED - %o"bExpandthis);
            return;
        }
        
this.tree.logDebug("dtnode._expand(%o) - %o"bExpandthis);
        var 
opts this.tree.options;
        if( !
bExpand && this.getLevel() < opts.minExpandLevel ) {
            
this.tree.logDebug("dtnode._expand(%o) prevented collapse - %o"bExpandthis);
            return;
        }
        if ( 
opts.onQueryExpand && opts.onQueryExpand.call(this.treebExpandthis) === false ){
            return; 
// Callback returned false
        
}
        
this.bExpanded bExpand;

        
// Persist expand state
        
if( opts.persist ) {
            if( 
bExpand ){
                
this.tree.persistence.addExpand(this.data.key);
            }else{
                
this.tree.persistence.clearExpand(this.data.key);
            }
        }
        
// Do not apply animations in init phase, or before lazy-loading
        
var allowEffects = !(this.data.isLazy && this.childList === null)
            && !
this._isLoading
            
&& !forceSync;
        
this.render(allowEffects);

        
// Auto-collapse mode: collapse all siblings
        
if( this.bExpanded && this.parent && opts.autoCollapse ) {
            var 
parents this._parentList(falsetrue);
            for(var 
i=0l=parents.lengthi<li++){
                
parents[i].collapseSiblings();
            }
        }
        
// If the currently active node is now hidden, deactivate it
        
if( opts.activeVisible && this.tree.activeNode && ! this.tree.activeNode.isVisible() ) {
            
this.tree.activeNode.deactivate();
        }
        
// Expanding a lazy node: set 'loading...' and call callback
        
if( bExpand && this.data.isLazy && this.childList === null && !this._isLoading ) {
            
this._loadContent();
            return;
        }
        if ( 
opts.onExpand ){
            
opts.onExpand.call(this.treebExpandthis);
        }
    },

    
isExpanded: function() {
        return 
this.bExpanded;
    },

    
expand: function(flag) {
        
flag = (flag !== false);
        if( !
this.childList && !this.data.isLazy && flag ){
            return; 
// Prevent expanding empty nodes
        
} else if( this.parent === null && !flag ){
            return; 
// Prevent collapsing the root
        
}
        
this._expand(flag);
    },

    
scheduleAction: function(modems) {
        
/** Schedule activity for delayed execution (cancel any pending request).
         *  scheduleAction('cancel') will cancel the request.
         */
        
if( this.tree.timer ) {
            
clearTimeout(this.tree.timer);
            
this.tree.logDebug("clearTimeout(%o)"this.tree.timer);
        }
        var 
self this// required for closures
        
switch (mode) {
        case 
"cancel":
            
// Simply made sure that timer was cleared
            
break;
        case 
"expand":
            
this.tree.timer setTimeout(function(){
                
self.tree.logDebug("setTimeout: trigger expand");
                
self.expand(true);
            }, 
ms);
            break;
        case 
"activate":
            
this.tree.timer setTimeout(function(){
                
self.tree.logDebug("setTimeout: trigger activate");
                
self.activate();
            }, 
ms);
            break;
        default:
            throw 
"Invalid mode " mode;
        }
        
this.tree.logDebug("setTimeout(%s, %s): %s"modemsthis.tree.timer);
    },

    
toggleExpand: function() {
        
this.expand(!this.bExpanded);
    },

    
collapseSiblings: function() {
        if( 
this.parent === null ){
            return;
        }
        var 
ac this.parent.childList;
        for (var 
i=0l=ac.lengthi<li++) {
            if ( 
ac[i] !== this && ac[i].bExpanded ){
                
ac[i]._expand(false);
            }
        }
    },

    
_onClick: function(event) {
//        this.tree.logDebug("dtnode.onClick(" + event.type + "): dtnode:" + this + ", button:" + event.button + ", which: " + event.which);
        
var targetType this.getEventTargetType(event);
        if( 
targetType === "expander" ) {
            
// Clicking the expander icon always expands/collapses
            
this.toggleExpand();
            
this.focus(); // issue 95
        
} else if( targetType === "checkbox" ) {
            
// Clicking the checkbox always (de)selects
            
this.toggleSelect();
            
this.focus(); // issue 95
        
} else {
            
this._userActivate();
            var 
aTag this.span.getElementsByTagName("a");
            if(
aTag[0]){
                
// issue 154, 313
//                if(!($.browser.msie && parseInt($.browser.version, 10) < 9)){
                
if(!(BROWSER.msie && parseInt(BROWSER.version10) < 9)){
                    
aTag[0].focus();
                }
            }else{
                
// 'noLink' option was set
                
return true;
            }
        }
        
// Make sure that clicks stop, otherwise <a href='#'> jumps to the top
        
event.preventDefault();
    },

    
_onDblClick: function(event) {
//        this.tree.logDebug("dtnode.onDblClick(" + event.type + "): dtnode:" + this + ", button:" + event.button + ", which: " + event.which);
    
},

    
_onKeydown: function(event) {
//        this.tree.logDebug("dtnode.onKeydown(" + event.type + "): dtnode:" + this + ", charCode:" + event.charCode + ", keyCode: " + event.keyCode + ", which: " + event.which);
        
var handled true,
            
sib;
//        alert("keyDown" + event.which);

        
switch( event.which ) {
            
// charCodes:
//            case 43: // '+'
            
case 107// '+'
            
case 187// '+' @ Chrome, Safari
                
if( !this.bExpanded ){ this.toggleExpand(); }
                break;
//            case 45: // '-'
            
case 109// '-'
            
case 189// '+' @ Chrome, Safari
                
if( this.bExpanded ){ this.toggleExpand(); }
                break;
            
//~ case 42: // '*'
                //~ break;
            //~ case 47: // '/'
                //~ break;
            // case 13: // <enter>
                // <enter> on a focused <a> tag seems to generate a click-event.
                // this._userActivate();
                // break;
            
case 32// <space>
                
this._userActivate();
                break;
            case 
8// <backspace>
                
if( this.parent ){
                    
this.parent.focus();
                }
                break;
            case 
37// <left>
                
if( this.bExpanded ) {
                    
this.toggleExpand();
                    
this.focus();
//                } else if( this.parent && (this.tree.options.rootVisible || this.parent.parent) ) {
                
} else if( this.parent && this.parent.parent ) {
                    
this.parent.focus();
                }
                break;
            case 
39// <right>
                
if( !this.bExpanded && (this.childList || this.data.isLazy) ) {
                    
this.toggleExpand();
                    
this.focus();
                } else if( 
this.childList ) {
                    
this.childList[0].focus();
                }
                break;
            case 
38// <up>
                
sib this.getPrevSibling();
                while( 
sib && sib.bExpanded && sib.childList ){
                    
sib sib.childList[sib.childList.length-1];
                }
//                if( !sib && this.parent && (this.tree.options.rootVisible || this.parent.parent) )
                
if( !sib && this.parent && this.parent.parent ){
                    
sib this.parent;
                }
                if( 
sib ){
                    
sib.focus();
                }
                break;
            case 
40// <down>
                
if( this.bExpanded && this.childList ) {
                    
sib this.childList[0];
                } else {
                    var 
parents this._parentList(falsetrue);
                    for(var 
i=parents.length-1i>=0i--) {
                        
sib parents[i].getNextSibling();
                        if( 
sib ){ break; }
                    }
                }
                if( 
sib ){
                    
sib.focus();
                }
                break;
            default:
                
handled false;
        }
        
// Return false, if handled, to prevent default processing
//        return !handled;
        
if(handled){
            
event.preventDefault();
        }
    },

    
_onKeypress: function(event) {
        
// onKeypress is only hooked to allow user callbacks.
        // We don't process it, because IE and Safari don't fire keypress for cursor keys.
//        this.tree.logDebug("dtnode.onKeypress(" + event.type + "): dtnode:" + this + ", charCode:" + event.charCode + ", keyCode: " + event.keyCode + ", which: " + event.which);
    
},

    
_onFocus: function(event) {
        
// Handles blur and focus events.
//        this.tree.logDebug("dtnode._onFocus(%o): %o", event, this);
        
var opts this.tree.options;
        if ( 
event.type == "blur" || event.type == "focusout" ) {
            if ( 
opts.onBlur ){
                
opts.onBlur.call(this.treethis);
            }
            if( 
this.tree.tnFocused ){
                $(
this.tree.tnFocused.span).removeClass(opts.classNames.focused);
            }
            
this.tree.tnFocused null;
            if( 
opts.persist ){
                $.
cookie(opts.cookieId+"-focus"""opts.cookie);
            }
        } else if ( 
event.type=="focus" || event.type=="focusin") {
            
// Fix: sometimes the blur event is not generated
            
if( this.tree.tnFocused && this.tree.tnFocused !== this ) {
                
this.tree.logDebug("dtnode.onFocus: out of sync: curFocus: %o"this.tree.tnFocused);
                $(
this.tree.tnFocused.span).removeClass(opts.classNames.focused);
            }
            
this.tree.tnFocused this;
            if ( 
opts.onFocus ){
                
opts.onFocus.call(this.treethis);
            }
            $(
this.tree.tnFocused.span).addClass(opts.classNames.focused);
            if( 
opts.persist ){
                $.
cookie(opts.cookieId+"-focus"this.data.keyopts.cookie);
            }
        }
        
// TODO: return anything?
//        return false;
    
},

    
visit: function(fnincludeSelf) {
        
// Call fn(node) for all child nodes. Stop iteration, if fn() returns false.
        
var res true;
        if( 
includeSelf === true ) {
            
res fn(this);
            if( 
res === false || res == "skip" ){
                return 
res;
            }
        }
        if(
this.childList){
            for(var 
i=0l=this.childList.lengthi<li++){
                
res this.childList[i].visit(fntrue);
                if( 
res === false ){
                    break;
                }
            }
        }
        return 
res;
    },

    
visitParents: function(fnincludeSelf) {
        
// Visit parent nodes (bottom up)
        
if(includeSelf && fn(this) === false){
            return 
false;
        }
        var 
this.parent;
        while( 
) {
            if(
fn(p) === false){
                return 
false;
            }
            
p.parent;
        }
        return 
true;
    },

    
remove: function() {
        
// Remove this node
//        this.tree.logDebug ("%s.remove()", this);
        
if ( this === this.tree.root ){
            throw 
"Cannot remove system root";
        }
        return 
this.parent.removeChild(this);
    },

    
removeChild: function(tn) {
        
// Remove tn from list of direct children.
        
var ac this.childList;
        if( 
ac.length == ) {
            if( 
tn !== ac[0] ){
                throw 
"removeChild: invalid child";
            }
            return 
this.removeChildren();
        }
        if( 
tn === this.tree.activeNode ){
            
tn.deactivate();
        }
        if( 
this.tree.options.persist ) {
            if( 
tn.bSelected ){
                
this.tree.persistence.clearSelect(tn.data.key);
            }
            if ( 
tn.bExpanded ){
                
this.tree.persistence.clearExpand(tn.data.key);
            }
        }
        
tn.removeChildren(true);
        if(
this.ul){
//            $("li", $(this.ul)).remove(); // issue 399
            
this.ul.removeChild(tn.li); // issue 402
        
}
        for(var 
i=0l=ac.lengthi<li++) {
            if( 
ac[i] === tn ) {
                
this.childList.splice(i1);
//                delete tn;  // JSLint complained
                
break;
            }
        }
    },

    
removeChildren: function(isRecursiveCallretainPersistence) {
        
// Remove all child nodes (more efficiently than recursive remove())
        
this.tree.logDebug("%s.removeChildren(%o)"thisisRecursiveCall);
        var 
tree this.tree;
        var 
ac this.childList;
        if( 
ac ) {
            for(var 
i=0l=ac.lengthi<li++) {
                var 
tn ac[i];
                if ( 
tn === tree.activeNode && !retainPersistence ){
                    
tn.deactivate();
                }
                if( 
this.tree.options.persist && !retainPersistence ) {
                    if( 
tn.bSelected ){
                        
this.tree.persistence.clearSelect(tn.data.key);
                    }
                    if ( 
tn.bExpanded ){
                        
this.tree.persistence.clearExpand(tn.data.key);
                    }
                }
                
tn.removeChildren(trueretainPersistence);
                if(
this.ul){
//                    this.ul.removeChild(tn.li);
                    
$("li", $(this.ul)).remove(); // issue 231
                
}
//                delete tn;  JSLint complained
            
}
            
// Set to 'null' which is interpreted as 'not yet loaded' for lazy
            // nodes
            
this.childList null;
        }
        if( ! 
isRecursiveCall ) {
//            this._expand(false);
//            this.isRead = false;
            
this._isLoading false;
            
this.render();
        }
    },

    
setTitle: function(title) {
        
this.fromDict({titletitle});
    },

    
reload: function(force) {
        throw 
"Use reloadChildren() instead";
    },

    
reloadChildren: function(callback) {
        
// Reload lazy content (expansion state is maintained).
        
if( this.parent === null ){
            throw 
"Use tree.reload() instead";
        }else if( ! 
this.data.isLazy ){
            throw 
"node.reloadChildren() requires lazy nodes.";
        }
        
// appendAjax triggers 'nodeLoaded' event.
        // We listen to this, if a callback was passed to reloadChildren
        
if(callback){
            var 
self this;
            var 
eventType "nodeLoaded.dynatree." this.tree.$tree.attr("id")
                + 
"." this.data.key;
            
this.tree.$tree.bind(eventType, function(enodeisOk){
                
self.tree.$tree.unbind(eventType);
                
self.tree.logDebug("loaded %o, %o, %o"enodeisOk);
                if(
node !== self){
                    throw 
"got invalid load event";
                }
                
callback.call(self.treenodeisOk);
            });
        }
        
// The expansion state is maintained
        
this.removeChildren();
        
this._loadContent();
//        if( this.bExpanded ) {
//            // Remove children first, to prevent effects being applied
//            this.removeChildren();
//            // then force re-expand to trigger lazy loading
////            this.expand(false);
////            this.expand(true);
//            this._loadContent();
//        } else {
//            this.removeChildren();
//            this._loadContent();
//        }
    
},

    
/**
     * Make sure the node with a given key path is available in the tree.
     */
    
_loadKeyPath: function(keyPathcallback) {
        var 
tree this.tree;
        
tree.logDebug("%s._loadKeyPath(%s)"thiskeyPath);
        if(
keyPath === ""){
            throw 
"Key path must not be empty";
        }
        var 
segList keyPath.split(tree.options.keyPathSeparator);
        if(
segList[0] === ""){
            throw 
"Key path must be relative (don't start with '/')";
        }
        var 
seg segList.shift();
        if(
this.childList){
            for(var 
i=0l=this.childList.lengthli++){
                var 
child this.childList[i];
                if( 
child.data.key === seg ){
                    if(
segList.length === 0) {
                        
// Found the end node
                        
callback.call(treechild"ok");

                    }else if(
child.data.isLazy && (child.childList === null || child.childList === undefined)){
                        
tree.logDebug("%s._loadKeyPath(%s) -> reloading %s..."thiskeyPathchild);
                        var 
self this;
                        
// Note: this line gives a JSLint warning (Don't make functions within a loop)
                        /*jshint loopfunc:true */
                        
child.reloadChildren(function(nodeisOk){
                            
// After loading, look for direct child with that key
                            
if(isOk){
                                
tree.logDebug("%s._loadKeyPath(%s) -> reloaded %s."nodekeyPathnode);
                                
callback.call(treechild"loaded");
                                
node._loadKeyPath(segList.join(tree.options.keyPathSeparator), callback);
                            }else{
                                
tree.logWarning("%s._loadKeyPath(%s) -> reloadChildren() failed."selfkeyPath);
                                
callback.call(treechild"error");
                            }
                        });
                        
// we can ignore it, since it will only be exectuted once, the the loop is ended
                        // See also http://stackoverflow.com/questions/3037598/how-to-get-around-the-jslint-error-dont-make-functions-within-a-loop
                    
} else {
                        
callback.call(treechild"loaded");
                        
// Look for direct child with that key
                        
child._loadKeyPath(segList.join(tree.options.keyPathSeparator), callback);
                    }
                    return;
                }
            }
        }
        
// Could not find key
        // Callback params: child: undefined, the segment, isEndNode (segList.length === 0)
        
callback.call(treeundefined"notfound"segsegList.length === 0);
        
tree.logWarning("Node not found: " seg);
        return;
    },

    
resetLazy: function() {
        
// Discard lazy content.
        
if( this.parent === null ){
            throw 
"Use tree.reload() instead";
        }else if( ! 
this.data.isLazy ){
            throw 
"node.resetLazy() requires lazy nodes.";
        }
        
this.expand(false);
        
this.removeChildren();
    },

    
_addChildNode: function(dtnodebeforeNode) {
        
/**
         * Internal function to add one single DynatreeNode as a child.
         *
         */
        
var tree this.tree,
            
opts tree.options,
            
pers tree.persistence;

//        tree.logDebug("%s._addChildNode(%o)", this, dtnode);

        // --- Update and fix dtnode attributes if necessary
        
dtnode.parent this;
//        if( beforeNode && (beforeNode.parent !== this || beforeNode === dtnode ) )
//            throw "<beforeNode> must be another child of <this>";

        // --- Add dtnode as a child
        
if ( this.childList === null ) {
            
this.childList = [];
        } else if( ! 
beforeNode ) {
            
// Fix 'lastsib'
            
if(this.childList.length 0) {
                $(
this.childList[this.childList.length-1].span).removeClass(opts.classNames.lastsib);
            }
        }
        if( 
beforeNode ) {
            var 
iBefore = $.inArray(beforeNodethis.childList);
            if( 
iBefore ){
                throw 
"<beforeNode> must be a child of <this>";
            }
            
this.childList.splice(iBefore0dtnode);
        } else {
            
// Append node
            
this.childList.push(dtnode);
        }

        
// --- Handle persistence
        // Initial status is read from cookies, if persistence is active and
        // cookies are already present.
        // Otherwise the status is read from the data attributes and then persisted.
        
var isInitializing tree.isInitializing();
        if( 
opts.persist && pers.cookiesFound && isInitializing ) {
            
// Init status from cookies
//            tree.logDebug("init from cookie, pa=%o, dk=%o", pers.activeKey, dtnode.data.key);
            
if( pers.activeKey === dtnode.data.key ){
                
tree.activeNode dtnode;
            }
            if( 
pers.focusedKey === dtnode.data.key ){
                
tree.focusNode dtnode;
            }
            
dtnode.bExpanded = ($.inArray(dtnode.data.keypers.expandedKeyList) >= 0);
            
dtnode.bSelected = ($.inArray(dtnode.data.keypers.selectedKeyList) >= 0);
//            tree.logDebug("    key=%o, bSelected=%o", dtnode.data.key, dtnode.bSelected);
        
} else {
            
// Init status from data (Note: we write the cookies after the init phase)
//            tree.logDebug("init from data");
            
if( dtnode.data.activate ) {
                
tree.activeNode dtnode;
                if( 
opts.persist ){
                    
pers.activeKey dtnode.data.key;
                }
            }
            if( 
dtnode.data.focus ) {
                
tree.focusNode dtnode;
                if( 
opts.persist ){
                    
pers.focusedKey dtnode.data.key;
                }
            }
            
dtnode.bExpanded = ( dtnode.data.expand === true ); // Collapsed by default
            
if( dtnode.bExpanded && opts.persist ){
                
pers.addExpand(dtnode.data.key);
            }
            
dtnode.bSelected = ( dtnode.data.select === true ); // Deselected by default
/*
            Doesn't work, cause pers.selectedKeyList may be null
            if( dtnode.bSelected && opts.selectMode==1
                && pers.selectedKeyList && pers.selectedKeyList.length>0 ) {
                tree.logWarning("Ignored multi-selection in single-mode for %o", dtnode);
                dtnode.bSelected = false; // Fixing bad input data (multi selection for mode:1)
            }
*/
            
if( dtnode.bSelected && opts.persist ){
                
pers.addSelect(dtnode.data.key);
            }
        }

        
// Always expand, if it's below minExpandLevel
//        tree.logDebug ("%s._addChildNode(%o), l=%o", this, dtnode, dtnode.getLevel());
        
if ( opts.minExpandLevel >= dtnode.getLevel() ) {
//            tree.logDebug ("Force expand for %o", dtnode);
            
this.bExpanded true;
        }

        
// In multi-hier mode, update the parents selection state
        // issue #82: only if not initializing, because the children may not exist yet
//        if( !dtnode.data.isStatusNode && opts.selectMode==3 && !isInitializing )
//            dtnode._fixSelectionState();

        // In multi-hier mode, update the parents selection state
        
if( dtnode.bSelected && opts.selectMode==) {
            var 
this;
            while( 
) {
                if( !
p.hasSubSel ){
                    
p._setSubSel(true);
                }
                
p.parent;
            }
        }
        
// render this node and the new child
        
if ( tree.bEnableUpdate ){
            
this.render();
        }
        return 
dtnode;
    },

    
addChild: function(objbeforeNode) {
        
/**
         * Add a node object as child.
         *
         * This should be the only place, where a DynaTreeNode is constructed!
         * (Except for the root node creation in the tree constructor)
         *
         * @param obj A JS object (may be recursive) or an array of those.
         * @param {DynaTreeNode} beforeNode (optional) sibling node.
         *
         * Data format: array of node objects, with optional 'children' attributes.
         * [
         *    { title: "t1", isFolder: true, ... }
         *    { title: "t2", isFolder: true, ...,
         *        children: [
         *            {title: "t2.1", ..},
         *            {..}
         *            ]
         *    }
         * ]
         * A simple object is also accepted instead of an array.
         *
         */
//        this.tree.logDebug("%s.addChild(%o, %o)", this, obj, beforeNode);
        
if(typeof(obj) == "string"){
            throw 
"Invalid data type for " obj;
        }else if( !
obj || obj.length === ){ // Passed null or undefined or empty array
            
return;
        }else if( 
obj instanceof DynaTreeNode ){
            return 
this._addChildNode(objbeforeNode);
        }

        if( !
obj.length ){ // Passed a single data object
            
obj = [ obj ];
        }
        var 
prevFlag this.tree.enableUpdate(false);

        var 
tnFirst null;
        for (var 
i=0l=obj.lengthi<li++) {
            var 
data obj[i];
            var 
dtnode this._addChildNode(new DynaTreeNode(thisthis.treedata), beforeNode);
            if( !
tnFirst ){
                
tnFirst dtnode;
            }
            
// Add child nodes recursively
            
if( data.children ){
                
dtnode.addChild(data.childrennull);
            }
        }
        
this.tree.enableUpdate(prevFlag);
        return 
tnFirst;
    },

    
append: function(obj) {
        
this.tree.logWarning("node.append() is deprecated (use node.addChild() instead).");
        return 
this.addChild(objnull);
    },

    
appendAjax: function(ajaxOptions) {
        var 
self this;
        
this.removeChildren(falsetrue);
        
this.setLazyNodeStatus(DTNodeStatus_Loading);
        
// Debug feature: force a delay, to simulate slow loading...
        
if(ajaxOptions.debugLazyDelay){
            var 
ms ajaxOptions.debugLazyDelay;
            
ajaxOptions.debugLazyDelay 0;
            
this.tree.logInfo("appendAjax: waiting for debugLazyDelay " ms);
            
setTimeout(function(){self.appendAjax(ajaxOptions);}, ms);
            return;
        }
        
// Ajax option inheritance: $.ajaxSetup < $.ui.dynatree.prototype.options.ajaxDefaults < tree.options.ajaxDefaults < ajaxOptions
        
var orgSuccess ajaxOptions.success,
            
orgError ajaxOptions.error,
            
eventType "nodeLoaded.dynatree." this.tree.$tree.attr("id") + "." this.data.key;
        var 
options = $.extend({}, this.tree.options.ajaxDefaultsajaxOptions, {
            
success: function(datatextStatusjqXHR){
                
// <this> is the request options
//                self.tree.logDebug("appendAjax().success");
                
var prevPhase self.tree.phase;
                
self.tree.phase "init";
                
// postProcess is similar to the standard dataFilter hook,
                // but it is also called for JSONP
                
if( options.postProcess ){
                    
data options.postProcess.call(thisdatathis.dataType);
                }
                
// Process ASPX WebMethod JSON object inside "d" property
                // http://code.google.com/p/dynatree/issues/detail?id=202
                
else if (data && data.hasOwnProperty("d")) {
                   
data = (typeof data.d) == "string" ? $.parseJSON(data.d) : data.d;
                }
                if(!$.
isArray(data) || data.length !== 0){
                    
self.addChild(datanull);
                }
                
self.tree.phase "postInit";
                if( 
orgSuccess ){
                    
orgSuccess.call(optionsselfdatatextStatus);
                }
                
self.tree.logDebug("trigger " eventType);
                
self.tree.$tree.trigger(eventType, [selftrue]);
                
self.tree.phase prevPhase;
                
// This should be the last command, so node._isLoading is true
                // while the callbacks run
                
self.setLazyNodeStatus(DTNodeStatus_Ok);
                if($.
isArray(data) && data.length === 0){
                    
// Set to [] which is interpreted as 'no children' for lazy
                    // nodes
                    
self.childList = [];
                    
self.render();
                }
                },
            
error: function(jqXHRtextStatuserrorThrown){
                
// <this> is the request options
                
self.tree.logWarning("appendAjax failed:"textStatus":n"jqXHR"n"errorThrown);
                if( 
orgError ){
                    
orgError.call(optionsselfjqXHRtextStatuserrorThrown);
                }
                
self.tree.$tree.trigger(eventType, [selffalse]);
                
self.setLazyNodeStatus(DTNodeStatus_Error, {infotextStatustooltip"" errorThrown});
                }
        });
        $.
ajax(options);
    },

    
move: function(targetNodemode) {
        
/**Move this node to targetNode.
         *  mode 'child': append this node as last child of targetNode.
         *                This is the default. To be compatble with the D'n'd
         *                hitMode, we also accept 'over'.
         *  mode 'before': add this node as sibling before targetNode.
         *  mode 'after': add this node as sibling after targetNode.
         */
        
var pos;
        if(
this === targetNode){
            return;
        }
        if( !
this.parent  ){
            throw 
"Cannot move system root";
        }
        if(
mode === undefined || mode == "over"){
            
mode "child";
        }
        var 
prevParent this.parent;
        var 
targetParent = (mode === "child") ? targetNode targetNode.parent;
        if( 
targetParent.isDescendantOf(this) ){
            throw 
"Cannot move a node to it's own descendant";
        }
        
// Unlink this node from current parent
        
if( this.parent.childList.length == ) {
            
this.parent.childList this.parent.data.isLazy ? [] : null;
            
this.parent.bExpanded false;
        } else {
            
pos = $.inArray(thisthis.parent.childList);
            if( 
pos ){
                throw 
"Internal error";
            }
            
this.parent.childList.splice(pos1);
        }
        
// Remove from source DOM parent
        
if(this.parent.ul){
            
this.parent.ul.removeChild(this.li);
        }

        
// Insert this node to target parent's child list
        
this.parent targetParent;
        if( 
targetParent.hasChildren() ) {
            switch(
mode) {
            case 
"child":
                
// Append to existing target children
                
targetParent.childList.push(this);
                break;
            case 
"before":
                
// Insert this node before target node
                
pos = $.inArray(targetNodetargetParent.childList);
                if( 
pos ){
                    throw 
"Internal error";
                }
                
targetParent.childList.splice(pos0this);
                break;
            case 
"after":
                
// Insert this node after target node
                
pos = $.inArray(targetNodetargetParent.childList);
                if( 
pos ){
                    throw 
"Internal error";
                }
                
targetParent.childList.splice(pos+10this);
                break;
            default:
                throw 
"Invalid mode " mode;
            }
        } else {
            
targetParent.childList = [ this ];
        }
        
// Parent has no <ul> tag yet:
        
if( !targetParent.ul ) {
            
// This is the parent's first child: create UL tag
            // (Hidden, because it will be
            
targetParent.ul document.createElement("ul");
            
targetParent.ul.style.display "none";
            
targetParent.li.appendChild(targetParent.ul);
        }
        
// Issue 319: Add to target DOM parent (only if node was already rendered(expanded))
        
if(this.li){
            
targetParent.ul.appendChild(this.li);
        }

        if( 
this.tree !== targetNode.tree ) {
            
// Fix node.tree for all source nodes
            
this.visit(function(node){
                
node.tree targetNode.tree;
            }, 
nulltrue);
            throw 
"Not yet implemented.";
        }
        
// TODO: fix selection state
        // TODO: fix active state
        
if( !prevParent.isDescendantOf(targetParent)) {
            
prevParent.render();
        }
        if( !
targetParent.isDescendantOf(prevParent) ) {
            
targetParent.render();
        }
//        this.tree.redraw();
/*
        var tree = this.tree;
        var opts = tree.options;
        var pers = tree.persistence;


        // Always expand, if it's below minExpandLevel
//        tree.logDebug ("%s._addChildNode(%o), l=%o", this, dtnode, dtnode.getLevel());
        if ( opts.minExpandLevel >= dtnode.getLevel() ) {
//            tree.logDebug ("Force expand for %o", dtnode);
            this.bExpanded = true;
        }

        // In multi-hier mode, update the parents selection state
        // issue #82: only if not initializing, because the children may not exist yet
//        if( !dtnode.data.isStatusNode && opts.selectMode==3 && !isInitializing )
//            dtnode._fixSelectionState();

        // In multi-hier mode, update the parents selection state
        if( dtnode.bSelected && opts.selectMode==3 ) {
            var p = this;
            while( p ) {
                if( !p.hasSubSel )
                    p._setSubSel(true);
                p = p.parent;
            }
        }
        // render this node and the new child
        if ( tree.bEnableUpdate )
            this.render();

        return dtnode;

*/
    
},

    
// --- end of class
    
lastentryundefined
};

/*************************************************************************
 * class DynaTreeStatus
 */

var DynaTreeStatus = Class.create();


DynaTreeStatus._getTreePersistData = function(cookieIdcookieOpts) {
    
// Static member: Return persistence information from cookies
    
var ts = new DynaTreeStatus(cookieIdcookieOpts);
    
ts.read();
    return 
ts.toDict();
};
// Make available in global scope
getDynaTreePersistData DynaTreeStatus._getTreePersistData// TODO: deprecated


DynaTreeStatus.prototype = {
    
// Constructor
    
initialize: function(cookieIdcookieOpts) {
//        this._log("DynaTreeStatus: initialize");
        
if( cookieId === undefined ){
            
cookieId = $.ui.dynatree.prototype.options.cookieId;
        }
        
cookieOpts = $.extend({}, $.ui.dynatree.prototype.options.cookiecookieOpts);

        
this.cookieId cookieId;
        
this.cookieOpts cookieOpts;
        
this.cookiesFound undefined;
        
this.activeKey null;
        
this.focusedKey null;
        
this.expandedKeyList null;
        
this.selectedKeyList null;
    },
    
// member functions
    
_log: function(msg) {
        
//    this.logDebug("_changeNodeList(%o): nodeList:%o, idx:%o", mode, nodeList, idx);
        
Array.prototype.unshift.apply(arguments, ["debug"]);
        
_log.apply(thisarguments);
    },
    
read: function() {
//        this._log("DynaTreeStatus: read");
        // Read or init cookies.
        
this.cookiesFound false;

        var 
cookie = $.cookie(this.cookieId "-active");
        
this.activeKey = ( cookie === null ) ? "" cookie;
        if( 
cookie !== null ){
            
this.cookiesFound true;
        }
        
cookie = $.cookie(this.cookieId "-focus");
        
this.focusedKey = ( cookie === null ) ? "" cookie;
        if( 
cookie !== null ){
            
this.cookiesFound true;
        }
        
cookie = $.cookie(this.cookieId "-expand");
        
this.expandedKeyList = ( cookie === null ) ? [] : cookie.split(",");
        if( 
cookie !== null ){
            
this.cookiesFound true;
        }
        
cookie = $.cookie(this.cookieId "-select");
        
this.selectedKeyList = ( cookie === null ) ? [] : cookie.split(",");
        if( 
cookie !== null ){
            
this.cookiesFound true;
        }
    },
    
write: function() {
//        this._log("DynaTreeStatus: write");
        
$.cookie(this.cookieId "-active", ( this.activeKey === null ) ? "" this.activeKeythis.cookieOpts);
        $.
cookie(this.cookieId "-focus", ( this.focusedKey === null ) ? "" this.focusedKeythis.cookieOpts);
        $.
cookie(this.cookieId "-expand", ( this.expandedKeyList === null ) ? "" this.expandedKeyList.join(","), this.cookieOpts);
        $.
cookie(this.cookieId "-select", ( this.selectedKeyList === null ) ? "" this.selectedKeyList.join(","), this.cookieOpts);
    },
    
addExpand: function(key) {
//        this._log("addExpand(%o)", key);
        
if( $.inArray(keythis.expandedKeyList) < ) {
            
this.expandedKeyList.push(key);
            $.
cookie(this.cookieId "-expand"this.expandedKeyList.join(","), this.cookieOpts);
        }
    },
    
clearExpand: function(key) {
//        this._log("clearExpand(%o)", key);
        
var idx = $.inArray(keythis.expandedKeyList);
        if( 
idx >= ) {
            
this.expandedKeyList.splice(idx1);
            $.
cookie(this.cookieId "-expand"this.expandedKeyList.join(","), this.cookieOpts);
        }
    },
    
addSelect: function(key) {
//        this._log("addSelect(%o)", key);
        
if( $.inArray(keythis.selectedKeyList) < ) {
            
this.selectedKeyList.push(key);
            $.
cookie(this.cookieId "-select"this.selectedKeyList.join(","), this.cookieOpts);
        }
    },
    
clearSelect: function(key) {
//        this._log("clearSelect(%o)", key);
        
var idx = $.inArray(keythis.selectedKeyList);
        if( 
idx >= ) {
            
this.selectedKeyList.splice(idx1);
            $.
cookie(this.cookieId "-select"this.selectedKeyList.join(","), this.cookieOpts);
        }
    },
    
isReloading: function() {
        return 
this.cookiesFound === true;
    },
    
toDict: function() {
        return {
            
cookiesFoundthis.cookiesFound,
            
activeKeythis.activeKey,
            
focusedKeythis.activeKey,
            
expandedKeyListthis.expandedKeyList,
            
selectedKeyListthis.selectedKeyList
        
};
    },
    
// --- end of class
    
lastentryundefined
};


/*************************************************************************
 * class DynaTree
 */

var DynaTree = Class.create();

// --- Static members ----------------------------------------------------------

DynaTree.version "$Version:$";

/*
DynaTree._initTree = function() {
};

DynaTree._bind = function() {
};
*/
//--- Class members ------------------------------------------------------------

DynaTree.prototype = {
    
// Constructor
//    initialize: function(divContainer, options) {
    
initialize: function($widget) {
        
// instance members
        
this.phase "init";
        
this.$widget $widget;
        
this.options $widget.options;
        
this.$tree $widget.element;
        
this.timer null;
        
// find container element
        
this.divTree this.$tree.get(0);

//        var parentPos = $(this.divTree).parent().offset();
//        this.parentTop = parentPos.top;
//        this.parentLeft = parentPos.left;

        
_initDragAndDrop(this);
    },

    
// member functions

    
_load: function(callback) {
        var 
$widget this.$widget;
        var 
opts this.options,
            
self this;
        
this.bEnableUpdate true;
        
this._nodeCount 1;
        
this.activeNode null;
        
this.focusNode null;

        
// Some deprecation warnings to help with migration
        
if( opts.rootVisible !== undefined ){
            
this.logWarning("Option 'rootVisible' is no longer supported.");
        }
        if( 
opts.minExpandLevel ) {
            
this.logWarning("Option 'minExpandLevel' must be >= 1.");
            
opts.minExpandLevel 1;
        }
//        _log("warn", "jQuery.support.boxModel " + jQuery.support.boxModel);

        // If a 'options.classNames' dictionary was passed, still use defaults
        // for undefined classes:
        
if( opts.classNames !== $.ui.dynatree.prototype.options.classNames ) {
            
opts.classNames = $.extend({}, $.ui.dynatree.prototype.options.classNamesopts.classNames);
        }
        if( 
opts.ajaxDefaults !== $.ui.dynatree.prototype.options.ajaxDefaults ) {
            
opts.ajaxDefaults = $.extend({}, $.ui.dynatree.prototype.options.ajaxDefaultsopts.ajaxDefaults);
        }
        if( 
opts.dnd !== $.ui.dynatree.prototype.options.dnd ) {
            
opts.dnd = $.extend({}, $.ui.dynatree.prototype.options.dndopts.dnd);
        }
        
// Guess skin path, if not specified
        
if(!opts.imagePath) {
            $(
"script").each( function () {
                var 
_rexDtLibName = /.*dynatree[^/]*.js$/i;
                if( 
this.src.search(_rexDtLibName) >= ) {
                    if( 
this.src.indexOf("/")>=){ // issue #47
                        
opts.imagePath this.src.slice(0this.src.lastIndexOf("/")) + "/skin/";
                    }else{
                        
opts.imagePath "skin/";
                    }
                    
self.logDebug("Guessing imagePath from '%s': '%s'"this.srcopts.imagePath);
                    return 
false// first match
                
}
            });
        }

        
this.persistence = new DynaTreeStatus(opts.cookieIdopts.cookie);
        if( 
opts.persist ) {
            if( !$.
cookie ){
                
_log("warn""Please include jquery.cookie.js to use persistence.");
            }
            
this.persistence.read();
        }
        
this.logDebug("DynaTree.persistence: %o"this.persistence.toDict());

        
// Cached tag strings
        
this.cache = {
            
tagEmpty"<span class='" opts.classNames.empty + "'></span>",
            
tagVline"<span class='" opts.classNames.vline "'></span>",
            
tagExpander"<span class='" opts.classNames.expander "'></span>",
            
tagConnector"<span class='" opts.classNames.connector "'></span>",
            
tagNodeIcon"<span class='" opts.classNames.nodeIcon "'></span>",
            
tagCheckbox"<span class='" opts.classNames.checkbox "'></span>",
            
lastentryundefined
        
};

        
// Clear container, in case it contained some 'waiting' or 'error' text
        // for clients that don't support JS.
        // We don't do this however, if we try to load from an embedded UL element.
        
if( opts.children || (opts.initAjax && opts.initAjax.url) || opts.initId ){
            $(
this.divTree).empty();
        }
        var 
$ulInitialize this.$tree.find(">ul:first").hide();

        
// Create the root element
        
this.tnRoot = new DynaTreeNode(nullthis, {});
        
this.tnRoot.bExpanded true;
        
this.tnRoot.render();
        
this.divTree.appendChild(this.tnRoot.ul);

        var 
root this.tnRoot,
            
isReloading = ( opts.persist && this.persistence.isReloading() ),
            
isLazy false,
            
prevFlag this.enableUpdate(false);

        
this.logDebug("Dynatree._load(): read tree structure...");

        
// Init tree structure
        
if( opts.children ) {
            
// Read structure from node array
            
root.addChild(opts.children);

        } else if( 
opts.initAjax && opts.initAjax.url ) {
            
// Init tree from AJAX request
            
isLazy true;
            
root.data.isLazy true;
            
this._reloadAjax(callback);

        } else if( 
opts.initId ) {
            
// Init tree from another UL element
            
this._createFromTag(root, $("#"+opts.initId));

        } else {
            
// Init tree from the first UL element inside the container <div>
//            var $ul = this.$tree.find(">ul:first").hide();
            
this._createFromTag(root$ulInitialize);
            
$ulInitialize.remove();
        }

        
this._checkConsistency();
        
// Fix part-sel flags
        
if(!isLazy && opts.selectMode == 3){
            
root._updatePartSelectionState();
        }
        
// Render html markup
        
this.logDebug("Dynatree._load(): render nodes...");
        
this.enableUpdate(prevFlag);

        
// bind event handlers
        
this.logDebug("Dynatree._load(): bind events...");
        
this.$widget.bind();

        
// --- Post-load processing
        
this.logDebug("Dynatree._load(): postInit...");
        
this.phase "postInit";

        
// In persist mode, make sure that cookies are written, even if they are empty
        
if( opts.persist ) {
            
this.persistence.write();
        }
        
// Set focus, if possible (this will also fire an event and write a cookie)
        
if( this.focusNode && this.focusNode.isVisible() ) {
            
this.logDebug("Focus on init: %o"this.focusNode);
            
this.focusNode.focus();
        }
        if( !
isLazy ) {
            if( 
opts.onPostInit ) {
                
opts.onPostInit.call(thisisReloadingfalse);
            }
            if( 
callback ){
                
callback.call(this"ok");
            }
        }
        
this.phase "idle";
    },

    
_reloadAjax: function(callback) {
        
// Reload
        
var opts this.options;
        if( ! 
opts.initAjax || ! opts.initAjax.url ){
            throw 
"tree.reload() requires 'initAjax' mode.";
        }
        var 
pers this.persistence;
        var 
ajaxOpts = $.extend({}, opts.initAjax);
        
// Append cookie info to the request
//        this.logDebug("reloadAjax: key=%o, an.key:%o", pers.activeKey, this.activeNode?this.activeNode.data.key:"?");
        
if( ajaxOpts.addActiveKey ){
            
ajaxOpts.data.activeKey pers.activeKey;
        }
        if( 
ajaxOpts.addFocusedKey ){
            
ajaxOpts.data.focusedKey pers.focusedKey;
        }
        if( 
ajaxOpts.addExpandedKeyList ){
            
ajaxOpts.data.expandedKeyList pers.expandedKeyList.join(",");
        }
        if( 
ajaxOpts.addSelectedKeyList ){
            
ajaxOpts.data.selectedKeyList pers.selectedKeyList.join(",");
        }
        
// Set up onPostInit callback to be called when Ajax returns
        
if( ajaxOpts.success ){
            
this.logWarning("initAjax: success callback is ignored; use onPostInit instead.");
        }
        if( 
ajaxOpts.error ){
            
this.logWarning("initAjax: error callback is ignored; use onPostInit instead.");
        }
        var 
isReloading pers.isReloading();
        
ajaxOpts.success = function(dtnodedatatextStatus) {
            if(
opts.selectMode == 3){
                
dtnode.tree.tnRoot._updatePartSelectionState();
            }
            if(
opts.onPostInit){
                
opts.onPostInit.call(dtnode.treeisReloadingfalse);
            }
            if(
callback){
                
callback.call(dtnode.tree"ok");
            }
        };
        
ajaxOpts.error = function(dtnodeXMLHttpRequesttextStatuserrorThrown) {
            if(
opts.onPostInit){
                
opts.onPostInit.call(dtnode.treeisReloadingtrueXMLHttpRequesttextStatuserrorThrown);
            }
            if(
callback){
                
callback.call(dtnode.tree"error"XMLHttpRequesttextStatuserrorThrown);
            }
        };
//        }
        
this.logDebug("Dynatree._init(): send Ajax request...");
        
this.tnRoot.appendAjax(ajaxOpts);
    },

    
toString: function() {
//        return "DynaTree '" + this.options.title + "'";
        
return "Dynatree '" this.$tree.attr("id") + "'";
    },

    
toDict: function() {
        return 
this.tnRoot.toDict(true);
    },

    
serializeArray: function(stopOnParents) {
        
// Return a JavaScript array of objects, ready to be encoded as a JSON
        // string for selected nodes
        
var nodeList this.getSelectedNodes(stopOnParents),
            
name this.$tree.attr("name") || this.$tree.attr("id"),
            
arr = [];
        for(var 
i=0l=nodeList.lengthi<li++){
            
arr.push({namenamevaluenodeList[i].data.key});
        }
        return 
arr;
    },

    
getPersistData: function() {
        return 
this.persistence.toDict();
    },

    
logDebug: function(msg) {
        if( 
this.options.debugLevel >= ) {
            Array.
prototype.unshift.apply(arguments, ["debug"]);
            
_log.apply(thisarguments);
        }
    },

    
logInfo: function(msg) {
        if( 
this.options.debugLevel >= ) {
            Array.
prototype.unshift.apply(arguments, ["info"]);
            
_log.apply(thisarguments);
        }
    },

    
logWarning: function(msg) {
        Array.
prototype.unshift.apply(arguments, ["warn"]);
        
_log.apply(thisarguments);
    },

    
isInitializing: function() {
        return ( 
this.phase=="init" || this.phase=="postInit" );
    },
    
isReloading: function() {
        return ( 
this.phase=="init" || this.phase=="postInit" ) && this.options.persist && this.persistence.cookiesFound;
    },
    
isUserEvent: function() {
        return ( 
this.phase=="userEvent" );
    },

    
redraw: function() {
//        this.logDebug("dynatree.redraw()...");
        
this.tnRoot.render(falsefalse);
//        this.logDebug("dynatree.redraw() done.");
    
},
    
renderInvisibleNodes: function() {
        
this.tnRoot.render(falsetrue);
    },
    
reload: function(callback) {
        
this._load(callback);
    },

    
getRoot: function() {
        return 
this.tnRoot;
    },

    
enable: function() {
        
this.$widget.enable();
    },

    
disable: function() {
        
this.$widget.disable();
    },

    
getNodeByKey: function(key) {
        
// Search the DOM by element ID (assuming this is faster than traversing all nodes).
        // $("#...") has problems, if the key contains '.', so we use getElementById()
        
var el document.getElementById(this.options.idPrefix key);
        if( 
el ){
            return 
el.dtnode el.dtnode null;
        }
        
// Not found in the DOM, but still may be in an unrendered part of tree
        
var match null;
        
this.visit(function(node){
//            window.console.log("%s", node);
            
if(node.data.key === key) {
                
match node;
                return 
false;
            }
        }, 
true);
        return 
match;
    },

    
getActiveNode: function() {
        return 
this.activeNode;
    },

    
reactivate: function(setFocus) {
        
// Re-fire onQueryActivate and onActivate events.
        
var node this.activeNode;
//        this.logDebug("reactivate %o", node);
        
if( node ) {
            
this.activeNode null// Force re-activating
            
node.activate();
            if( 
setFocus ){
                
node.focus();
            }
        }
    },

    
getSelectedNodes: function(stopOnParents) {
        var 
nodeList = [];
        
this.tnRoot.visit(function(node){
            if( 
node.bSelected ) {
                
nodeList.push(node);
                if( 
stopOnParents === true ){
                    return 
"skip"// stop processing this branch
                
}
            }
        });
        return 
nodeList;
    },

    
activateKey: function(key) {
        var 
dtnode = (key === null) ? null this.getNodeByKey(key);
        if( !
dtnode ) {
            if( 
this.activeNode ){
                
this.activeNode.deactivate();
            }
            
this.activeNode null;
            return 
null;
        }
        
dtnode.focus();
        
dtnode.activate();
        return 
dtnode;
    },

    
loadKeyPath: function(keyPathcallback) {
        var 
segList keyPath.split(this.options.keyPathSeparator);
        
// Remove leading '/'
        
if(segList[0] === ""){
            
segList.shift();
        }
        
// Remove leading system root key
        
if(segList[0] == this.tnRoot.data.key){
            
this.logDebug("Removed leading root key.");
            
segList.shift();
        }
        
keyPath segList.join(this.options.keyPathSeparator);
        return 
this.tnRoot._loadKeyPath(keyPathcallback);
    },

    
selectKey: function(keyselect) {
        var 
dtnode this.getNodeByKey(key);
        if( !
dtnode ){
            return 
null;
        }
        
dtnode.select(select);
        return 
dtnode;
    },

    
enableUpdate: function(bEnable) {
        if ( 
this.bEnableUpdate==bEnable ){
            return 
bEnable;
        }
        
this.bEnableUpdate bEnable;
        if ( 
bEnable ){
            
this.redraw();
        }
        return !
bEnable// return previous value
    
},

    
count: function() {
        return 
this.tnRoot.countChildren();
    },

    
visit: function(fnincludeRoot) {
        return 
this.tnRoot.visit(fnincludeRoot);
    },

    
_createFromTag: function(parentTreeNode$ulParent) {
        
// Convert a <UL>...</UL> list into children of the parent tree node.
        
var self this;
/*
TODO: better?
        this.$lis = $("li:has(a[href])", this.element);
        this.$tabs = this.$lis.map(function() { return $("a", this)[0]; });
 */
        
$ulParent.find(">li").each(function() {
            var 
$li = $(this),
                
$liSpan $li.find(">span:first"),
                
$liA $li.find(">a:first"),
                
title,
                
href null,
                
target null,
                
tooltip;
            if( 
$liSpan.length ) {
                
// If a <li><span> tag is specified, use it literally.
                
title $liSpan.html();
            } else if( 
$liA.length ) {
                
title $liA.html();
                
href $liA.attr("href");
                
target $liA.attr("target");
                
tooltip $liA.attr("title");
            } else {
                
// If only a <li> tag is specified, use the trimmed string up to
                // the next child <ul> tag.
                
title $li.html();
                var 
iPos title.search(/<ul/i);
                if( 
iPos >= ){
                    
title = $.trim(title.substring(0iPos));
                }else{
                    
title = $.trim(title);
                }
//                self.logDebug("%o", title);
            
}
            
// Parse node options from ID, title and class attributes
            
var data = {
                
titletitle,
                
tooltiptooltip,
                
isFolder$li.hasClass("folder"),
                
isLazy$li.hasClass("lazy"),
                
expand$li.hasClass("expanded"),
                
select$li.hasClass("selected"),
                
activate$li.hasClass("active"),
                
focus$li.hasClass("focused"),
                
noLink$li.hasClass("noLink")
            };
            if( 
href ){
                
data.href href;
                
data.target target;
            }
            if( 
$li.attr("title") ){
                
data.tooltip $li.attr("title"); // overrides <a title='...'>
            
}
            if( 
$li.attr("id") ){
                
data.key "" $li.attr("id");
            }
            
// If a data attribute is present, evaluate as a JavaScript object
            
if( $li.attr("data") ) {
                var 
dataAttr = $.trim($li.attr("data"));
                if( 
dataAttr ) {
                    if( 
dataAttr.charAt(0) != "{" ){
                        
dataAttr "{" dataAttr "}";
                    }
                    try {
                        $.
extend(data, eval("(" dataAttr ")"));
                    } catch(
e) {
                        throw (
"Error parsing node data: " "ndata:n'" dataAttr "'");
                    }
                }
            }
            var 
childNode parentTreeNode.addChild(data);
            
// Recursive reading of child nodes, if LI tag contains an UL tag
            
var $ul $li.find(">ul:first");
            if( 
$ul.length ) {
                
self._createFromTag(childNode$ul); // must use 'self', because 'this' is the each() context
            
}
        });
    },

    
_checkConsistency: function() {
//        this.logDebug("tree._checkConsistency() NOT IMPLEMENTED - %o", this);
    
},

    
_setDndStatus: function(sourceNodetargetNodehelperhitModeaccept) {
        
// hitMode: 'after', 'before', 'over', 'out', 'start', 'stop'
        
var $source sourceNode ? $(sourceNode.span) : null,
            
$target = $(targetNode.span);
        if( !
this.$dndMarker ) {
            
this.$dndMarker = $("<div id='dynatree-drop-marker'></div>")
                .
hide()
                .
css({"z-index"1000})
                .
prependTo($(this.divTree).parent());

//            logMsg("Creating marker: %o", this.$dndMarker);
        
}
/*
        if(hitMode === "start"){
        }
        if(hitMode === "stop"){
//            sourceNode.removeClass("dynatree-drop-target");
        }
*/
        
if(hitMode === "after" || hitMode === "before" || hitMode === "over"){
//            $source && $source.addClass("dynatree-drag-source");
//            $target.addClass("dynatree-drop-target");

            
var markerOffset "0 0";
            
/* concrete5 */
            
var my "left center";
            var 
at "left center";
            
/* end concrete5 */

            
switch(hitMode){
            case 
"before":
                
this.$dndMarker.removeClass("dynatree-drop-after dynatree-drop-over");
                
this.$dndMarker.addClass("dynatree-drop-before");
                
markerOffset "0 -8";
                
/* concrete5 */
                  
my "left bottom";
                  
at "left center";
                
/* end concrete5 */
                
break;
            case 
"after":
                
this.$dndMarker.removeClass("dynatree-drop-before dynatree-drop-over");
                
this.$dndMarker.addClass("dynatree-drop-after");
                
markerOffset "0 8";
    
my "left center";
    
at "left bottom";
                break;
            default:
                
this.$dndMarker.removeClass("dynatree-drop-after dynatree-drop-before");
                
this.$dndMarker.addClass("dynatree-drop-over");
                
$target.addClass("dynatree-drop-target");
                
markerOffset "8 0";
            }
//            logMsg("Creating marker: %o", this.$dndMarker);
//            logMsg("    $target.offset=%o", $target);
//            logMsg("    pos/$target.offset=%o", pos);
//            logMsg("    $target.position=%o", $target.position());
//            logMsg("    $target.offsetParent=%o, ot:%o", $target.offsetParent(), $target.offsetParent().offset());
//            logMsg("    $(this.divTree).offset=%o", $(this.divTree).offset());
//            logMsg("    $(this.divTree).parent=%o", $(this.divTree).parent());
//            var pos = $target.offset();
//            var parentPos = $target.offsetParent().offset();
//            var bodyPos = $target.offsetParent().offset();

            
this.$dndMarker
                
.show()
                .
position({
                    
/* concrete5 */
                    
mymy,
                    
atat,
                    
/* end concrete5 */
                    
of$target,
                    
offsetmarkerOffset
                
});

//            helper.addClass("dynatree-drop-hover");
        
} else {
//            $source && $source.removeClass("dynatree-drag-source");
            
$target.removeClass("dynatree-drop-target");
            
this.$dndMarker.hide();
//            helper.removeClass("dynatree-drop-hover");
        
}
        if(
hitMode === "after"){
            
$target.addClass("dynatree-drop-after");
        } else {
            
$target.removeClass("dynatree-drop-after");
        }
        if(
hitMode === "before"){
            
$target.addClass("dynatree-drop-before");
        } else {
            
$target.removeClass("dynatree-drop-before");
        }
        if(
accept === true){
            if(
$source){
                
$source.addClass("dynatree-drop-accept");
            }
            
$target.addClass("dynatree-drop-accept");
            
helper.addClass("dynatree-drop-accept");
        }else{
            if(
$source){
                
$source.removeClass("dynatree-drop-accept");
            }
            
$target.removeClass("dynatree-drop-accept");
            
helper.removeClass("dynatree-drop-accept");
        }
        if(
accept === false){
            if(
$source){
                
$source.addClass("dynatree-drop-reject");
            }
            
$target.addClass("dynatree-drop-reject");
            
helper.addClass("dynatree-drop-reject");
        }else{
            if(
$source){
                
$source.removeClass("dynatree-drop-reject");
            }
            
$target.removeClass("dynatree-drop-reject");
            
helper.removeClass("dynatree-drop-reject");
        }
    },

    
_onDragEvent: function(eventNamenodeotherNodeeventuidraggable) {
        
/**
         * Handles drag'n'drop functionality.
         *
         * A standard jQuery drag-and-drop process may generate these calls:
         *
         * draggable helper():
         *     _onDragEvent("helper", sourceNode, null, event, null, null);
         * start:
         *     _onDragEvent("start", sourceNode, null, event, ui, draggable);
         * drag:
         *     _onDragEvent("leave", prevTargetNode, sourceNode, event, ui, draggable);
         *     _onDragEvent("over", targetNode, sourceNode, event, ui, draggable);
         *     _onDragEvent("enter", targetNode, sourceNode, event, ui, draggable);
         * stop:
         *     _onDragEvent("drop", targetNode, sourceNode, event, ui, draggable);
         *     _onDragEvent("leave", targetNode, sourceNode, event, ui, draggable);
         *     _onDragEvent("stop", sourceNode, null, event, ui, draggable);
         */
//        if(eventName !== "over"){
//            this.logDebug("tree._onDragEvent(%s, %o, %o) - %o", eventName, node, otherNode, this);
//        }
        
var opts this.options,
            
dnd this.options.dnd,
            
res null,
            
nodeTag = $(node.span),
            
hitMode,
            
enterResponse;

        switch (
eventName) {
        case 
"helper":
            
// Only event and node argument is available
            
var $helper = $("<div class='dynatree-drag-helper'><span class='dynatree-drag-helper-img' /></div>")
                .
append($(event.target).closest(".dynatree-title").clone());
//                .append($(event.target).closest('a').clone());
            // issue 244: helper should be child of scrollParent
            
$("ul.dynatree-container"node.tree.divTree).append($helper);
//            $(node.tree.divTree).append($helper);
            // Attach node reference to helper object
            
$helper.data("dtSourceNode"node);
//            this.logDebug("helper=%o", $helper);
//            this.logDebug("helper.sourceNode=%o", $helper.data("dtSourceNode"));
            
res $helper;
            break;
        case 
"start":
            if(
node.isStatusNode()) {
                
res false;
            } else if(
dnd.onDragStart) {
                
res dnd.onDragStart(node);
            }
            if(
res === false) {
                
this.logDebug("tree.onDragStart() cancelled");
                
//draggable._clear();
                // NOTE: the return value seems to be ignored (drag is not canceled, when false is returned)
                
ui.helper.trigger("mouseup");
                
ui.helper.hide();
            } else {
                
nodeTag.addClass("dynatree-drag-source");
            }
            break;
        case 
"enter":
            
res dnd.onDragEnter dnd.onDragEnter(nodeotherNode) : null;
            if(!
res){
                
// convert null, undefined, false to false
                
res false;
            }else{
                
res = {
                    
over: ((res === true) || (res === "over") || $.inArray("over"res) >= 0),
                    
before: ((res === true) || (res === "before") || $.inArray("before"res) >= 0),
                    
after: ((res === true) || (res === "after") || $.inArray("after"res) >= 0)
                };
            }
            
ui.helper.data("enterResponse"res);
//            this.logDebug("helper.enterResponse: %o", res);
            
break;
        case 
"over":
            
enterResponse ui.helper.data("enterResponse");
            
hitMode null;
            if(
enterResponse === false){
                
// Don't call onDragOver if onEnter returned false.
                // issue 332
//                break;
            
} else if(typeof enterResponse === "string") {
                
// Use hitMode from onEnter if provided.
                
hitMode enterResponse;
            } else {
                
// Calculate hitMode from relative cursor position.
                
var nodeOfs nodeTag.offset();
//                var relPos = { x: event.clientX - nodeOfs.left,
//                            y: event.clientY - nodeOfs.top };
//                nodeOfs.top += this.parentTop;
//                nodeOfs.left += this.parentLeft;
                
var relPos = { xevent.pageX nodeOfs.left,
                               
yevent.pageY nodeOfs.top };
                var 
relPos2 = { xrelPos.nodeTag.width(),
                                
yrelPos.nodeTag.height() };
//                this.logDebug("event.page: %s/%s", event.pageX, event.pageY);
//                this.logDebug("event.client: %s/%s", event.clientX, event.clientY);
//                this.logDebug("nodeOfs: %s/%s", nodeOfs.left, nodeOfs.top);
////                this.logDebug("parent: %s/%s", this.parentLeft, this.parentTop);
//                this.logDebug("relPos: %s/%s", relPos.x, relPos.y);
//                this.logDebug("relPos2: %s/%s", relPos2.x, relPos2.y);
                
if( enterResponse.after && relPos2.0.75 ){
                    
hitMode "after";
                } else if(!
enterResponse.over && enterResponse.after && relPos2.0.5 ){
                    
hitMode "after";
                } else if(
enterResponse.before && relPos2.<= 0.25) {
                    
hitMode "before";
                } else if(!
enterResponse.over && enterResponse.before && relPos2.<= 0.5) {
                    
hitMode "before";
                } else if(
enterResponse.over) {
                    
hitMode "over";
                }
                
// Prevent no-ops like 'before source node'
                // TODO: these are no-ops when moving nodes, but not in copy mode
                
if( dnd.preventVoidMoves ){
                    if(
node === otherNode){
//                        this.logDebug("    drop over source node prevented");
                        
hitMode null;
                    }else if(
hitMode === "before" && otherNode && node === otherNode.getNextSibling()){
//                        this.logDebug("    drop after source node prevented");
                        
hitMode null;
                    }else if(
hitMode === "after" && otherNode && node === otherNode.getPrevSibling()){
//                        this.logDebug("    drop before source node prevented");
                        
hitMode null;
                    }else if(
hitMode === "over" && otherNode
                            
&& otherNode.parent === node && otherNode.isLastSibling() ){
//                        this.logDebug("    drop last child over own parent prevented");
                        
hitMode null;
                    }
                }
//                this.logDebug("hitMode: %s - %s - %s", hitMode, (node.parent === otherNode), node.isLastSibling());
                
ui.helper.data("hitMode"hitMode);
            }
            
// Auto-expand node (only when 'over' the node, not 'before', or 'after')
            
if(hitMode === "over"
                
&& dnd.autoExpandMS && node.hasChildren() !== false && !node.bExpanded) {
                
node.scheduleAction("expand"dnd.autoExpandMS);
            }
            if(
hitMode && dnd.onDragOver){
                
res dnd.onDragOver(nodeotherNodehitMode);
                if(
res === "over" || res === "before" || res === "after") {
                    
hitMode res;
                }
            }
            
// issue 332
//            this._setDndStatus(otherNode, node, ui.helper, hitMode, res!==false);
            
this._setDndStatus(otherNodenodeui.helperhitModeres!==false && hitMode !== null);
            break;
        case 
"drop":
            
// issue 286: don't trigger onDrop, if DnD status is 'reject'
            
var isForbidden ui.helper.hasClass("dynatree-drop-reject");
            
hitMode ui.helper.data("hitMode");
            if(
hitMode && dnd.onDrop && !isForbidden){
                
dnd.onDrop(nodeotherNodehitModeuidraggable);
            }
            break;
        case 
"leave":
            
// Cancel pending expand request
            
node.scheduleAction("cancel");
            
ui.helper.data("enterResponse"null);
            
ui.helper.data("hitMode"null);
            
this._setDndStatus(otherNodenodeui.helper"out"undefined);
            if(
dnd.onDragLeave){
                
dnd.onDragLeave(nodeotherNode);
            }
            break;
        case 
"stop":
            
nodeTag.removeClass("dynatree-drag-source");
            if(
dnd.onDragStop){
                
dnd.onDragStop(node);
            }
            break;
        default:
            throw 
"Unsupported drag event: " eventName;
        }
        return 
res;
    },

    
cancelDrag: function() {
         var 
dd = $.ui.ddmanager.current;
         if(
dd){
             
dd.cancel();
         }
    },

    
// --- end of class
    
lastentryundefined
};

/*************************************************************************
 * Widget $(..).dynatree
 */

$.widget("ui.dynatree", {
/*
    init: function() {
        // ui.core 1.6 renamed init() to _init(): this stub assures backward compatibility
        _log("warn", "ui.dynatree.init() was called; you should upgrade to jquery.ui.core.js v1.8 or higher.");
        return this._init();
    },
 */
    
_init: function() {
//        if( parseFloat($.ui.version) < 1.8 ) {
        
if(versionCompare($.ui.version"1.8") < 0){
            
// jquery.ui.core 1.8 renamed _init() to _create(): this stub assures backward compatibility
            
if(this.options.debugLevel >= 0){
                
_log("warn""ui.dynatree._init() was called; you should upgrade to jquery.ui.core.js v1.8 or higher.");
            }
            return 
this._create();
        }
        
// jquery.ui.core 1.8 still uses _init() to perform "default functionality"
        
if(this.options.debugLevel >= 2){
            
_log("debug""ui.dynatree._init() was called; no current default functionality.");
        }
    },

    
_create: function() {
        var 
opts this.options;
        if(
opts.debugLevel >= 1){
            
logMsg("Dynatree._create(): version='%s', debugLevel=%o.", $.ui.dynatree.versionthis.options.debugLevel);
        }
        
// The widget framework supplies this.element and this.options.
        
this.options.event += ".dynatree"// namespace event

        
var divTree this.element.get(0);
/*        // Clear container, in case it contained some 'waiting' or 'error' text
        // for clients that don't support JS
        if( opts.children || (opts.initAjax && opts.initAjax.url) || opts.initId )
            $(divTree).empty();
*/
        // Create the DynaTree object
        
this.tree = new DynaTree(this);
        
this.tree._load();
        
this.tree.logDebug("Dynatree._init(): done.");
    },

    
bind: function() {
        
// Prevent duplicate binding
        
this.unbind();

        var 
eventNames "click.dynatree dblclick.dynatree";
        if( 
this.options.keyboard ){
            
// Note: leading ' '!
            
eventNames += " keypress.dynatree keydown.dynatree";
        }
        
this.element.bind(eventNames, function(event){
            var 
dtnode = $.ui.dynatree.getNode(event.target);
            if( !
dtnode ){
                return 
true;  // Allow bubbling of other events
            
}
            var 
tree dtnode.tree;
            var 
tree.options;
            
tree.logDebug("event(%s): dtnode: %s"event.typedtnode);
            var 
prevPhase tree.phase;
            
tree.phase "userEvent";
            try {
                switch(
event.type) {
                case 
"click":
                    return ( 
o.onClick && o.onClick.call(treedtnodeevent)===false ) ? false dtnode._onClick(event);
                case 
"dblclick":
                    return ( 
o.onDblClick && o.onDblClick.call(treedtnodeevent)===false ) ? false dtnode._onDblClick(event);
                case 
"keydown":
                    return ( 
o.onKeydown && o.onKeydown.call(treedtnodeevent)===false ) ? false dtnode._onKeydown(event);
                case 
"keypress":
                    return ( 
o.onKeypress && o.onKeypress.call(treedtnodeevent)===false ) ? false dtnode._onKeypress(event);
                }
            } catch(
e) {
                var 
null// issue 117
                
tree.logWarning("bind(%o): dtnode: %o, error: %o"eventdtnodee);
            } finally {
                
tree.phase prevPhase;
            }
        });

        
// focus/blur don't bubble, i.e. are not delegated to parent <div> tags,
        // so we use the addEventListener capturing phase.
        // See http://www.howtocreate.co.uk/tutorials/javascript/domevents
        
function __focusHandler(event) {
            
// Handles blur and focus.
            // Fix event for IE:
            // doesn't pass JSLint:
//            event = arguments[0] = $.event.fix( event || window.event );
            // what jQuery does:
//            var args = jQuery.makeArray( arguments );
//            event = args[0] = jQuery.event.fix( event || window.event );
            
event = $.event.fixevent || window.event );
            var 
dtnode = $.ui.dynatree.getNode(event.target);
            return 
dtnode dtnode._onFocus(event) : false;
        }
        var 
div this.tree.divTree;

        if( 
div.addEventListener ) {
            
div.addEventListener("focus"__focusHandlertrue);
            
div.addEventListener("blur"__focusHandlertrue);
        } else {
            
div.onfocusin div.onfocusout __focusHandler;
        }
        
// EVENTS
        // disable click if event is configured to something else
//        if (!(/^click/).test(o.event))
//            this.$tabs.bind("click.tabs", function() { return false; });

    
},

    
unbind: function() {
        
this.element.unbind(".dynatree");
    },

/* TODO: we could handle option changes during runtime here (maybe to re-render, ...)
    setData: function(key, value) {
        this.tree.logDebug("dynatree.setData('" + key + "', '" + value + "')");
    },
*/
    
enable: function() {
        
this.bind();
        
// Call default disable(): remove -disabled from css:
        
$.Widget.prototype.enable.apply(thisarguments);
    },

    
disable: function() {
        
this.unbind();
        
// Call default disable(): add -disabled to css:
        
$.Widget.prototype.disable.apply(thisarguments);
    },

    
// --- getter methods (i.e. NOT returning a reference to $)
    
getTree: function() {
        return 
this.tree;
    },

    
getRoot: function() {
        return 
this.tree.getRoot();
    },

    
getActiveNode: function() {
        return 
this.tree.getActiveNode();
    },

    
getSelectedNodes: function() {
        return 
this.tree.getSelectedNodes();
    },

    
// ------------------------------------------------------------------------
    
lastentryundefined
});


// The following methods return a value (thus breaking the jQuery call chain):
if(versionCompare($.ui.version"1.8") < 0){
//if( parseFloat($.ui.version) < 1.8 ) {
    
$.ui.dynatree.getter "getTree getRoot getActiveNode getSelectedNodes";
}

/*******************************************************************************
 * Tools in ui.dynatree namespace
 */
$.ui.dynatree.version "$Version:$";

/**
 * Return a DynaTreeNode object for a given DOM element
 */
$.ui.dynatree.getNode = function(el) {
    if(
el instanceof DynaTreeNode){
        return 
el// el already was a DynaTreeNode
    
}
    if(
el.selector !== undefined){
        
el el[0]; // el was a jQuery object: use the DOM element
    
}
    
// TODO: for some reason $el.parents("[dtnode]") does not work (jQuery 1.6.1)
    // maybe, because dtnode is a property, not an attribute
    
while( el ) {
        if(
el.dtnode) {
            return 
el.dtnode;
        }
        
el el.parentNode;
    }
    return 
null;
/*
    var $el = el.selector === undefined ? $(el) : el,
//        parent = $el.closest("[dtnode]"),
//        parent = $el.parents("[dtnode]").first(),
        useProp = (typeof $el.prop == "function"),
        node;
    $el.parents().each(function(){
        node = useProp ? $(this).prop("dtnode") : $(this).attr("dtnode");
        if(node){
            return false;
        }
    });
    return node;
*/
};

/**Return persistence information from cookies.*/
$.ui.dynatree.getPersistData DynaTreeStatus._getTreePersistData;

/*******************************************************************************
 * Plugin default options:
 */
$.ui.dynatree.prototype.options = {
    
title"Dynatree"// Tree's name (only used for debug output)
    
minExpandLevel1// 1: root node is not collapsible
    
imagePathnull// Path to a folder containing icons. Defaults to 'skin/' subdirectory.
    
childrennull// Init tree structure from this object array.
    
initIdnull// Init tree structure from a <ul> element with this ID.
    
initAjaxnull// Ajax options used to initialize the tree strucuture.
    
autoFocustrue// Set focus to first child, when expanding or lazy-loading.
    
keyboardtrue// Support keyboard navigation.
    
persistfalse// Persist expand-status to a cookie
    
autoCollapsefalse// Automatically collapse all siblings, when a node is expanded.
    
clickFolderMode3// 1:activate, 2:expand, 3:activate and expand
    
activeVisibletrue// Make sure, active nodes are visible (expanded).
    
checkboxfalse// Show checkboxes.
    
selectMode2// 1:single, 2:multi, 3:multi-hier
    
fxnull// Animations, e.g. null or { height: "toggle", duration: 200 }
    
noLinkfalse// Use <span> instead of <a> tags for all nodes
    // Low level event handlers: onEvent(dtnode, event): return false, to stop default processing
    
onClicknull// null: generate focus, expand, activate, select events.
    
onDblClicknull// (No default actions.)
    
onKeydownnull// null: generate keyboard navigation (focus, expand, activate).
    
onKeypressnull// (No default actions.)
    
onFocusnull// null: set focus to node.
    
onBlurnull// null: remove focus from node.

    // Pre-event handlers onQueryEvent(flag, dtnode): return false, to stop processing
    
onQueryActivatenull// Callback(flag, dtnode) before a node is (de)activated.
    
onQuerySelectnull// Callback(flag, dtnode) before a node is (de)selected.
    
onQueryExpandnull// Callback(flag, dtnode) before a node is expanded/collpsed.

    // High level event handlers
    
onPostInitnull// Callback(isReloading, isError) when tree was (re)loaded.
    
onActivatenull// Callback(dtnode) when a node is activated.
    
onDeactivatenull// Callback(dtnode) when a node is deactivated.
    
onSelectnull// Callback(flag, dtnode) when a node is (de)selected.
    
onExpandnull// Callback(flag, dtnode) when a node is expanded/collapsed.
    
onLazyReadnull// Callback(dtnode) when a lazy node is expanded for the first time.
    
onCustomRendernull// Callback(dtnode) before a node is rendered. Return a HTML string to override.
    
onCreatenull// Callback(dtnode, nodeSpan) after a node was rendered for the first time.
    
onRendernull// Callback(dtnode, nodeSpan) after a node was rendered.
                // postProcess is similar to the standard dataFilter hook,
                // but it is also called for JSONP
    
postProcessnull// Callback(data, dataType) before an Ajax result is passed to dynatree

    // Drag'n'drop support
    
dnd: {
        
// Make tree nodes draggable:
        
onDragStartnull// Callback(sourceNode), return true, to enable dnd
        
onDragStopnull// Callback(sourceNode)
//        helper: null,
        // Make tree nodes accept draggables
        
autoExpandMS1000// Expand nodes after n milliseconds of hovering.
        
preventVoidMovestrue// Prevent dropping nodes 'before self', etc.
        
onDragEnternull// Callback(targetNode, sourceNode)
        
onDragOvernull// Callback(targetNode, sourceNode, hitMode)
        
onDropnull// Callback(targetNode, sourceNode, hitMode)
        
onDragLeavenull // Callback(targetNode, sourceNode)
    
},
    
ajaxDefaults: { // Used by initAjax option
        
cachefalse// false: Append random '_' argument to the request url to prevent caching.
        
timeout0// >0: Make sure we get an ajax error for invalid URLs
        
dataType"json" // Expect json format and pass json object to callbacks.
    
},
    
strings: {
        
loading"Loading&#8230;",
        
loadError"Load error!"
    
},
    
generateIdsfalse// Generate id attributes like <span id='dynatree-id-KEY'>
    
idPrefix"dynatree-id-"// Used to generate node id's like <span id="dynatree-id-<key>">.
    
keyPathSeparator"/"// Used by node.getKeyPath() and tree.loadKeyPath().
//    cookieId: "dynatree-cookie", // Choose a more unique name, to allow multiple trees.
    
cookieId"dynatree"// Choose a more unique name, to allow multiple trees.
    
cookie: {
        
expiresnull //7, // Days or Date; null: session cookie
//        path: "/", // Defaults to current page
//        domain: "jquery.com",
//        secure: true
    
},
    
// Class names used, when rendering the HTML markup.
    // Note: if only single entries are passed for options.classNames, all other
    // values are still set to default.
    
classNames: {
        
container"dynatree-container",
        
node"dynatree-node",
        
folder"dynatree-folder",
//        document: "dynatree-document",

        
empty: "dynatree-empty",
        
vline"dynatree-vline",
        
expander"dynatree-expander",
        
connector"dynatree-connector",
        
checkbox"dynatree-checkbox",
        
nodeIcon"dynatree-icon",
        
title"dynatree-title",
        
noConnector"dynatree-no-connector",

        
nodeError"dynatree-statusnode-error",
        
nodeWait"dynatree-statusnode-wait",
        
hidden"dynatree-hidden",
        
combinedExpanderPrefix"dynatree-exp-",
        
combinedIconPrefix"dynatree-ico-",
        
nodeLoading"dynatree-loading",
//        disabled: "dynatree-disabled",
        
hasChildren"dynatree-has-children",
        
active"dynatree-active",
        
selected"dynatree-selected",
        
expanded"dynatree-expanded",
        
lazy"dynatree-lazy",
        
focused"dynatree-focused",
        
partsel"dynatree-partsel",
        
lastsib"dynatree-lastsib"
    
},
    
debugLevel2// 0:quiet, 1:normal, 2:debug $REPLACE:    debugLevel: 1,

    // ------------------------------------------------------------------------
    
lastentryundefined
};
//
if(versionCompare($.ui.version"1.8") < 0){
//if( parseFloat($.ui.version) < 1.8 ) {
    
$.ui.dynatree.defaults = $.ui.dynatree.prototype.options;
}

/*******************************************************************************
 * Reserved data attributes for a tree node.
 */
$.ui.dynatree.nodedatadefaults = {
    
titlenull// (required) Displayed name of the node (html is allowed here)
    
keynull// May be used with activate(), select(), find(), ...
    
isFolderfalse// Use a folder icon. Also the node is expandable but not selectable.
    
isLazyfalse// Call onLazyRead(), when the node is expanded for the first time to allow for delayed creation of children.
    
tooltipnull// Show this popup text.
    
hrefnull// Added to the generated <a> tag.
    
iconnull// Use a custom image (filename relative to tree.options.imagePath). 'null' for default icon, 'false' for no icon.
    
addClassnull// Class name added to the node's span tag.
    
noLinkfalse// Use <span> instead of <a> tag for this node
    
activatefalse// Initial active status.
    
focusfalse// Initial focused status.
    
expandfalse// Initial expanded status.
    
selectfalse// Initial selected status.
    
hideCheckboxfalse// Suppress checkbox display for this node.
    
unselectablefalse// Prevent selection.
//  disabled: false,
    // The following attributes are only valid if passed to some functions:
    
childrennull// Array of child nodes.
    // NOTE: we can also add custom attributes here.
    // This may then also be used in the onActivate(), onSelect() or onLazyTree() callbacks.
    // ------------------------------------------------------------------------
    
lastentryundefined
};

/*******************************************************************************
 * Drag and drop support
 */
function _initDragAndDrop(tree) {
    var 
dnd tree.options.dnd || null;
    
// Register 'connectToDynatree' option with ui.draggable
    
if(dnd && (dnd.onDragStart || dnd.onDrop)) {
        
_registerDnd();
    }
    
// Attach ui.draggable to this Dynatree instance
    
if(dnd && dnd.onDragStart ) {
        
tree.$tree.draggable({
            
addClassesfalse,
            
appendTo"body",
            
containmentfalse,
            
delay0,
            
distance4,
            
revertfalse,
            
scrolltrue// issue 244: enable scrolling (if ul.dynatree-container)
            
scrollSpeed7,
            
scrollSensitivity10,
            
// Delegate draggable.start, drag, and stop events to our handler
            
connectToDynatreetrue,
            
// Let source tree create the helper element
            
helper: function(event) {
                var 
sourceNode = $.ui.dynatree.getNode(event.target);
                if(!
sourceNode){ // issue 211
                    
return "<div></div>";
                }
                return 
sourceNode.tree._onDragEvent("helper"sourceNodenulleventnullnull);
            },
            
start: function(eventui) {
                
// See issues 211, 268, 278
//                var sourceNode = $.ui.dynatree.getNode(event.target);
                
var sourceNode ui.helper.data("dtSourceNode");
                return !!
sourceNode// Abort dragging if no Node could be found
            
},
            
_lastnull
        
});
    }
    
// Attach ui.droppable to this Dynatree instance
    
if(dnd && dnd.onDrop) {
        
tree.$tree.droppable({
            
addClassesfalse,
            
tolerance"intersect",
            
greedyfalse,
            
_lastnull
        
});
    }
}

//--- Extend ui.draggable event handling --------------------------------------
var didRegisterDnd false;
var 
_registerDnd = function() {
    if(
didRegisterDnd){
        return;
    }
    
// Register proxy-functions for draggable.start/drag/stop
    
$.ui.plugin.add("draggable""connectToDynatree", {
        
start: function(eventui) {
            
// issue 386
            
var draggable = $(this).data("ui-draggable") || $(this).data("draggable"),
                
sourceNode ui.helper.data("dtSourceNode") || null;
//            logMsg("draggable-connectToDynatree.start, %s", sourceNode);
//            logMsg("    this: %o", this);
//            logMsg("    event: %o", event);
//            logMsg("    draggable: %o", draggable);
//            logMsg("    ui: %o", ui);

            
if(sourceNode) {
                
// Adjust helper offset, so cursor is slightly outside top/left corner
//                draggable.offset.click.top -= event.target.offsetTop;
//                draggable.offset.click.left -= event.target.offsetLeft;
                
draggable.offset.click.top = -2;
                
draggable.offset.click.left = + 16;
//                logMsg("    draggable2: %o", draggable);
//                logMsg("    draggable.offset.click FIXED: %s/%s", draggable.offset.click.left, draggable.offset.click.top);
                // Trigger onDragStart event
                // TODO: when called as connectTo..., the return value is ignored(?)
                
return sourceNode.tree._onDragEvent("start"sourceNodenulleventuidraggable);
            }
        },
        
drag: function(eventui) {
            
// issue 386
            
var draggable = $(this).data("ui-draggable") || $(this).data("draggable"),
                
sourceNode ui.helper.data("dtSourceNode") || null,
                
prevTargetNode ui.helper.data("dtTargetNode") || null,
                
targetNode = $.ui.dynatree.getNode(event.target);
//            logMsg("$.ui.dynatree.getNode(%o): %s", event.target, targetNode);
//            logMsg("connectToDynatree.drag: helper: %o", ui.helper[0]);
            
if(event.target && !targetNode){
                
// We got a drag event, but the targetNode could not be found
                // at the event location. This may happen,
                // 1. if the mouse jumped over the drag helper,
                // 2. or if non-dynatree element is dragged
                // We ignore it:
                
var isHelper = $(event.target).closest("div.dynatree-drag-helper,#dynatree-drop-marker").length 0;
                if(
isHelper){
//                    logMsg("Drag event over helper: ignored.");
                    
return;
                }
            }
//            logMsg("draggable-connectToDynatree.drag: targetNode(from event): %s, dtTargetNode: %s", targetNode, ui.helper.data("dtTargetNode"));
            
ui.helper.data("dtTargetNode"targetNode);
            
// Leaving a tree node
            
if(prevTargetNode && prevTargetNode !== targetNode ) {
                
prevTargetNode.tree._onDragEvent("leave"prevTargetNodesourceNodeeventuidraggable);
            }
            if(
targetNode){
                if(!
targetNode.tree.options.dnd.onDrop) {
                    
// not enabled as drop target
//                    noop(); // Keep JSLint happy
                
} else if(targetNode === prevTargetNode) {
                    
// Moving over same node
                    
targetNode.tree._onDragEvent("over"targetNodesourceNodeeventuidraggable);
                }else{
                    
// Entering this node first time
                    
targetNode.tree._onDragEvent("enter"targetNodesourceNodeeventuidraggable);
                }
            }
            
// else go ahead with standard event handling
        
},
        
stop: function(eventui) {
            
// issue 386
            
var draggable = $(this).data("ui-draggable") || $(this).data("draggable"),
                
sourceNode ui.helper.data("dtSourceNode") || null,
                
targetNode ui.helper.data("dtTargetNode") || null,
                
mouseDownEvent draggable._mouseDownEvent,
                
eventType event.type,
                
dropped = (eventType == "mouseup" && event.which == 1);
            
logMsg("draggable-connectToDynatree.stop: targetNode(from event): %s, dtTargetNode: %s"targetNodeui.helper.data("dtTargetNode"));
//            logMsg("draggable-connectToDynatree.stop, %s", sourceNode);
//            logMsg("    type: %o, downEvent: %o, upEvent: %o", eventType, mouseDownEvent, event);
//            logMsg("    targetNode: %o", targetNode);
            
if(!dropped){
                
logMsg("Drag was cancelled");
            }
            if(
targetNode) {
                if(
dropped){
                    
targetNode.tree._onDragEvent("drop"targetNodesourceNodeeventuidraggable);
                }
                
targetNode.tree._onDragEvent("leave"targetNodesourceNodeeventuidraggable);
            }
            if(
sourceNode){
                
sourceNode.tree._onDragEvent("stop"sourceNodenulleventuidraggable);
            }
        }
    });
    
didRegisterDnd true;
};

// ---------------------------------------------------------------------------
}(jQuery));
?>
Онлайн: 0
Реклама