Detect target treenode when dragging items to the TreePanel instance - javascript

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
}
});

Related

How to keep track of DIV's child element count in runtime?

I have a div, which will contain dropdowns and these dropdowns are created dynamically by the user on the click oo a button which is kept outside this div.
So what I need to achieve here is I wanna display 'No filter applied' when there are no dropdowns and remove that 'No filter applied' while there are dropdowns present.
I tried this scenario through addEventListener but I am not sure what action needs to implement for this scenario?
document.addEventListener("DOMContentLoaded", function(event) {
var activities = document.getElementById("dvContainer");
activities.addEventListener("change", function() {
if (activities.childElementCount > 0) {
activities.classList.add("displayZero");
} else {
activities.classList.remove("displayZero");
}
//console.log('ajay');
});
});
function AddDropDownList() {}
<input type="button" id="btnAdd" onclick="AddDropDownList()" value="Add Filter" />
<div id="dvContainer"><span>No Filters applied.</span></div>
This is my try, thanks in advance.
According to what you mentioned, you have a button that with click it, you add dropdowns dynamically.
so you don't need any extra event!!
in your button's click function:
yourButton.onclick=function(){
//..... do somethings similar adding dropdowns
activities.classList.add("displayZero");
};
And where you remove dropdown:
activities.classList.remove("displayZero");
Currently, I can think of only 2 ways to resolve:
The Easiest solution is first to create update function then call it from init of dom and then in the AddDropDownList. E.g.
function update() {
if (activities.childElementCount > 0) {
activities.classList.add("displayZero");
} else {
activities.classList.remove("displayZero");
}
}
window.onload = function() {
update();
}
function AddDropDownList() {
//Put Your code as you have written and then add
update();
}
Use Mutation Observer
window.onload = function() {
// Select the node that will be observed for mutations
var targetNode = document.getElementById('dvContainer');
// Options for the observer (which mutations to observe)
var config = {
attributes: true,
childList: true,
subtree: true
};
// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
}
function AddDropDownList() {
}
// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
for (var mutation of mutationsList) {
if (mutation.type == 'childList') {
console.log('A child node has been added or removed.');
if (activities.childElementCount > 0) {
activities.classList.add("displayZero");
} else {
activities.classList.remove("displayZero");
}
} else if (mutation.type == 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
};

How to achieve a queue of object instances of jQuery modals, assuring only one instance is on per time?

I've a task of building a modal prompt, that's been simple so far describing its methods like "show", "hide" when it comes down just to DOM manupulation.
Now comes the hardship for me... Imagine we have a page on which there are several immediate calls to construct and show several modals on one page
//on page load:
$("browser-deprecated-modal").modal();
$("change-your-city-modal").modal();
$("promotion-modal").modal();
By default my Modal (and other libraries i tried) construct all of these modals at once and show them overlapping each other in reverse order -
i.e $(promotion-modal) is on the top, while the
$("browser-deprecated-modal") will be below all of them. that's not what i want, let alone overlapping overlays.
I need each modal to show up only when the previous one (if there'are any) has been closed. So, first we should see "browser-deprecated-modal" (no other modals underneath), upon closing it there must pop up the second one and so on.
I've been trying to work it out with this:
$.fn.modal = function(options) {
return this.each(function() {
if (Modal.running) {
Modal.toInstantiateLater.push({this,options});
} else {
var md = new Modal(this, options);
}
});
}
destroy :function () {
....
if (Modal.toInstantiateLater.length)
new Modal (Modal.toInstantiateLater[0][0],Modal.toInstantiateLater[0][1]);
}
keeping a track of all calls to construct a Modal in a array and in the "destroy" method make a check of this array is not empty.
but it seems awkward and buggy me thinks.
i need a robust and clear solution. I've been thinking about $.Callbacks or $.Deferred,
kinda set up a Callback queue
if (Modal.running) { //if one Modal is already running
var cb = $.Callbacks();
cb.add(function(){
new Modal(this, options);
});
} else { //the road is clear
var md = new Modal(this, options);
}
and to trigger firing cb in the destroy method, but i'm new to this stuff and stuck and cannot progress, whether it's right or not, or other approach is more suitable.
Besides, I read that callbacks fire all the functions at once (if we had more than one extra modal in a queue), which is not right, because I need to fire Modal creation one by one and clear the Callback queue one by one.
Please help me in this mess.
My code jsfiddle
I got rid of the counter variable, as you can use toInstantiateLater to keep track of where you are, and only had to make a few changes. Give this a try...
Javscript
function Modal(el, opts){
this.el = $(el);
this.opts = opts;
this.overlay = $("<div class='overlay' id='overlay"+Modal.counter+"'></div>");
this.wrap = $("<div class='wrap' id='wrap"+Modal.counter+"'></div>");
this.replace = $("<div class='replace' id='replace"+Modal.counter+"'></div>");
this.close = $("<span class='close' id='close"+Modal.counter+"'></span>")
if (Modal.running) {
Modal.toInstantiateLater.push(this);
}
else {
Modal.running = true;
this.show();
}
}
Modal.destroyAll = function() {
Modal.prototype.destroyAll();
};
Modal.prototype = {
show: function() {
var s = this;
s.wrap.append(s.close);
s.el.before(s.replace).appendTo(s.wrap).show();
$('body').append(s.overlay).append(s.wrap);
s.bindEvents();
Modal.currentModal = s;
},
bindEvents: function() {
var s = this;
s.close.on("click.modal",function(e){
s.destroy.call(s,e);
});
},
destroy: function(e) {
var s = this;
s.replace.replaceWith(s.el.hide());
s.wrap.remove();
s.overlay.remove();
if (Modal.toInstantiateLater.length > 0) {
Modal.toInstantiateLater.shift().show();
}
else {
Modal.running = false;
}
},
destroyAll: function(e) {
Modal.toInstantiateLater = [];
Modal.currentModal.destroy();
}
}
Modal.running = false;
Modal.toInstantiateLater = [];
Modal.currentModal = {};
$.fn.modal = function(options) {
return this.each(function() {
var md = new Modal(this, options);
});
}
$("document").ready(function(){
$("#browser-deprecated-modal").modal();
$("#change-your-city-modal").modal();
$("#promotion-modal").modal();
$("#destroy-all").on("click", function() {
Modal.destroyAll();
});
});
jsfiddle example
http://jsfiddle.net/zz9ccbLn/4/

jquery fancytree load and check all child nodes on check

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);
});
});
}
});
}
}

