// var $ul = this.$tree.find(">ul:first").hide();
this._createFromTag(root, $ulInitialize);
$ulInitialize.remove();
}
this._checkConsistency();
// 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 && opts.onPostInit) {
opts.onPostInit.call(this, isReloading, false);
}
this.phase = "idle";
},
// _setNoUpdate: function(silent) {
// // TODO: set options to disable and re-enable updates while loading
// var opts = this.options;
// var prev = {
// fx: opts.fx,
// autoFocus: opts.autoFocus,
// autoCollapse: opts.autoCollapse };
// if(silent === true){
// opts.autoFocus = false;
// opts.fx = null;
// opts.autoCollapse = false;
// } else {
// opts.autoFocus = silent.autoFocus;
// opts.fx = silent.fx;
// opts.autoCollapse = silent.autoCollapse;
// }
// return prev;
// },
_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 (opts.onPostInit) {
if (ajaxOpts.success) {
this.logWarning("initAjax: success callback is ignored when onPostInit was specified.");
}
if (ajaxOpts.error) {
this.logWarning("initAjax: error callback is ignored when onPostInit was specified.");
}
var isReloading = pers.isReloading();
ajaxOpts.success = function(dtnode) {
opts.onPostInit.call(dtnode.tree, isReloading, false);
if (callback) {
callback.call(dtnode.tree, "ok");
}
};
ajaxOpts.error = function(dtnode) {
opts.onPostInit.call(dtnode.tree, isReloading, true);
if (callback) {
callback.call(dtnode.tree, "error");
}
};
}
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 = 0, l = nodeList.length; i < l; i++) {
arr.push({name: name, value: nodeList[i].data.key});
}
return arr;
},
getPersistData: function() {
return this.persistence.toDict();
},
logDebug: function(msg) {
if (this.options.debugLevel >= 2) {
Array.prototype.unshift.apply(arguments, ["debug"]);
_log.apply(this, arguments);
}
},
logInfo: function(msg) {
if (this.options.debugLevel >= 1) {
Array.prototype.unshift.apply(arguments, ["info"]);
_log.apply(this, arguments);
}
},
logWarning: function(msg) {
Array.prototype.unshift.apply(arguments, ["warn"]);
_log.apply(this, arguments);
},
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(false, false);
// this.logDebug("dynatree.redraw() done.");
},
renderInvisibleNodes: function() {
this.tnRoot.render(false, true);
},
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(keyPath, callback) {
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(keyPath, callback);
},
selectKey: function(key, select) {
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(fn, includeRoot) {
return this.tnRoot.visit(fn, includeRoot);
},
_createFromTag: function(parentTreeNode, $ulParent) {
// Convert a
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;
if ($liSpan.length) {
// If a
tag is specified, use it literally.
title = $liSpan.html();
} else if ($liA.length) {
title = $liA.html();
href = $liA.attr("href");
target = $liA.attr("target");
} else {
// If only a tag is specified, use the trimmed string up to
// the next child tag.
title = $li.html();
var iPos = title.search(/= 0) {
title = $.trim(title.substring(0, iPos));
} else {
title = $.trim(title);
}
// self.logDebug("%o", title);
}
// Parse node options from ID, title and class attributes
var data = {
title: title,
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");
}
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: " + e + "\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(sourceNode, targetNode, helper, hitMode, accept) {
// hitMode: 'after', 'before', 'over', 'out', 'start', 'stop'
var $source = sourceNode ? $(sourceNode.span) : null;
var $target = $(targetNode.span);
if (!this.$dndMarker) {
this.$dndMarker = $("")
.hide()
.prependTo($(this.divTree).parent());
// .prependTo("body");
// logMsg("Creating marker: %o", this.$dndMarker);
}
/*
if(hitMode === "start"){
}
if(hitMode === "stop"){
// sourceNode.removeClass("dynatree-drop-target");
}
*/
// this.$dndMarker.attr("class", hitMode);
if (hitMode === "after" || hitMode === "before" || hitMode === "over") {
// $source && $source.addClass("dynatree-drag-source");
var pos = $target.position();
switch (hitMode) {
case "before":
this.$dndMarker.removeClass("dynatree-drop-after dynatree-drop-over");
this.$dndMarker.addClass("dynatree-drop-before");
pos.top -= 8;
break;
case "after":
this.$dndMarker.removeClass("dynatree-drop-before dynatree-drop-over");
this.$dndMarker.addClass("dynatree-drop-after");
pos.top += 8;
break;
default:
this.$dndMarker.removeClass("dynatree-drop-after dynatree-drop-before");
this.$dndMarker.addClass("dynatree-drop-over");
$target.addClass("dynatree-drop-target");
pos.left += 8;
}
this.$dndMarker.css({"left": (pos.left) + "px", "top": (pos.top) + "px" })
.show();
// 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(eventName, node, otherNode, event, ui, draggable) {
/**
* 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;
var dnd = this.options.dnd;
var res = null;
var nodeTag = $(node.span);
var hitMode;
switch (eventName) {
case "helper":
// Only event and node argument is available
var helper = $("
")
.append($(event.target).closest('a').clone());
// Attach node reference to helper object
helper.data("dtSourceNode", node);
// 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(node, otherNode) : null;
res = {
over: (res !== false) && ((res === true) || (res === "over") || $.inArray("over", res) >= 0),
before: (res !== false) && ((res === true) || (res === "before") || $.inArray("before", res) >= 0),
after: (res !== false) && ((res === true) || (res === "after") || $.inArray("after", res) >= 0)
};
ui.helper.data("enterResponse", res);
// this.logDebug("helper.enterResponse: %o", res);
break;
case "over":
var enterResponse = ui.helper.data("enterResponse");
hitMode = null;
if (enterResponse === false) {
// Don't call onDragOver if onEnter returned false.
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 = { x: event.pageX - nodeOfs.left,
y: event.pageY - nodeOfs.top };
var relPos2 = { x: relPos.x / nodeTag.width(),
y: relPos.y / 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.y > 0.75) {
hitMode = "after";
} else if (!enterResponse.over && enterResponse.after && relPos2.y > 0.5) {
hitMode = "after";
} else if (enterResponse.before && relPos2.y <= 0.25) {
hitMode = "before";
} else if (!enterResponse.over && enterResponse.before && relPos2.y <= 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(node, otherNode, hitMode);
}
this._setDndStatus(otherNode, node, ui.helper, hitMode, res !== false);
break;
case "drop":
hitMode = ui.helper.data("hitMode");
if (hitMode && dnd.onDrop) {
dnd.onDrop(node, otherNode, hitMode, ui, draggable);
}
break;
case "leave":
// Cancel pending expand request
node.scheduleAction("cancel");
ui.helper.data("enterResponse", null);
ui.helper.data("hitMode", null);
this._setDndStatus(otherNode, node, ui.helper, "out", undefined);
if (dnd.onDragLeave) {
dnd.onDragLeave(node, otherNode);
}
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
lastentry: undefined
};
/*************************************************************************
* 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) {
// 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.", DynaTree.version, this.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 = getDtNodeFromElement(event.target);
if (!dtnode) {
return true; // Allow bubbling of other events
}
var tree = dtnode.tree;
var o = tree.options;
tree.logDebug("event(%s): dtnode: %s", event.type, dtnode);
var prevPhase = tree.phase;
tree.phase = "userEvent";
try {
switch (event.type) {
case "click":
return ( o.onClick && o.onClick.call(tree, dtnode, event) === false ) ? false : dtnode._onClick(event);
case "dblclick":
return ( o.onDblClick && o.onDblClick.call(tree, dtnode, event) === false ) ? false : dtnode._onDblClick(event);
case "keydown":
return ( o.onKeydown && o.onKeydown.call(tree, dtnode, event) === false ) ? false : dtnode._onKeydown(event);
case "keypress":
return ( o.onKeypress && o.onKeypress.call(tree, dtnode, event) === false ) ? false : dtnode._onKeypress(event);
}
} catch(e) {
var _ = null; // issue 117
tree.logWarning("bind(%o): dtnode: %o, error: %o", event, dtnode, e);
} finally {
tree.phase = prevPhase;
}
});
// focus/blur don't bubble, i.e. are not delegated to parent