I have two dynatrees on the same page. Tree 1 has various number of nodes, tree 2 is empty. I would like to drag a node from tree 1 and drop it on tree 2. Everything works fine while tree 2 is not empty. If tree 2 is empty, however, it is not working at all. Here is my code:
$('#tree1').dynatree({
dnd: {
onDragStart: function (node) {
return true;
}
}
});
$('#tree2').dynatree({
dnd: {
onDragEnter: function (node, sourceNode) {
return true;
},
onDrop: function (node, sourceNode, hitMode, ui, draggable) {
var copyNode = sourceNode.toDict(true, function (dict) {
delete dict.key;
});
node.addChild(copyNode);
}
}
});
Can anyone tell me how to solve this problem? Thanx in advance.
After reading the dynatree source code (version 1.2.0), I found that it is not possible to add a node to an empty tree without the modification of the source code. This is because when you try to drop a source node to a target tree, it is actually drop a source node to/before/after a target node instead of target tree. Hence, dynatree will not work if there is no node in a tree.
I did something like this:
var tree1Dragging = false,
tree2Over = false;
$('#tree1').dynatree({
dnd : {
onDragStart : function(node) {
tree1Dragging = true;
return true;
},
onDragStop : function(node) {
critDrag = false;
if (tree2Over) {
//TODO do your drop to tree2 process here (get the root and append the "node" to it for example)
}
}
}
});
$('#tree2').dynatree({
dnd : {
onDragEnter : function(node, sourceNode) {
return true;
},
onDrop : function(node, sourceNode, hitMode, ui, draggable) {
var copyNode = sourceNode.toDict(true, function(dict) {
delete dict.key;
});
node.addChild(copyNode);
}
}
});
// Add mouse events to know when we are above the tree2 div
$("#tree2").mouseenter(function() {
if (tree1Dragging) {
tree2Over = true;
}
}).mouseleave(function(){
tree2Over = false;
});
Related
We are working on Office UI Fabric JS 1.2.0. Issue is with context menu having submenu items. When you open Second level, and then click anywhere on menu, first level menu was closing and second menu was re-aligning to Top-Left of page. We couldn't find the solution in next version of fabric also.
The issue gets resolved after overriding two methods from fabric.js of ContextualHost as follows:
fabric.ContextualHost.prototype.disposeModal = function () {
if (fabric.ContextualHost.hosts.length > 0) {
window.removeEventListener("resize", this._resizeAction, false);
document.removeEventListener("click", this._dismissAction, true);
document.removeEventListener("keyup", this._handleKeyUpDismiss, true);
this._container.parentNode.removeChild(this._container);
if (this._disposalCallback) {
this._disposalCallback();
}
// Dispose of all ContextualHosts
var index = fabric.ContextualHost.hosts.indexOf(this);
fabric.ContextualHost.hosts.splice(index, 1);
//Following is original code removed
//var i = ContextualHost.hosts.length;
//while (i--) {
// ContextualHost.hosts[i].disposeModal();
// ContextualHost.hosts.splice(i, 1);
//}
}
};
fabric.ContextualHost.prototype._dismissAction = function (e) {
var i = fabric.ContextualHost.hosts.length;
while (i--) { //New added
var currentHost = fabric.ContextualHost.hosts[i];
if (!currentHost._container.contains(e.target) && e.target !== this._container) {
if (currentHost._children !== undefined) {
var isChild_1 = false;
currentHost._children.map(function (child) {
if (child !== undefined) {
isChild_1 = child.contains(e.target);
}
});
if (!isChild_1) {
currentHost.disposeModal();
}
}
else {
currentHost.disposeModal();
}
}
}
};
Hope this question + answer helps someone looking to override fabric.js original code.
I'm using the fancytree plugin to create a treeview. All of my data is loaded on the expand via ajax and json. I also have code in to load child nodes if a user checks a parent node as they aren't technically loaded if that node isn't expanded first.
My problem is, if you have a parent child relationship 3 levels deep, and expand to level 2, the third level doesn't get checked.
In essence I have something like this
Parent1
-->Child1
---->Child1-1
---->Child1-2
---->Child1-3
-->Child2
Now, if you never expand parent and check it, the nodes all get loaded and checked. However, if you expand parent 1 to show child 1 and 2, then check parent1 the child nodes 1-1 through 3 never get loaded or checked. Here is the code I have to load the child nodes on check, what am I missing.
select: function (event, data) { //here's where I need to load the children for that node, if it has them, so they can be set to checked
var node = data.node;
//alert(node.key);
if (node.isSelected()) {
if (node.isUndefined()) {
// Load and select all child nodes
node.load().done(function () {
node.visit(function (childNode) {
childNode.setSelected(true);
});
});
} else {
// Select all child nodes
node.visit(function (childNode) {
childNode.setSelected(true);
});
}
// Get a list of all selected nodes, and convert to a key array:
var selKeys = $.map(data.tree.getSelectedNodes(), function (node) {
treeHash[node.data.treeItemType + node.key] = node.key;
});
}
Here is my full JS just for reference
if ($("entityTree") != null) {
$(function () {
// Create the tree inside the <div id="tree"> element.
$("#entityTree").fancytree({
source: { url: "/Home/GetTreeViewData", cache: true }
, checkbox: true
, icons: false
, cache: true
, lazyLoad: function (event, data) {
var node = data.node;
data.result = {
url: "/Home/GetTreeViewData/" + node.key.replace(node.data.idPrefix, "")
, data: { mode: "children", parent: node.key }
, cache: true
};
}
, renderNode: function (event, data) {
// Optionally tweak data.node.span
var node = data.node;
var $span = $(node.span);
if (node.key != "_statusNode") {
$span.find("> span.fancytree-expander").css({
borderLeft: node.data.customLeftBorder
//borderLeft: "1px solid orange"
});
}
}
, selectMode: 3
, select: function (event, data) { //here's where I need to load the children for that node, if it has them, so they can be set to checked
var node = data.node;
//alert(node.key);
if (node.isSelected()) {
if (node.isUndefined()) {
// Load and select all child nodes
node.load().done(function () {
node.visit(function (childNode) {
childNode.setSelected(true);
});
});
} else {
// Select all child nodes
node.visit(function (childNode) {
childNode.setSelected(true);
});
}
// Get a list of all selected nodes, and convert to a key array:
var selKeys = $.map(data.tree.getSelectedNodes(), function (node) {
treeHash[node.data.treeItemType + node.key] = node.key;
});
}
else {
delete treeHash[node.data.treeItemType + node.key];
//alert("remove " + node.key);
}
for (var i in treeHash) {
alert(treeHash[i]);
}
}
, strings: {
loading: "Grabbing places and events…",
loadError: "Load error!"
},
})
});
}
Try this and let me know if works. We need to load the undefined child of child nodes which are loaded already.
if (node.isSelected()) {
if (node.isUndefined()) {
// Load and select all child nodes
node.load().done(function () {
node.visit(function (childNode) {
childNode.setSelected(true);
});
});
} else {
// Select all child nodes
node.visit(function (childNode) {
childNode.setSelected(true);
if (childNode.isUndefined()) {
// Load and select all child nodes
childNode.load().done(function () {
childNode.visit(function (itschildNode) {
itschildNode.setSelected(true);
});
});
}
});
}
}
I'm using WYSIHTML5 Bootstrap ( http://jhollingworth.github.com/bootstrap-wysihtml5 ), based on WYSIHTML5 ( https://github.com/xing/wysihtml5 ) which is absolutely fantastic at cleaning up HTML when copy pasting from websites.
I'd like to be able to handle code into the editor, and then highlight the syntax with HighlightJS.
I've created a new button and replicated the method used in wysihtml5.js to toggle bold <b> on and off, using <pre> instead:
(function(wysihtml5) {
var undef;
wysihtml5.commands.pre = {
exec: function(composer, command) {
return wysihtml5.commands.formatInline.exec(composer, command, "pre");
},
state: function(composer, command, color) {
return wysihtml5.commands.formatInline.state(composer, command, "pre");
},
value: function() {
return undef;
}
};
})(wysihtml5)
But that's not enough. The editor hides the tags when editing. I need to be able to wrap my content in both <pre>and <code>ie. <pre><code></code></pre>.
This means writing a different function than the one used by wysihtml5, and I don't know how... Could anyone help me with that?
Here's the code for the formatInline function in wysihtml5:
wysihtml5.commands.formatInline = {
exec: function(composer, command, tagName, className, classRegExp) {
var range = composer.selection.getRange();
if (!range) {
return false;
}
_getApplier(tagName, className, classRegExp).toggleRange(range);
composer.selection.setSelection(range);
},
state: function(composer, command, tagName, className, classRegExp) {
var doc = composer.doc,
aliasTagName = ALIAS_MAPPING[tagName] || tagName,
range;
// Check whether the document contains a node with the desired tagName
if (!wysihtml5.dom.hasElementWithTagName(doc, tagName) &&
!wysihtml5.dom.hasElementWithTagName(doc, aliasTagName)) {
return false;
}
// Check whether the document contains a node with the desired className
if (className && !wysihtml5.dom.hasElementWithClassName(doc, className)) {
return false;
}
range = composer.selection.getRange();
if (!range) {
return false;
}
return _getApplier(tagName, className, classRegExp).isAppliedToRange(range);
},
value: function() {
return undef;
}
};
})(wysihtml5);
Got the answer from Christopher, the developer of wysihtml5:
wysihtml5.commands.formatCode = function() {
exec: function(composer) {
var pre = this.state(composer);
if (pre) {
// caret is already within a <pre><code>...</code></pre>
composer.selection.executeAndRestore(function() {
var code = pre.querySelector("code");
wysihtml5.dom.replaceWithChildNodes(pre);
if (code) {
wysihtml5.dom.replaceWithChildNodes(pre);
}
});
} else {
// Wrap in <pre><code>...</code></pre>
var range = composer.selection.getRange(),
selectedNodes = range.extractContents(),
pre = composer.doc.createElement("pre"),
code = composer.doc.createElement("code");
pre.appendChild(code);
code.appendChild(selectedNodes);
range.insertNode(pre);
composer.selection.selectNode(pre);
}
},
state: function(composer) {
var selectedNode = composer.selection.getSelectedNode();
return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "CODE" }) && wysihtml5.dom.getParentElement(selectedNode, { nodeName: "PRE" });
}
};
... and add this to your toolbar:
<a data-wysihtml5-command="formatCode">highlight code</a>
Many thanks Christopher!!
I fork today bootstrap-wysihtml5 project and add code highlighting support using Highlight.js.
You can check demo at http://evereq.github.com/bootstrap-wysihtml5 and review source code https://github.com/evereq/bootstrap-wysihtml5. It's basically almost same code as from Christopher, together with UI changes and embeded inside bootstrap version of editor itself.
We are experiencing an issue when using the lazyloading feature and open_all feature together.
The contents of the tree are loaded using lazy loading feature.
When we select a node and click on expand all button, all the child nodes of that node will be fetched using jstree ajax call and opened using the open_all function, when clicking collapse all button, we are using close_all function. This works perfectly for the first time.
But on second time, when we click on expand all on the same node, same ajax url is hit on recursively. ( We think, the url is hit on every time on opening a node using open_all). The intended behaviour is not to call the url(as data is already loaded), only the open_all function should be executed.
Could you please clarify how to fix the issue
//code to load the tree
$("#TreePanel").jstree({
"xml_data" : {
"ajax" : {
"url" : "/ajax/loadTree",
"type" : "post",
"data" : function(node) {
var data = {};
data.dunsNumber = ${dunsNumber};
if (node == -1) {
//set duns number to data
} else {
data.selectedNodeId = node.attr("id");
data.expandAll = isExpandAll;
}
return data;
},
"success" : function(data) {
if ($(data).attr('id') == 'error') {
$("#overlayContent").empty();
} else {
return data;
}
}
},
"xsl" : "nest"
},
"plugins" : [ "themes", "xml_data" ]
});
//code to expand all nodes
$("#ufvExpandAll").bind("click", function() {
isExpandAll= true;
$("#TreePanel").jstree("open_all", selectedNode);
isExpandAll= false;
});
//code to collapse all nodes
$("#ufvCollapseAll").bind("click", function() {
$("#TreePanel").jstree("close_all", selectedNode);
});
//code to get the node and set on a variable on clicking a node
var selectedNode;
$("#TreePanel").delegate("a", "click", function(e, data) {
var node = $(e.target).closest("li");
if (selectedNode != undefined && selectedNode != null) {
$("#" + selectedNode.id + " > a").removeClass("jstree-default- selected-node");
}
selectedNode = node[0];
$("#" + selectedNode.id + " > a").addClass("jstree-default-selected- node");
$("#ufvExpandAll").attr("disabled", false);
$("#ufvCollapseAll").attr("disabled", false);
return false;
});
Thanks In Advance
Regards
Hari
Try to inspect the node to see if it has any pre-existing children before executing logic to load it with children.
.bind("open_node.jstree", function (event, data) {
var node = $(data.rslt.obj);
var children = $.jstree._reference(node)._get_children(node);
if (children.length==0){
//node is empty, so do node open logic here
}
})
I have GridPanel and TreePanel instances. Elements from GridPanel could be dragged into the treepanel. But I can't detect which tree node receives these dragged items.
I initialize tree panel DD with the following code (method of class derived from Ext.tree.TreePanel):
initDD: function() {
var treePanelDropTargetEl = this.getEl();
var treePanelDropTarget = new Ext.dd.DropTarget(treePanelDropTargetEl, {
ddGroup: 'ddgroup-1',
notifyDrop: function(ddSource, e, data) {
// do something with data, here I need to know target tree node
return true;
}
});
}
So how can I find out which tree node received dragged items in the "notifyDrop" handler. I can take e.getTarget() and calculate the node but I don't like that method.
If you use a TreeDropZone (instead of DropTarget) you'll have more tree-specific options and events like onNodeDrop. Note that there are many ways to do DnD with Ext JS.
Here is some kind of solution
MyTreePanel = Ext.extend(Ext.tree.TreePanel, {
listeners: {
nodedragover: function(e) {
// remember node
this.targetDropNode = e.target;
}
}
initComponent: function() {
// other initialization steps
this.targetDropNode = false;
var config = {
// ...
dropConfig: {
ddGroup: 'mygroupdd',
notifyDrop: function(ddSource, e, data) {
// process here using treepanel.targetDropNode
}
}
}
// ...
};
// other initialization steps
}
});