Multiple instances of a JavaScript carousel

So I have the following code I have written to build a carousel in JavaScript using Hammer.js and jQuery:
var hCarousel = {
container: false,
panes: false,
pane_width: 0,
pane_count: 0,
current_pane: 0,
build: function( element ) {
hCarousel.container = $(element).find('.hcarousel-inner-container');
hCarousel.panes = $(hCarousel.container).find('> .section');
hCarousel.pane_width = 0;
hCarousel.pane_count = hCarousel.panes.length;
hCarousel.current_pane = 0;
hCarousel.setPaneDimensions( element );
$(window).on('load resize orientationchange', function() {
hCarousel.setPaneDimensions( element );
});
$(element).hammer({ drag_lock_to_axis: true })
.on('release dragleft dragright swipeleft swiperight', hCarousel.handleHammer);
},
setPaneDimensions: function( element ){
hCarousel.pane_width = $(element).width();
hCarousel.panes.each(function() {
$(this).width(hCarousel.pane_width);
});
hCarousel.container.width(hCarousel.pane_width*hCarousel.pane_count);
},
next: function() {
return hCarousel.showPane(hCarousel.current_pane+1, true);
},
prev: function() {
return hCarousel.showPane(hCarousel.current_pane-1, true);
},
showPane: function( index ) {
// between the bounds
index = Math.max(0, Math.min(index, hCarousel.pane_count-1));
hCarousel.current_pane = index;
var offset = -((100/hCarousel.pane_count)*hCarousel.current_pane);
hCarousel.setContainerOffset(offset, true);
},
setContainerOffset: function( percent, animate ) {
hCarousel.container.removeClass("animate");
if(animate) {
hCarousel.container.addClass("animate");
}
if(Modernizr.csstransforms3d) {
hCarousel.container.css("transform", "translate3d("+ percent +"%,0,0) scale3d(1,1,1)");
}
else if(Modernizr.csstransforms) {
hCarousel.container.css("transform", "translate("+ percent +"%,0)");
}
else {
var px = ((hCarousel.pane_width*hCarousel.pane_count) / 100) * percent;
hCarousel.container.css("left", px+"px");
}
},
handleHammer: function( ev ) {
ev.gesture.preventDefault();
switch(ev.type) {
case 'dragright':
case 'dragleft':
// stick to the finger
var pane_offset = -(100/hCarousel.pane_count)*hCarousel.current_pane;
var drag_offset = ((100/hCarousel.pane_width)*ev.gesture.deltaX) / hCarousel.pane_count;
// slow down at the first and last pane
if((hCarousel.current_pane == 0 && ev.gesture.direction == Hammer.DIRECTION_RIGHT) ||
(hCarousel.current_pane == hCarousel.pane_count-1 && ev.gesture.direction == Hammer.DIRECTION_LEFT)) {
drag_offset *= .4;
}
hCarousel.setContainerOffset(drag_offset + pane_offset);
break;
case 'swipeleft':
hCarousel.next();
ev.gesture.stopDetect();
break;
case 'swiperight':
hCarousel.prev();
ev.gesture.stopDetect();
break;
case 'release':
// more then 50% moved, navigate
if(Math.abs(ev.gesture.deltaX) > hCarousel.pane_width/2) {
if(ev.gesture.direction == 'right') {
hCarousel.prev();
} else {
hCarousel.next();
}
}
else {
hCarousel.showPane(hCarousel.current_pane, true);
}
break;
}
}
}
And I call this like:
var hSections;
$(document).ready(function(){
hSections = hCarousel.build('.hcarousel-container');
});
Which works fine. But I want to make it so that I can have multiple carousels on the page which again works... but the overall width of the container is incorrect because it's combining the width of both carousels.
How can I run multiple instances of something like this, but the code know WHICH instance it's interacting with so things don't become mixed up, etc.
The problem is your design is not really suited to multiple instances, because of the object literal which has properties of the carousel, but also the build method.
If I was starting this from scratch, I would prefer a more OOP design, with a carousel class that can you instantiate, or have it as a jQuery plugin. That said, it's not impossible to adapt your existing code.
function hCarousel(selector){
function hCarouselInstance(element){
var hCarousel = {
// insert whole hCarousel object code
container: false,
panes: false,
build : function( element ){
...
};
this.hCarousel = hCarousel;
hCarousel.build(element);
}
var instances = [];
$(selector).each(function(){
instances.push(new hCarouselInstance(this));
});
return instances;
}
Usage
For example, all elements with the hcarousel-container class will become an independant carousel.
$(document).ready(function(){
var instances = hCarousel('.hcarousel-container');
});
Explanation:
The hCarousel function is called passing the selector, which can match multiple elements. It could also be called multiple times if needed.
The inner hCarouselInstance is to be used like a class, and instantiated using the new keyword. When hCarousel is called, it iterates over the matched elements and creates a new instance of hCarouselInstance.
Now, hCarouselInstance is a self contained function that houses your original hCarousel object, and after creating the object it calls hCarousel.build().
The instances return value is an array containing each instance object. You can access the hCarousel properties and methods from there, such as:
instances[0].hCarousel.panes;
jQuery plugin
Below is a conversion to a jQuery plugin, which will work for multiple carousels.
(function ( $ ) {
$.fn.hCarousel = function( ) {
return this.each(function( ) {
var hCarousel = {
// insert whole hCarousel object code here - same as in the question
};
hCarousel.build(this);
});
};
}( jQuery ));
Plugin usage:
$('.hcarousel-container').hCarousel();
I would try turning it into a function which you can use like a class. Then you can create separate objects for your carousels.
So you would have something like the following:
function HCarousel (element) {
this.element=element;
this.container= false;
this.panes= false;
this.pane_width= 0;
this.pane_count= 0;
this.current_pane= 0;
}
And then add each method on the class like this.
HCarousel.prototype.build = function() {
this.container = $(element).find('.hcarousel-inner-container');
this.panes = $(hCarousel.container).find('> .section');
this.pane_width = 0;
this.pane_count = hCarousel.panes.length;
this.current_pane = 0;
this.setPaneDimensions( element );
$(window).on('load resize orientationchange', function() {
this.setPaneDimensions( element );
});
$(this.element).hammer({ drag_lock_to_axis: true }).on('release dragleft dragright swipeleft swiperight', hCarousel.handleHammer);
};
etc. That should give you the basic idea. Will take a little bit of re-writing, but then you can create a carousel with something like this:
var carousel1 = new HCarousel('.hcarousel-container');
Hope that puts you on the right track.
Classes don't actually exist in JS, but this is a way to simulate one using a function. Here's a good article on using classes in JS http://www.phpied.com/3-ways-to-define-a-javascript-class/

How to drop a node on an empty dynatree

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;
});

Categories