PDA

View Full Version : Ext.ux.MultiSelectTreePanel



Deadmeat
21 Feb 2009, 10:49 AM
This is a tree panel that allows multiple selection and drag and drop of said selection.

Main features:

Ctrl-click multi select
Shift-click range select
Drag and Drop multiple nodes
Shows tree/subnodes in dragged version
Keeps config options and events of default trees
Tested in FF3, Safari3.2, Opera 9.5+, IE 6,7,8rc1


http://www.users.on.net/~clear/ext/

It was posted in examples, but since I cleaned it up, I thought I'd post it here.

Original Thread (http://extjs.com/forum/showthread.php?t=28115)

Deadmeat
3 Mar 2009, 6:10 AM
I've updated the examples page with more information and more examples.

Including a Column Tree Example (http://www.users.on.net/~clear/ext/#Example4) which works around issues with the original example (http://extjs.com/deploy/dev/examples/tree/column-tree.html).

And some notes about the planned features(suggestions welcome) for the future. Although ExtJS-3.0 is around the corner so I'll need a need to update when it ships.

Deadmeat
7 Mar 2009, 5:38 AM
Okay so when I updated the page I didn't test it in IE. So I didn't know I'd hit the name = id bug so it just spewed errors everywhere.

So I've fixed that and update the script to fix a bunch of issues.

It's v1.1 now, but works much the same, except that you need to enableDD, enabledDrag or enableDrop in the config to get Drag and Drop working again.

See http://www.users.on.net/~clear/ext/ for more information.

extjs_new
13 Mar 2009, 11:17 PM
Hi, can I ask something? I wanted to use this in Managing Hierarchical Data in MySQL.
Do you have sample for this?

Thanks.

Deadmeat
15 Mar 2009, 12:11 AM
Sorry, I don't have one prepared, I developed to it work with files and folders mostly.
The general gist would just be a nodedrop handler like this:



// not tested...
tree.on("nodedrop", function(de) {
var ns = de.dropNode;
if(!Ext.isArray(ns)) {
ns = [ns];
}
var ids = [];
for (var c=0,len=ns.length;c<len;c++) {
ids.push(ns[c].id);
}
var targetid = de.target.id;
Ext.Ajax.request({
url: 'treedrop.php',
params: { target: targetid, 'nodes[]': ids }
});
});


I'm not sure what Ext does when using arrays as request params, so you'll need to play with that part to pass the ids. If your heirarchy is based on path, you'll need to move the code into a "beforeinsert" handler to and collect the paths of the tree nodes before they are moved.

You might also look at Sakis Remote Tree (http://extjs.net/forum/showthread.php?t=55102) for a comprehensive example of the sorts of thing you need to handle. The only real difference with this tree is that there is more than one node being dropped at a time, so de.dropNode needs to be treated as a array.

Hope this helps. I had planned on seeing what would be needed to layer the RemoteTree extension over my tree, but I haven't gotten around to it yet. If you get stuck, post some code and I'll try to help out.

cwford
19 Mar 2009, 2:31 PM
Deadmeat,

First, thank you so much for your work on this multi-selection extender for trees. In running V1.1 I came across this minor bug. In comparing node order the IE test "document.documentElement.sourceIndex" will test negative when sourceIndex is 0, resulting in an error under IE because the following createRange statement is executed. I simply tested that sourceIndex was defined, and that seemed to take care of the problem. As I said, minor compared to the hard work you put into this extension,

Clyde

SEE CODE BELOW:


// private
// for comparing node order... (taken from quirksmode.org and googlecode)
compareNodeOrder: document.compareDocumentPosition ?
function(node1, node2) {
// W3C DOM lvl 3 method (Gecko)
return 3 - (node1.ui.elNode.compareDocumentPosition(node2.ui.elNode) & 6);
} :
// (document.documentElement.sourceIndex ? Replaced with following line, so sourceIndex = 0 doesn't cause statement to be evaluated as false
(typeof document.documentElement.sourceIndex !== "undefined" ?
function(node1, node2) {
// IE source index method
return node1.ui.elNode.sourceIndex - node2.ui.elNode.sourceIndex;
} :
function(node1, node2) {
if (node1 == node2) return 0;
// Safari doesn't support compareDocumentPosition or sourceIndex
// from http://code.google.com/p/doctype/wiki/ArticleNodeCompareDocumentOrder
var range1 = document.createRange();
range1.selectNode(a.ui.elNode);
range1.collapse(true);

var range2 = document.createRange();
range2.selectNode(b.ui.elNode);
range2.collapse(true);

return range1.compareBoundaryPoints(Range.START_TO_END, range2);
}
)

Deadmeat
20 Mar 2009, 3:15 AM
Looks pretty good. I didn't experiment enough with the sourceIndex, but this seems to work.

I've updated the 1.1 code. Just wondering if there is a reason you used typeof instead of just checking !== undefined.

Thanks for the heads up, fix added.

cwford
20 Mar 2009, 5:17 AM
Deadmeat,

Used typeof instinctively, goes back to an old cross-browser problem I encountered some years back, can't remember all the details, do remember just checking for undefined didn't work across all browsers (think one browser returned it as a string while most didn't) so I got into te habit of using typeof and not worrying about the result because all browsers return it as a string.

Then started using it as a way to make my js code a bit more typed, and easier to read. So it's just a habit now. Probably don't need typeof in this case but it can't hurt.

Clyde

Deadmeat
20 Mar 2009, 8:34 PM
Cool. I wish things like that were simpler to understand.

Thanks again and good luck with your app.

Kartt_in
15 Apr 2009, 2:01 AM
Thanks for all giving this excellent feature, it will be much helpful if you could post a sample java code how to link this Feature in existing TreePanel.

Thanks
Karthik

Deadmeat
15 Apr 2009, 8:42 PM
Thanks for all giving this excellent feature, it will be much helpful if you could post a sample java code how to link this Feature in existing TreePanel.

Thanks
Karthik

I assume you mean with GXT or Ext GWT. I haven't ever tried GWT so I can't help you. I assume you'd have to write a GXT extensions of the relevant GXT classes, but I don't know how it works.

In terms of server side scripts, they would be exactly the same as with single drag and drop, except for it being multiple. So in JSP you'd loop over paramValues.dragitems or request.getParameterValues("dragitems") and do the same thing for each item.

I've been meaning to work up some more samples, but atm I'm working on an unrelated project and don't have time. Ext JS 3.0 is also due soonish, so I'm waiting for that too.

Deadmeat
8 May 2009, 3:17 AM
I've done a few tests and so far the extension works unchanged in the 3.0 release candidate. I suspect that the tree code saw very few changes in this release.

The fix for the order in which tree nodes are inserted seems to be in there, but the allowContainerDrop issue persists.

mjaomaha
8 Jun 2009, 7:39 AM
I have noticed a very subtle problem when I implement this feature. It appears that if you do not use drag and drop, (enableDD: true) there are a set of listeners for clicks, shift clicks, and control clicks that make this tree function. If you enable Drag and Drop, there are more methods that get registered, specifically getDragData, that is causing problems. When I hit control select, and click down, the node is selected, and then when I unclick, the node is unselected. If I simply hit select, I notice when I put console.debug statements in that the "select" method is called twice, which does not cause any problems. Shift select also executes twice, but since it does not unselect, this does not cause any problems either. The problem is that control click does not work.

mjaomaha
8 Jun 2009, 8:41 AM
Here is my code as I have implemented it below. I don't know if there is something I am missing.




var

userListTree = new Ext.ux.MultiSelectTreePanel({



autoScroll : true,
id: 'userListTree',
title: 'Users to Share With',

useArrows: true,

// enableDD: true, this has to be commented out or the double select happens
ddGroup: 'doubleListTree',




afterRender:function() {


Ext.tree.TreePanel.prototype.afterRender.apply(this, arguments);

new Ext.dd.DropTarget(this.body, {




ddGroup: 'doubleListTree',


// what to do when user drops a node here


notifyDrop: function(dd, e, dragdata) {


e.preventDefault();


e.stopEvent();

for(var i=0; i<dragdata.nodes.length; i++) {

Ext.getCmp('userListTree').getRootNode().appendChild(dragdata.nodes[i]);




}


return true;

} // eo function notifyDrop

});





}, // eo function afterRender


rootVisible : false,
singleExpand :true,
root :new Ext.tree.TreeNode({id:'userListTreeRoot'})


});

Deadmeat
9 Jun 2009, 3:12 AM
Hi mjaomaha,

I'm not sure what your example is doing. It looks like an attempt to implement allowContainerDrop (allowing drops on the container to append to the root)

The built in allowContainerDrop is broken in Ext 2.x (see here (http://extjs.com/forum/showthread.php?t=61001) for details and a hacky workaround), it's fixed in the rc2 of Ext 3.0 (see here (https://extjs.com/forum/showthread.php?p=324824))

It likely an issue with my modified Ext.ux.MultiSelectTree(Drag/Drop)Zones not playing nice with normal Drag/DropZones.

or see the next reply.

Deadmeat
9 Jun 2009, 3:56 AM
I have noticed a very subtle problem when I implement this feature. It appears that if you do not use drag and drop, (enableDD: true) there are a set of listeners for clicks, shift clicks, and control clicks that make this tree function. If you enable Drag and Drop, there are more methods that get registered, specifically getDragData, that is causing problems. When I hit control select, and click down, the node is selected, and then when I unclick, the node is unselected. If I simply hit select, I notice when I put console.debug statements in that the "select" method is called twice, which does not cause any problems. Shift select also executes twice, but since it does not unselect, this does not cause any problems either. The problem is that control click does not work.

Hi again,

I re-read this and I'm wondering if maybe I've worked out what is happening.

Have you changed the selection model back to the standard Ext.tree.MultiSelectionModel (or the SingleSelectionModel).

The default tree implementation works in two distinct stages. It adds a mousedown listener to trigger Drag&Drop and a click listener to implement selection. I changed all that and don't use click listener at all. However my custom TreeDragZone calls selectionModel.select() as part of the mousedown event.

If you are using a default selection model (or variant thereof) it will have registered it's own click listener which will fire select the second time.

Most of my code is in heavily customised SelectionModel and Tree(Drag/Drop)Zones. If you need to add your own functionality in these objects, you'll need to extend or instantiate one of the following:



Ext.ux.MultSelectDragZone
Ext.ux.MultiSelectDropZone
Ext.ux.FixedMultiSelectionMode


I'm not aware of what you trying to do with your tree, but it sounds like you are adding considerable custom functionality of your own. The post of about allowContainerDrop is still relevant because that is what your example was effectively achieving.

Sorry for the confusion, I hope this helps explain what's happening.

mjaomaha
11 Jun 2009, 10:47 AM
Thanks for your reply. I think you may be on to something there. It seems as if two selection models are working at the same time. I am actually using cometd (which is a Java push technology) to build interactive components, and so everything that changes on one clients machine sends an ajax request, and all users that have that page open will get the update on their machines as well. It's a really fun project!

I'm trying to understand what you are saying above. Are you saying that if another part of my page uses a regular tree, that it may interfere with your tree because of two different selection models?

I can send you more of my code in an email, but what I am building looks like Eclipse, with an explore bar on the left. The left Explore bar is a tree that does not use drag and drop, but I do use Ext.ux.MultiSelectTreePanel because I like the Shift Select feature. When you double click on a node on this tree, it opens a panel in the center section, and in this panel there are two Ext.ux.MultiSelectTreePanel's side by side, both of them with enableDD set to true. These two trees seem to responding to both sets of events, just like you said below. Where do I check to see if this is turned on?

Ill include the full source code of the Ext panel that all this fits in on another post. You will see the cometd stuff too. I temporarily disable some of the control click functionality as a way to make it work, but I would rather have it working as designed.

Thanks alot.

Matt

mjaomaha
11 Jun 2009, 10:52 AM
actioncenter.projectAssigner = Ext.extend(Ext.Panel, {
title: 'Project Assigner',
projectID:'',
projectName:'',
layout: 'absolute',


initComponent : function(){
var projectID = this.projectID;
var addUsersToProject = function(users) {
var channel = "/noresponse";
dojox.cometd.startBatch();
for (var i = 0;i<users.length;i++){
dojox.cometd.publish("/requests/service", {
action: "grantUserAccessToProject",
user: users[i].text,
receivingchannel : channel,
projectID: projectID
});
}
dojox.cometd.endBatch();
};
var removeUsersFromProject = function(users) {
var channel = "/noresponse";
dojox.cometd.startBatch();
for (var i = 0;i<users.length;i++){
dojox.cometd.publish("/requests/service", {
action: "removeUserFromProject",
user: users[i].text,
receivingchannel : channel,
projectID: projectID
});
}
dojox.cometd.endBatch();
};

//create user list tool

var userListTree = new Ext.ux.MultiSelectTreePanel({
projectID: this.projectID,
projectName: this.projectName,
autoScroll :true,
id: 'userListTree',
title: 'User List',
useArrows: true,
rootVisible :false,
singleExpand :true,
enableDD: true,
ddGroup:'doubleListTree',
afterRender:function() {
Ext.ux.MultiSelectTreePanel.prototype.afterRender.apply(this, arguments);
new Ext.dd.DropTarget(this.body, {
// must be same as for tree

ddGroup:'doubleListTree',

// what to do when user drops a node here

notifyDrop:function(dd, e, dragdata) {
e.preventDefault();
e.stopEvent();
for(var i=0; i<dragdata.nodes.length; i++) {
Ext.getCmp('userListTree').getRootNode().appendChild(dragdata.nodes[i]);
//call cometd channel to remove these users from the project

}
removeUsersFromProject(dragdata.nodes);
returntrue;
} // eo function notifyDrop

});

}, // eo function afterRender


root :new Ext.tree.TreeNode({id:'userListTreeRoot'})
});

var participantListTree = new Ext.ux.MultiSelectTreePanel({

projectID: this.projectID,
projectName: this.projectName,
autoScroll :true,
id: 'participantListTree',
title: 'Participant List of '+this.projectName,
useArrows: true,
rootVisible :false,
singleExpand :true,
enableDD: true,
ddGroup:'doubleListTree',
afterRender:function() {
Ext.ux.MultiSelectTreePanel.prototype.afterRender.apply(this, arguments);
new Ext.dd.DropTarget(this.body, {
// must be same as for tree

ddGroup:'doubleListTree',
// what to do when user drops a node here

notifyDrop:function(dd, e, dragdata) {
e.preventDefault();
e.stopEvent();
for(var i=0; i<dragdata.nodes.length; i++) {
Ext.getCmp('participantListTree').getRootNode().appendChild(dragdata.nodes[i]);
//call cometd channel to remove these users from the project

}
addUsersToProject(dragdata.nodes);
returntrue;
} // eo function notifyDrop

});
}, // eo function afterRender


root :new Ext.tree.TreeNode({id:'participantListTreeRoot'})

});

var doubleUserListPanel = new Ext.Panel({
xtype: 'panel',
border: false,
layout: 'table',
layoutConfig: {
// The total column count must be specified here

columns: 3
},
items: [userListTree,
{
xtype:'panel',
header:false,
items:[{
xtype:'button',
text: '>>',
handler: function(){
var sm = Ext.getCmp('userListTree').getSelectionModel();
var projectID = Ext.getCmp('userListTree').projectID;
var selectedNodes = sm.getSelectedNodes();//get current selected nodes

sm.clearSelections();
if (selectedNodes.length == 0){
Ext.MessageBox.show({
title: 'Interface Assistance',
msg: 'Please select a user to move',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.WARNING
});
//alert('please select a user to move first');

}else{

for (var i = 0;i<selectedNodes.length;i++){
Ext.getCmp('participantListTree').getRootNode().appendChild(new Ext.tree.TreeNode({
leaf: true,
text: selectedNodes[i].text,
id: selectedNodes[i].text
}));
selectedNodes[i].remove();
}
addUsersToProject(selectedNodes);

}
}
},{
xtype:'button',
text: '<<',
handler: function(){
var sm = Ext.getCmp('participantListTree').getSelectionModel();
var projectID = Ext.getCmp('participantListTree').projectID;
var selectedNodes = sm.getSelectedNodes();//get current selected nodes

sm.clearSelections();
if (selectedNodes.length == 0){
Ext.MessageBox.show({
title: 'Interface Assistance',
msg: 'Please select a user to move',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.WARNING
});
}else{

for (var i = 0;i<selectedNodes.length;i++){

Ext.getCmp('userListTree').getRootNode().appendChild(new Ext.tree.TreeNode({
leaf: true,
text: selectedNodes[i].text,
id: selectedNodes[i].text
}));
selectedNodes[i].remove();
}
removeUsersFromProject(selectedNodes);

}
}
}]
},
participantListTree]
});
// end of creating user list tool

this.items = doubleUserListPanel;
actioncenter.projectAssigner.superclass.initComponent.apply(this,arguments);
},
Resize:function(){
var s=this.getSize();
this.items.get(0).items.get(0).setSize(s.width/3,s.height-40);
this.items.get(0).items.get(2).setSize(s.width/3,s.height-40);
this.items.get(0).setPosition((s.width-s.width/1.5-15)/2,20);
},
onRender : function(ct, position){
actioncenter.projectAssigner.superclass.onRender.call(this, ct, position);
this.on('bodyresize',this.Resize,this);
},
afterRender : function(){
//get the user list after this component already rendered.

actionCenterProject.getUsers(this.projectID);
actionCenterProject.getProjectUsers(this.projectID);
actioncenter.projectAssigner.superclass.afterRender.apply(this,arguments);
}

});
actioncenter.ProjectTypeArray.register(actioncenter.projectAssigner);

mjaomaha
11 Jun 2009, 11:01 AM
If you are using a default selection model (or variant thereof) it will have registered it's own click listener which will fire select the second time.


On one of the other trees (the tree on the left side of the page), I have custom nodes that do have their own selection. Would this have any effect? I'm not using these nodes on the drag and drop trees.




actioncenter.projectNodeUI = Ext.extend(Ext.tree.TreeNodeUI,{
onContextMenu : function(e){
e.stopEvent();
this.onClick(e);
if (!this.node.menu){
this.node.menu = new Ext.menu.Menu({
items: [{
text: 'Copy'
},{
text: 'Edit Properties',handler: actionCenterProject.editProjectProperties
},{
text: 'Close',handler: actionCenterProject.closeProjectInExplorer
}]});
}
this.node.select();//Select the node
this.node.menu.showAt(e.getXY());
actioncenter.projectNodeUI.superclass.onContextMenu.apply(this,arguments)
},


onDblClick : function(e){
e.stopEvent();
var targetTabPanelId = this.node.attributes.targetTabPanelId;


if (Ext.getCmp(targetTabPanelId).findById(this.node.id + '-tabpanel') == null){
Ext.getCmp(targetTabPanelId).removeAll();
Ext.getCmp(targetTabPanelId).add(new actioncenter.projectTabPanel({
projectID: this.node.id,
title: this.node.text,
tabPosition:'bottom',
closable:true,
id: this.node.id + '-tabpanel',
type: 'project',
layoutOnTabChange: true,
activeTab: 0
}));
Ext.getCmp(targetTabPanelId).findParentByType('panel').doLayout();
Ext.getCmp(targetTabPanelId).activate(this.node.id+'-tabpanel');
} else {
Ext.getCmp(targetTabPanelId).activate(this.node.id+'-tabpanel');
}
actioncenter.projectNodeUI.superclass.onDblClick.apply(this,arguments)
}


})


actioncenter.projectNode = function(attributes){
this.pretext = attributes.pretext;
var uiClass = this.attributes.uiProvider || this.defaultUI || actioncenter.projectNodeUI;
/**
*Read-only.TheUIforthisnode
*@typeTreeNodeUI
*/
this.ui = new uiClass(this);
actioncenter.projectNode.superclass.constructor.call(this); // call superclass' constructor
};


actioncenter.projectNode = Ext.extend(Ext.tree.TreeNode,{
type: 'project',
cls:'x-tree-node-actioncenter',
targetTabPanelId: '',
subcribeAllChannels: function() {
var channel = "/contributions/contribution/" + this.id + "/update";
dojox.cometd.startBatch();
dojox.cometd.subscribe(channel,this,'editNode');
dojox.cometd.endBatch();


},
unsubscribeAllChannels: function() {
var channel = "/contributions/contribution/" + this.id + "/update";
dojox.cometd.startBatch();
dojox.cometd.unsubscribe(channel,this,'editNode');
dojox.cometd.endBatch();
},
editNode: function(msg) {
var newText;
try {
newText = msg.data.contribution.contributionProperties.projectName.value;
}
catch(err) {
return;
}
this.setText(newText);
},
addChild: function(msg){


},
deleteChild: function(msg){


},
//pretext: attributes.pretext
uiProvider: actioncenter.projectNodeUI
});

Deadmeat
13 Jun 2009, 1:44 AM
I'm still looking at this and trying to see what it going on. Sorry I haven't had more time to help out.

A few points.

It shouldn't matter what you do in different tree instances. Unless you try and mash incompatible components together in the same tree instance, or share objects between instances. Using different trees components on the same page should work fine so long as they are separate instances.

I have ten something tree instances (including a default tree) on my demo page without any ill effects that I can see.

If I can't get your code working, I'll try and mock up something similar and see where it get's me.

I can see what you're trying to do. I've been thinking about something very similar to this. I just haven't had to time to try it out.

Deadmeat
13 Jun 2009, 3:46 AM
Hi mjaomaha,

I'm totally stumped for further ideas.

I put together your panel (or something akin to it) at: http://www.users.on.net/~clear/ext/dropbug.html and it seems to work okayish for me. Of course it's not entirely complete.

Obviously it's combination of several things which I can't reproduce. I can't logically see how trees in other panels, or in other places could be having any effect. Mixing dojo code into ext apps should be fine.

All I can suggest is perhaps you breakdown the app into components. Implement just the broken panel in isolation on a page and see if the bug shows up. Keep adding layers until you start seeing problems.

I have found one bug though: Selection doesn't work at all without enableDD or enabledDrag set. I'll have to fix this I guess. I get a few people using the tree for stuff other than drag and drop. I'll add it to the known issues for now.

You can also change enableDD to enableDrag since you have implemented your own drop functionality and the dropzone it automatically creates is superfluous.

Good luck..

mjaomaha
18 Jun 2009, 12:42 PM
I created an offline version, and all you'll need is extjs 2.2.1 and it should run. To see it work, you just create a new project, and then double click on the project and you should see the double list pop up. Here is the source code with dojo disabled for the important functions.




<html>
<head>
<title>CACE System</title>
<!-- Include Ext stylesheets here: -->
<link rel="stylesheet" type="text/css" href="extjs-2.2.1/resources/css/ext-all.css">
<link rel="stylesheet" type="text/css" href="actioncenters/resources/css/tree.css">
<style type="text/css">
.focusborder {
border: 2px solid #66FF00;
}
.tabs{
background-image:url(images/folder.gif) !important;
}
.error-text-style{
color:#E52626; font-size:100%;
}
</style>
<!-- Include Ext and app-specific scripts: -->
<script type="text/javascript" src="dojo-release-1.2.3/dojo/dojo.js"></script>
<script type="text/javascript">

var workspaceUser = 'Admin';
</script>
<script type="text/javascript" src="extjs-2.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="extjs-2.2.1/ext-all-debug.js"></script>
<script type="text/javascript">
/*
* JavaScript file created by Rockstarapps Concatenation
*/

/*
* START OF FILE - /actioncenters/src/main/webapp/actioncenters/offline/TreePanelExtensions.js

*/
/*
* MultiSelectTreePanel v 1.1
*
* This work is derivative of Ext-JS 2.2. Much of the code is modified versions of default code.
* Refer to Ext-JS 2.2 licencing for more information. http://extjs.com/license
*
* Any and all original code is made available as is for whatever purpose you see fit.
*
* Should be a largely drop in replacement for ordinary TreePanel when you require multiselect

* with drag and drop. Overrides most of the methods and events to pass a nodelist rather than
* a single node.
*
* Note that the code is provided as-is and should be considered experimental and likely to contain
* bugs, especially when combined with other extensions or modifications to the default library.
*
* It has been tested against Ext-JS 2.2 and 2.2.1 with:
*
* Firefox 3, Opera 9.5+, Safari 3.1, MSIE 6,7,8rc1 (+5.5 seems to work too)
*
* Usage:
*
* Add the following CSS to make the floating "drag" version of the tree indent prettily..

.x-dd-drag-ghost .x-tree-node-indent,.x-dd-drag-ghost .x-tree-ec-icon {display: inline !important;}

*
* If you are using Ext-JS 2.2.1 or earlier you need to add this override! (reported as a bug)


Ext.override(Ext.tree.TreeDropZone, {
completeDrop : function(de){
var ns = de.dropNode, p = de.point, t = de.target;
if(!Ext.isArray(ns)){
ns = [ns];
}
var n, node, ins = false;
if (p != 'append'){
ins = true;
node = (p == 'above') ? t : t.nextSibling;
}
for(var i = 0, len = ns.length; i < len; i++){
n = ns[i];
if (ins){
t.parentNode.insertBefore(n, node);
}else{
t.appendChild(n);
}
if(Ext.enableFx && this.tree.hlDrop){
n.ui.highlight();
}
}
ns[0].ui.focus();
t.ui.endDrop();
this.tree.fireEvent("nodedrop", de);
}

});

*
* Instantiate like a normal tree (except DD stuff is enabled by default)

var tree = new Ext.ux.MultiSelectTreePanel({
autoScroll:true,
width:400,
height:500,
animate:true,
containerScroll: true,
enableDD: true,
root: new Ext.tree.AsyncTreeNode({
text: 'A Book',
draggable:false,
id:'node0'
}),
loader: new Ext.tree.TreeLoader({
dataUrl:'bookdata.json'
})
});
tree.render("target");

*
* When listening for DND events look for dragdata.nodes instead of dragdata.node
*
* Use ctrl-click to select multiple nodes.
* Use shift-click to select a range of nodes.
*
* Changelog

*
* v1.0 Initial Release
*
* v1.1
* - reinstated enableDD, enableDrag, enableDrop config params. *NEED TO INCLUDE THIS NOW*
* - consolidated compareDocumentPosition code into compareNodeOrder (only works with rendered nodes)
* - cleaned up select function by above and creating selectNode function.
* - cleaned up DDGhost generation code to be less hacky (still not ideal)
* - included onContainerOver and onContainerDrop code (awaiting ExtJS fix)
* - fixed several lingering postdrag selection bugs
* - fixed key events to respect shift/ctrl keys
*
* Enjoy
*/

/* If you are using Ext-JS 2.2.1 or earlier you need to add this override! (reported as a bug)*/

Ext.override(Ext.tree.TreeDropZone, {
completeDrop : function(de){
var ns = de.dropNode, p = de.point, t = de.target;
if(!Ext.isArray(ns)){
ns = [ns];
}
var n, node, ins = false;
if (p != 'append'){
ins = true;
node = (p == 'above') ? t : t.nextSibling;
}
for(var i = 0, len = ns.length; i < len; i++){
n = ns[i];
if (ins){
t.parentNode.insertBefore(n, node);
}else{
t.appendChild(n);
}
if(Ext.enableFx && this.tree.hlDrop){
n.ui.highlight();
}
}
ns[0].ui.focus();
t.ui.endDrop();
this.tree.fireEvent("nodedrop", de);
}

});

Ext.ux.FixedMultiSelectionModel = Ext.extend(Ext.tree.MultiSelectionModel, {
// disabled tracking of mouse clicks because it doubles up drag selection...
//onNodeClick : function(node, e){
// if (e.shiftKey) e.preventDefault();
// this.select(node);
//},

// private
// for comparing node order... (taken from quirksmode.org and googlecode)
compareNodeOrder: document.compareDocumentPosition ?
function(node1, node2) {
// W3C DOM lvl 3 method (Gecko)
return 3 - (node1.ui.elNode.compareDocumentPosition(node2.ui.elNode) & 6);
} :
(typeof document.documentElement.sourceIndex !== "undefined" ?
function(node1, node2) {
// IE source index method
return node1.ui.elNode.sourceIndex - node2.ui.elNode.sourceIndex;
} :
function(node1, node2) {
if (node1 == node2) return 0;
// Safari doesn't support compareDocumentPosition or sourceIndex
// from http://code.google.com/p/doctype/wiki/ArticleNodeCompareDocumentOrder
var range1 = document.createRange();
range1.selectNode(a.ui.elNode);
range1.collapse(true);

var range2 = document.createRange();
range2.selectNode(b.ui.elNode);
range2.collapse(true);

return range1.compareBoundaryPoints(Range.START_TO_END, range2);
}
),

// private
sortSelNodes: function() {
if (this.selNodes.length > 1) {
if (!this.selNodes[0].ui.elNode) return;
this.selNodes.sort(this.compareNodeOrder);
}
},

// private single point for selectNode
selectNode: function(node, push) {
if (!this.isSelected(node)) {
this.selNodes.push(node);
this.selMap[node.id] = node;
node.ui.onSelectedChange(true);
}
},

// overwritten from MultiSelectionModel to fix unselecting...
select : function(node, e, keepExisting){
// Add in setting an array as selected... (for multi-selecting D&D nodes)
if(node instanceof Array){
for (var c=0;c<node.length;c++) {
this.selectNode(node[c]);
}
this.sortSelNodes();
this.fireEvent("selectionchange", this, this.selNodes, this.lastSelNode);
return node;
}
// Shift Select to select a range
// NOTE: Doesn't change lastSelNode
// EEK has to be a prettier way to do this
if (e && e.shiftKey && this.selNodes.length > 0) {
this.lastSelNode = this.lastSelNode || this.selNodes[0];
var before = this.compareNodeOrder(this.lastSelNode, node) > 0;
// if (this.lastSelNode == node) {
// check dom node ordering (from ppk of quirksmode.org)
this.clearSelections(true);
var cont = true;
var inside = false;
var parent = this.lastSelNode;
// ummm... yeah don't read this bit...
do {
for (var next=parent;next!=null;next=(before?next.previousSibling:next.nextSibling)) {
// hack to make cascade work the way I want it to
inside = inside || (before && (next == node || next.contains(node)));
if (next.isExpanded()) {
next.cascade(function(n) {
if (cont != inside) {
this.selectNode(n);
}
cont = (cont && n != node);
return true;
}, this);
} else {
this.selectNode(next);
cont = (next != node);
}
if (!cont) break;
}
if (!cont) break;
while ((parent = parent.parentNode) != null) {
if (before) {
this.selectNode(parent);
}
cont = (cont && parent != node);
if (before && parent.previousSibling) {
parent = parent.previousSibling;
break;
}
if (!before && parent.nextSibling) {
parent = parent.nextSibling;
break;
}
}
if (!cont) break;
} while (parent != null);
this.selectNode(node);
// sort the list
this.sortSelNodes();
this.fireEvent("selectionchange", this, this.selNodes, node);
e.preventDefault();
return node;
} else if(keepExisting !== true) {
this.clearSelections(true);
}
if(this.isSelected(node)) {
// handle deselect of node...
if (keepExisting === true) {
this.unselect(node);
if (this.lastSelNode === node) {
this.lastSelNode = this.selNodes[0];
}
return node;
}
this.lastSelNode = node;
return node;
}
// save a resort later on...
this.selectNode(node);
this.sortSelNodes();
this.lastSelNode = node;
this.fireEvent("selectionchange", this, this.selNodes, this.lastSelNode);
return node;
},
// returns selected nodes precluding children of other selected nodes...
// used for multi drag and drop...
getUniqueSelectedNodes: function() {
var ret = [];
for (var c=0;c<this.selNodes.length;c++) {
var parent = this.selNodes[c];
ret.push(parent);
// nodes are sorted(?) so skip over subsequent nodes inside this one..
while ((c+1)<this.selNodes.length && parent.contains(this.selNodes[c+1])) c++;
}
return ret;
},

// check for descendents when nodes are removed...
unselect: function(node, subnodes) {
if (subnodes) {
for (var c=this.selNodes.length-1;c>=0;c--) {
if (this.selNodes[c].isAncestor(node)) {
Ext.ux.FixedMultiSelectionModel.superclass.unselect.call(this, this.selNodes[c]);
}
}
}
return Ext.ux.FixedMultiSelectionModel.superclass.unselect.call(this, node);
},

/**
* Selects the node above the selected node in the tree, intelligently walking the nodes
* @return TreeNode The new selection
*/
selectPrevious : function(keepExisting){
var s = this.selNodes[0];
if(!s){
return null;
}
var ps = s.previousSibling;
if(ps){
if(!ps.isExpanded() || ps.childNodes.length < 1){
return this.select(ps, null, keepExisting);
} else{
var lc = ps.lastChild;
while(lc && lc.isExpanded() && lc.childNodes.length > 0){
lc = lc.lastChild;
}
return this.select(lc, null, keepExisting);
}
} else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
return this.select(s.parentNode, null, keepExisting);
}
return null;
},

/**
* Selects the node above the selected node in the tree, intelligently walking the nodes
* @return TreeNode The new selection
*/
selectNext : function(keepExisting){
var s = this.selNodes[this.selNodes.length-1];
if(!s){
return null;
}
if(s.firstChild && s.isExpanded()){
return this.select(s.firstChild, null, keepExisting);
}else if(s.nextSibling){
return this.select(s.nextSibling, null, keepExisting);
}else if(s.parentNode){
var newS = null;
s.parentNode.bubble(function(){
if(this.nextSibling){
newS = this.getOwnerTree().selModel.select(this.nextSibling, null, keepExisting);
return false;
}
});
return newS;
}
return null;
},

onKeyDown : function(e){
var s = this.selNode || this.lastSelNode;
// undesirable, but required
var sm = this;
if(!s){
return;
}
var k = e.getKey();
switch(k){
case e.DOWN:
e.stopEvent();
this.selectNext(e.shiftKey || e.ctrlKey);
break;
case e.UP:
e.stopEvent();
this.selectPrevious(e.shiftKey || e.ctrlKey);
break;
case e.RIGHT:
e.preventDefault();
if(s.hasChildNodes()){
if(!s.isExpanded()){
s.expand();
}else if(s.firstChild){
this.select(s.firstChild, e, e.shiftKey || e.ctrlKey);
}
}
break;
case e.LEFT:
e.preventDefault();
if(s.hasChildNodes() && s.isExpanded()){
s.collapse();
}else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
this.select(s.parentNode, e, e.shiftKey || e.ctrlKey);
}
break;
};
}

});
/*
Enhanced to support dragging multiple nodes...

for extension refer to data.nodes instead of data.node

*/
Ext.ux.MultiSelectTreeDragZone = Ext.extend(Ext.tree.TreeDragZone, {
onBeforeDrag : function(data, e){
if (data.nodes && data.nodes.length > 0) {
for (var c=0;c<data.nodes.length;c++) {
n = data.nodes[c];
if (n.draggable === false || n.disabled) return false
}
return true;
} else if (data.node) {
if (data.node.draggable === false || data.node.disabled) return false
}
return false;

},
// v1.0
// fixed to handle multiSelectionModel
// Data now calls SelectionModel.select instead of waiting for the click event
// Creates Ghost inline rather than calling TreeNodeUI.
//
// v1.1
// cleanup to have ghost generation slightly less hacky... still hacky though...
// fixes problems with using extra tag nesting in a custom TreeNodeUI.
getDragData : function(e) {
// use tree selection model..
var selModel = this.tree.getSelectionModel();
// get event target
var target = Ext.dd.Registry.getHandleFromEvent(e);
// if no target (die)
if (target == null) return;

if (target.node.isSelected() && e.ctrlKey) {
selModel.unselect(target.node);
return;
}
var selNodes = [];
if (!selModel.getSelectedNodes) {
// if not multiSelectionModel.. just use the target...
selNodes = [target.node];
} else {
// if target not selected select it...
if (!target.node.isSelected() || e.shiftKey) {

selModel.select(target.node, e, e.ctrlKey);

}
// get selected nodes - nested nodes...
selNodes = selModel.getUniqueSelectedNodes();
}
// if no nodes selected stop now...
if (!selNodes || selNodes.length < 1) return;
var dragData = { nodes: selNodes };
// create a container for the proxy...
var div = document.createElement('ul'); // create the multi element drag "ghost"
// add classes to keep is pretty...
div.className = 'x-tree-node-ct x-tree-lines';
// add actual dom nodes to div (instead of tree nodes)
var height = 0;
for(var i = 0, len = selNodes.length; i < len; i++) {
// add entire node to proxy
// normally this is done by TreeNodeUI.appendDDGhost(), but overriding that class requires
// also overriding TreeLoader etc. Ext.extend() is an option though...
var clonenode = selNodes[i].ui.wrap.cloneNode(true);
// fix extra indenting by removing extra spacers
// should really modify UI rendering code to render a duplicate subtree but this is simpler...
// count current indent nodes from ui indentNode... (add 1 for elbow)
var subtract = selNodes[i].ui.indentNode.childNodes.length + 1;
// avoid indent alterations if possible..
if (subtract > 0) {
// relies on node ui using the same tag for all elems...
var subNodes = Ext.query(selNodes[i].ui.indentNode.nodeName+".x-tree-node-indent", clonenode);
for (var c=0,clen=subNodes.length;c<clen;c++) {
var inode = subNodes[c];
var current = inode.childNodes.length;
if (current <= subtract) {
inode.innerHTML = "";
// remove elbow icon as well..
if (current < subtract) inode.parentNode.removeChild(subNodes[c].nextSibling);
} else {
for (var r=0;r<subtract;r++) {
subNodes[c].removeChild(subNodes[c].firstChild);
}
}
}
}
div.appendChild(clonenode);
Ext.fly(clonenode).removeClass(['x-tree-selected','x-tree-node-over']);
}
dragData.ddel = div;
return dragData;
},
// fix from TreeDragZone (references dragData.node instead of dragData.nodes)
onInitDrag : function(e){
var data = this.dragData;
this.tree.eventModel.disable();
this.proxy.update("");
this.proxy.ghost.dom.appendChild(data.ddel);
this.tree.fireEvent("startdrag", this.tree, data.nodes, e);
},
// Called from TreeDropZone (looks like hack for handling multiple tree nodes)
getTreeNode: function() {
return this.dragData.nodes;
},
// fix from TreeDragZone (refers to data.node instead of data.nodes)
// Don't know what this does, so leaving as first node.
getRepairXY : function(e, data){
return data.nodes[0].ui.getDDRepairXY();
},

// fix from TreeDragZone (refers to data.node instead of data.nodes)
onEndDrag : function(data, e){
this.tree.eventModel.enable.defer(100, this.tree.eventModel);
this.tree.fireEvent("enddrag", this.tree, data.nodes || [data.node], e);
},

// fix from TreeDragZone (refers to dragData.node instead of dragData.nodes)
onValidDrop : function(dd, e, id){
this.tree.fireEvent("dragdrop", this.tree, this.dragData.nodes, dd, e);
this.hideProxy();
},

// fix for invalid Drop
beforeInvalidDrop : function(e, id){
// this scrolls the original position back into view
var sm = this.tree.getSelectionModel();
// sm.clearSelections();
// sm.select(this.dragData.nodes, e, true);
}

});

/*

MultiSelectTreeDropZone

Contains following fixups

- modified functions to handle multiple nodes in dd operation
isValidDropPoint
afterRepair
- modified getDropPoint such that isValidDropPoint can simulate leaf style below inserting.
Overriding isValidDropPoint affects getDropPoint affects onNodeOver and onNodeDrop

Refer to data.nodes instead of data.node for events..

*/
Ext.ux.MultiSelectTreeDropZone = Ext.extend(Ext.tree.TreeDropZone, {

// fix from TreeDropZone (referred to data.node instead of data.nodes)
isValidDropPoint : function(n, pt, dd, e, data){
if(!n || !data) { return false; }
var targetNode = n.node;
var dropNodes = data.nodes?data.nodes:[data.node];
// default drop rules
if(!(targetNode && targetNode.isTarget && pt)){
return false;
}
if(pt == "append" && targetNode.allowChildren === false){
return false;
}
if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
return false;
}
// don't allow dropping a treenode inside itself...
for (var c=0;c<dropNodes.length;c++) {
if(dropNodes[c] && (targetNode == dropNodes[c] || dropNodes[c].contains(targetNode))){
return false;
}
}
// reuse the object
var overEvent = this.dragOverData;
overEvent.tree = this.tree;
overEvent.target = targetNode;
overEvent.data = data;
overEvent.point = pt;
overEvent.source = dd;
overEvent.rawEvent = e;
overEvent.dropNode = dropNodes;
overEvent.cancel = false;
var result = this.tree.fireEvent("nodedragover", overEvent);
return overEvent.cancel === false && result !== false;
},

// override to allow insert "below" when leaf != true...
getDropPoint : function(e, n, dd, data){
var tn = n.node;
if(tn.isRoot){
return this.isValidDropPoint(n, "append", dd, e, data)? "append" : false;
}
var dragEl = n.ddel;
var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
var y = Ext.lib.Event.getPageY(e);
var noAppend = tn.allowChildren === false || tn.isLeaf() || !this.isValidDropPoint(n, "append", dd, e, data);
if(!this.appendOnly && tn.parentNode.allowChildren !== false){
var noBelow = false;
if(!this.allowParentInsert){
noBelow = tn.hasChildNodes() && tn.isExpanded();
}
var q = (b - t) / (noAppend ? 2 : 3);
if(y >= t && y < (t + q) && this.isValidDropPoint(n, "above", dd, e, data)){
return "above";
}else if(!noBelow && (noAppend || y >= b-q && y <= b) && this.isValidDropPoint(n, "below", dd, e, data)){
return "below";
}
}
return noAppend? false: "append";
},

// Override because it calls getDropPoint and isValidDropPoint
onNodeOver : function(n, dd, e, data){
var pt = this.getDropPoint(e, n, dd, data);
var node = n.node;

if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
this.queueExpand(node);
}else if(pt != "append"){
this.cancelExpand();
}

var returnCls = this.dropNotAllowed;
if(pt){
var el = n.ddel;
var cls;
if(pt == "above"){
returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
cls = "x-tree-drag-insert-above";
}else if(pt == "below"){
returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
cls = "x-tree-drag-insert-below";
}else{
returnCls = "x-tree-drop-ok-append";
cls = "x-tree-drag-append";
}
if(this.lastInsertClass != cls){
Ext.fly(el).replaceClass(this.lastInsertClass, cls);
this.lastInsertClass = cls;
}
}
return returnCls;
},

// Override because it calls getDropPoint and isValidDropPoint
onNodeDrop : function(n, dd, e, data){
var point = this.getDropPoint(e, n, dd, data);
var targetNode = n.node;
targetNode.ui.startDrop();
if(point === false) {
targetNode.ui.endDrop();
return false;
}

var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
var dropEvent = {
tree : this.tree,
target: targetNode,
data: data,
point: point,
source: dd,
rawEvent: e,
dropNode: dropNode,
cancel: !dropNode,
dropStatus: false
};
var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
targetNode.ui.endDrop();
return dropEvent.dropStatus;
}

targetNode = dropEvent.target;
if(point == "append" && !targetNode.isExpanded()){
targetNode.expand(false, null, function(){
this.completeDrop(dropEvent);
}.createDelegate(this));
}else{
this.completeDrop(dropEvent);
}
return true;
},

// fix from TreeDropZone (referred to data.node instead of data.nodes)
afterRepair : function(data){
if(data && Ext.enableFx){
var nl = data.nodes?data.nodes:[data.node];
for (var c=0,len=nl.length;c<len;c++) {
nl[c].ui.highlight();
}
}
this.hideProxy();
},

// handle allowContainerDrop (appends nodes to the root node)
onContainerDrop : function(dd, e, data) {
if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
var targetNode = this.tree.getRootNode();
targetNode.ui.startDrop();
var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, "append", e) : null);
var dropEvent = {
tree : this.tree,
target: targetNode,
data: data,
point: "append",
source: dd,
rawEvent: e,
dropNode: dropNode,
cancel: !dropNode,
dropStatus: false
};
var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
targetNode.ui.endDrop();
return dropEvent.dropStatus;
}

targetNode = dropEvent.target;
if(!targetNode.isExpanded()){
targetNode.expand(false, null, function(){
this.completeDrop(dropEvent);
}.createDelegate(this));
}else{
this.completeDrop(dropEvent);
}
return true;
}
return false;
},

// handle allowContaineDrop (treat as a drop to the root node)
onContainerOver : function(dd, e, data) {
if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
return this.dropAllowed;
}
return this.dropNotAllowed;
}

});

/*

MultiSelectTreePanel

sets up using FixedMultiSelectionModel
and initing with extended DragZone and DropZone by default

*/

Ext.ux.MultiSelectTreePanel = Ext.extend(Ext.tree.TreePanel, {

getSelectionModel : function(){
if(!this.selModel){
this.selModel = new Ext.ux.FixedMultiSelectionModel();
}
return this.selModel;
},

initEvents: function() {
if((this.enableDD || this.enableDrop) && !this.dropZone){
this.dropZone = new Ext.ux.MultiSelectTreeDropZone(this, this.dropConfig || {
ddGroup: this.ddGroup || "TreeDD",
appendOnly: this.ddAppendOnly === true
});
}
if((this.enableDD || this.enableDrag) && !this.dragZone){
this.dragZone = new Ext.ux.MultiSelectTreeDragZone(this, {
ddGroup: this.ddGroup || "TreeDD",
scroll: this.ddScroll
});
}
Ext.ux.MultiSelectTreePanel.superclass.initEvents.apply(this, arguments);

// This is temporary. Should really Ext.extend on TreeNode.removeChild()
// and call getOwnerTree().removeNode(node) or similar...

this.on("remove", function(tree, parent, node) {
tree.getSelectionModel().unselect(node, true);
});
}
});

Ext.reg('multiselecttreepanel', Ext.ux.MultiSelectTreePanel);
/*
* END OF FILE - /actioncenters/src/main/webapp/actioncenters/offline/TreePanelExtensions.js
*/

/*
* START OF FILE - /actioncenters/src/main/webapp/actioncenters/offline/SubPanels.js
*/
Ext.namespace('actioncenter');

/****************************** List of Project Type objects ***********************/
/*The purpose of this array is to store all the panel objects that are going to be the children of the project tab panel*/
actioncenter.ProjectTypeArray = function(){
var list = [];

return {
/*
* registerType register a class to the types hash map along with the key actype
* */
register : function(projectTypeObject){
list.push(projectTypeObject);
},
/*
* create is used to instantiate a class that has the actype with the configuration passed as parameter
* */
getList : function(){
return list;
}
};
}();
/********************************************************************/

/****************************** Project Tab Panel using Project Type array as children list***********************/
actioncenter.projectTabPanel = Ext.extend(Ext.TabPanel,{
projectID: '',
projectName: '',
initComponent: function() {
//alert(this.items[0].title);
var projectTypeClassList = actioncenter.ProjectTypeArray.getList();
var childrenList = [];
var i;
for (i = 0;i<projectTypeClassList.length;i++){
childrenList.push(new projectTypeClassList[i]({
projectID: this.projectID,
projectName: this.title
}));
}
this.items = childrenList;
actioncenter.projectTabPanel.superclass.initComponent.apply(this,arguments);
}
});
/********************************************************************/

/****************************** All Project SubPanels ***********************/
actioncenter.projectAssigner = Ext.extend(Ext.Panel, {
title: 'Project Assigner',
projectID:'',
projectName:'',
layout: 'absolute',


initComponent : function(){
var projectID = this.projectID;
var addUsersToProject = function(users) {
var channel = "/noresponse";
//dojox.cometd.startBatch();
for (var i = 0;i<users.length;i++){
//dojox.cometd.publish("/requests/service", {
// action: "grantUserAccessToProject",
// user: users[i].text,
// receivingchannel : channel,
// projectID: projectID
//});
}
//dojox.cometd.endBatch();
};
var removeUsersFromProject = function(users) {
var channel = "/noresponse";
//dojox.cometd.startBatch();
for (var i = 0;i<users.length;i++){
//dojox.cometd.publish("/requests/service", {
// action: "removeUserFromProject",
// user: users[i].text,
// receivingchannel : channel,
// projectID: projectID
//});
}
//dojox.cometd.endBatch();
};

//create user list tool
var userListTree = new Ext.ux.MultiSelectTreePanel({
projectID: this.projectID,
projectName: this.projectName,
autoScroll :true,
id: 'userListTree',
title: 'User List',
useArrows: true,
rootVisible :false,
singleExpand :true,
enableDD: true,
ddGroup:'doubleListTree',
afterRender:function() {
Ext.ux.MultiSelectTreePanel.prototype.afterRender.apply(this, arguments);
new Ext.dd.DropTarget(this.body, {
// must be same as for tree
ddGroup:'doubleListTree',

// what to do when user drops a node here
notifyDrop:function(dd, e, dragdata) {
e.preventDefault();
e.stopEvent();
for(var i=0; i<dragdata.nodes.length; i++) {
Ext.getCmp('userListTree').getRootNode().appendChild(dragdata.nodes[i]);
//call cometd channel to remove these users from the project
}
removeUsersFromProject(dragdata.nodes);
return true;
} // eo function notifyDrop
});

}, // eo function afterRender

root :new Ext.tree.TreeNode({id:'userListTreeRoot'})
});

var participantListTree = new Ext.ux.MultiSelectTreePanel({

projectID: this.projectID,
projectName: this.projectName,
autoScroll :true,
id: 'participantListTree',
title: 'Participant List of '+this.projectName,
useArrows: true,
rootVisible :false,
singleExpand :true,
enableDD: true,
ddGroup:'doubleListTree',
afterRender:function() {
Ext.ux.MultiSelectTreePanel.prototype.afterRender.apply(this, arguments);
new Ext.dd.DropTarget(this.body, {
// must be same as for tree
ddGroup:'doubleListTree',
// what to do when user drops a node here
notifyDrop:function(dd, e, dragdata) {
e.preventDefault();
e.stopEvent();
for(var i=0; i<dragdata.nodes.length; i++) {
Ext.getCmp('participantListTree').getRootNode().appendChild(dragdata.nodes[i]);
//call cometd channel to remove these users from the project
}
addUsersToProject(dragdata.nodes);
return true;
} // eo function notifyDrop
});
}, // eo function afterRender

root :new Ext.tree.TreeNode({id:'participantListTreeRoot'})

});

var doubleUserListPanel = new Ext.Panel({
xtype: 'panel',
border: false,
layout: 'table',
layoutConfig: {
// The total column count must be specified here
columns: 3
},
items: [userListTree,
{
xtype:'panel',
header:false,
items:[{
xtype:'button',
text: '>>',
handler: function(){
var sm = Ext.getCmp('userListTree').getSelectionModel();
var projectID = Ext.getCmp('userListTree').projectID;
var selectedNodes = sm.getSelectedNodes();//get current selected nodes
sm.clearSelections();
if (selectedNodes.length == 0){
Ext.MessageBox.show({
title: 'Interface Assistance',
msg: 'Please select a user to move',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.WARNING
});
//alert('please select a user to move first');
}else{

for (var i = 0;i<selectedNodes.length;i++){
Ext.getCmp('participantListTree').getRootNode().appendChild(new Ext.tree.TreeNode({
leaf: true,
text: selectedNodes[i].text,
id: selectedNodes[i].text
}));
selectedNodes[i].remove();
}
addUsersToProject(selectedNodes);

}
}
},{
xtype:'button',
text: '<<',
handler: function(){
var sm = Ext.getCmp('participantListTree').getSelectionModel();
var projectID = Ext.getCmp('participantListTree').projectID;
var selectedNodes = sm.getSelectedNodes();//get current selected nodes
sm.clearSelections();
if (selectedNodes.length == 0){
Ext.MessageBox.show({
title: 'Interface Assistance',
msg: 'Please select a user to move',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.WARNING
});
}else{

for (var i = 0;i<selectedNodes.length;i++){

Ext.getCmp('userListTree').getRootNode().appendChild(new Ext.tree.TreeNode({
leaf: true,
text: selectedNodes[i].text,
id: selectedNodes[i].text
}));
selectedNodes[i].remove();
}
removeUsersFromProject(selectedNodes);

}
}
}]
},
participantListTree]
});
// end of creating user list tool
this.items = doubleUserListPanel;
actioncenter.projectAssigner.superclass.initComponent.apply(this,arguments);
},
Resize:function(){
var s=this.getSize();
this.items.get(0).items.get(0).setSize(s.width/3,s.height-40);
this.items.get(0).items.get(2).setSize(s.width/3,s.height-40);
this.items.get(0).setPosition((s.width-s.width/1.5-15)/2,20);
},
onRender : function(ct, position){
actioncenter.projectAssigner.superclass.onRender.call(this, ct, position);
this.on('bodyresize',this.Resize,this);
},
afterRender : function(){
//get the user list after this component already rendered.
actionCenterProject.getUsers(this.projectID);
actionCenterProject.getProjectUsers(this.projectID);
actioncenter.projectAssigner.superclass.afterRender.apply(this,arguments);
}

});
actioncenter.ProjectTypeArray.register(actioncenter.projectAssigner);



/********************************* AC BUILDER SUBPANEL FOR PROJECT ******************************/
actioncenter.acBuilder = Ext.extend(Ext.Panel, {
title: 'Action Center Builder',
projectID:'',
projectName:'',
layout:'fit',
html:'<p>This is a action center builder panel to be removed after demo.</p>'
///all the other stuff it does.
});
actioncenter.ProjectTypeArray.register(actioncenter.acBuilder);

/********************************************************************/
/*
* END OF FILE - /actioncenters/src/main/webapp/actioncenters/offline/SubPanels.js
*/

/*
* START OF FILE - /actioncenters/src/main/webapp/actioncenters/offline/CaceExplorer.js
*/
//Ext.namespace('actioncenter');
actioncenter.projectNodeUI = Ext.extend(Ext.tree.TreeNodeUI,{
onContextMenu : function(e){
e.stopEvent();
this.onClick(e);
if (!this.node.menu){
this.node.menu = new Ext.menu.Menu({
items: [{
text: 'Copy'
},{
text: 'Edit Properties',handler: actionCenterProject.editProjectProperties
},{
text: 'Close',handler: actionCenterProject.closeProjectInExplorer
}]});
}
this.node.select();//Select the node
this.node.menu.showAt(e.getXY());
actioncenter.projectNodeUI.superclass.onContextMenu.apply(this,arguments)
},

onDblClick : function(e){
e.stopEvent();
var targetTabPanelId = this.node.attributes.targetTabPanelId;

if (Ext.getCmp(targetTabPanelId).findById(this.node.id + '-tabpanel') == null){
Ext.getCmp(targetTabPanelId).removeAll();
Ext.getCmp(targetTabPanelId).add(new actioncenter.projectTabPanel({
projectID: this.node.id,
title: this.node.text,
tabPosition:'bottom',
closable:true,
id: this.node.id + '-tabpanel',
type: 'project',
layoutOnTabChange: true,
activeTab: 0
}));
Ext.getCmp(targetTabPanelId).findParentByType('panel').doLayout();
Ext.getCmp(targetTabPanelId).activate(this.node.id+'-tabpanel');
} else {
Ext.getCmp(targetTabPanelId).activate(this.node.id+'-tabpanel');
}
actioncenter.projectNodeUI.superclass.onDblClick.apply(this,arguments)
}

})

actioncenter.projectNode = function(attributes){
this.pretext = attributes.pretext;
var uiClass = this.attributes.uiProvider || this.defaultUI || actioncenter.projectNodeUI;
/**
* Read-only. The UI for this node
* @type TreeNodeUI
*/
this.ui = new uiClass(this);
actioncenter.projectNode.superclass.constructor.call(this); // call superclass' constructor
};

actioncenter.projectNode = Ext.extend(Ext.tree.TreeNode,{
type: 'project',
cls:'x-tree-node-actioncenter',
targetTabPanelId: '',
subcribeAllChannels: function() {
//var channel = "/contributions/contribution/" + this.id + "/update";
//dojox.cometd.startBatch();
//dojox.cometd.subscribe(channel,this,'editNode');
//dojox.cometd.endBatch();

},
unsubscribeAllChannels: function() {
//var channel = "/contributions/contribution/" + this.id + "/update";
//dojox.cometd.startBatch();
//dojox.cometd.unsubscribe(channel,this,'editNode');
//dojox.cometd.endBatch();
},
editNode: function(msg) {
var newText;
try {
newText = msg.data.contribution.contributionProperties.projectName.value;
}
catch(err) {
return;
}
this.setText(newText);
},
addChild: function(msg){

},
deleteChild: function(msg){

},
//pretext: attributes.pretext
uiProvider: actioncenter.projectNodeUI
});

/*
* END OF FILE - /actioncenters/src/main/webapp/actioncenters/offline/CaceExplorer.js
*/

/*
* START OF FILE - /actioncenters/src/main/webapp/actioncenters/offline/ProjectFunctions.js
*/
var actionCenterProject = {


/**************** EDITING A PROJECT ***************/
counter: 0,

editProjectProperties: function() {
//var channel = "/response/" + dojox.cometd.clientId + "/getprojectdata";
var sm = Ext.getCmp('treeExplorer').getSelectionModel();
var selectedNodes = sm.getSelectedNodes();//get current selected nodes
var node;
if(selectedNodes.length > 0) {
node = selectedNodes[0];
}
else {

Ext.MessageBox.show({
title: 'Interface Assistance',
msg: 'Please select a project to edit',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.WARNING
});


//alert("Please select a project to edit");
return;
}


var msg = {
data : {
id: node.id,
projectName: node.text,
projectDescription: '',
projectURL: ''

}
}
actionCenterProject.editProjectDialog(msg);

},


editProjectDialog : function(msg) {

var editProjectWindow = new Ext.Window({
id:'editProjectWindow',
layout: 'form',
title:'Edit project',
width: 500,
autoHeight: true,
bodyStyle: 'padding:5px',
draggable: true,
resizable: false,
closable:true,
frame: true,
modal: true,
displayError: function(msg){

if(msg.data.result == 'success'){

Ext.getCmp('editProjectWindow').close();

}
else {
//display the error here.
var error = 'Populate Error';

Ext.getCmp('projectError').setText(msg.data.errorstring,false);
Ext.getCmp('projectError').show();
}
//var channel = "/response/" + dojox.cometd.clientId + "/editProjectProperties";
//dojox.cometd.unsubscribe(channel,Ext.getCmp('editProjectWindow'),'displayError');
},
items:[
{
xtype:'label',
id: 'projectError',
hideLabel: true,
cls: 'error-text-style',
anchor: '100%'
},
{
xtype:'hidden',
id:'id',
name:'id',
value: msg.data.id
},
{
fieldLabel:'Project name',
xtype:'textfield',
id: 'projectName',
value: msg.data.projectName,
anchor: '100%',
allowBlank: false
},
{
fieldLabel:'Description',
xtype:'textarea',
id:'projectDescription',
value: msg.data.projectDescription,
anchor: '100%',
height: 100
},
{
fieldLabel:'URL',
xtype:'textfield',
id:'projectURL',
value: msg.data.projectURL,
anchor: '100%'

}],
buttons:[{
text: 'Save',
handler: function() {
actionCenterProject.editProjectPropertiesSave();
}
},{
text: 'Cancel',
handler: function(){
Ext.getCmp('editProjectWindow').close();
}
}],
buttonAlign: 'center'
}).show();
//var channel = "/response/" + dojox.cometd.clientId + "/getprojectdata";
//dojox.cometd.unsubscribe(channel, actionCenterProject, 'editProjectDialog');
},

editProjectPropertiesSave : function() {
//var treeRootId = Ext.getCmp('treeExplorer').getRootNode();
//var channel = "/response/" + dojox.cometd.clientId + "/editProjectProperties";
//dojox.cometd.subscribe(channel,Ext.getCmp('editProjectWindow'),'displayError');
//calls a function that gets the next project name
//dojox.cometd.publish("/requests/service", {
// action : "editProject",
// ParentId : treeRootId.id,
// user: workspaceUser,
// receivingchannel : channel,
// id: Ext.getCmp('editProjectWindow').findById('id').el.dom.value,
// projectName: Ext.getCmp('editProjectWindow').findById('projectName').el.dom.value,
// projectDescription: Ext.getCmp('editProjectWindow').findById('projectDescription').el.dom.value,
// projectURL: Ext.getCmp('editProjectWindow').findById('projectURL').el.dom.value
//});
var sm = Ext.getCmp('treeExplorer').getSelectionModel();
var selectedNodes = sm.getSelectedNodes();//get current selected nodes
var node;
if(selectedNodes.length > 0) {
node = selectedNodes[0];
}
node.setText(Ext.getCmp('editProjectWindow').findById('projectName').el.dom.value);

Ext.getCmp('editProjectWindow').close();
},

/**************** OPENING A PROJECT / RECYCLEBIN ***************/

openProjectEditors: function(type,projectid,displayName) {
},

getAvailableProjects: function() {
var channel = "/response/" + dojox.cometd.clientId + "/getavailableprojects";

dojox.cometd.startBatch();
dojox.cometd.subscribe(channel, actionCenterProject, 'openProjectInDesktopDialog');

//calls a function that gets the next project name
dojox.cometd.publish("/requests/service", {
action : "getAvailableProjects",
ParentId : '',
user: workspaceUser,
receivingchannel : channel,
id: ''
});
dojox.cometd.endBatch();
},

openProjectInDesktopDialog: function(msg) {
var myprojects = msg.data.allProjects;
var channel = "/response/" + dojox.cometd.clientId + "/getavailableprojects";
var openProjectTree = new Ext.ux.MultiSelectTreePanel({
anchor: '100%',
height: 400,
autoScroll :true,
id: 'openProjectTree',
//selModel: new Ext.tree.MultiSelectionModel(),
listeners: {'render':function(tp){
if(myprojects.length > 0){

for (var i = 0; i < myprojects.length; i++){
tp.getRootNode().appendChild(new actioncenter.projectNode({
text: myprojects[i].contributionProperties.projectName.value,
leaf: false,
cls:'x-tree-node-actioncenter',
id: myprojects[i].id,
targetTabPanelId:'workbenchPanel',//the tabpanel that the project will attach its editor tabpanel to
uiProvider: actioncenter.projectNodeUI
}));
}
}
}
},

// tree-specific configs:
rootVisible :false,
singleExpand :true,

root :new Ext.tree.TreeNode()
//allowBlank: false
});

var openProjectWindow = new Ext.Window({
id:'openProjectWindow',
layout: 'form',
title:'Open project',
width: 400,
autoHeight: true,
bodyStyle: 'padding:5px',
draggable: true,
hideLabels: true,
resizable: false,
closable:true,
frame: true,
modal: true,
items:openProjectTree,
buttons:[{
text: 'Open',
handler: function(){
var sm = Ext.getCmp('openProjectTree').getSelectionModel();
var selectedNodes = sm.getSelectedNodes();//get current selected nodes
sm.clearSelections();
if (selectedNodes.length == 0){
Ext.MessageBox.show({
title: 'Interface Assistance',
msg: 'Please select a project to open',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.WARNING
});
//alert('please select a project to open');
}else{

var treeRootId = Ext.getCmp('treeExplorer').getRootNode();
var channel = "/contributions/relationship/" + treeRootId.id + "/childof";
dojox.cometd.startBatch();

for(var i=0; i<selectedNodes.length; i++){
dojox.cometd.publish("/requests/service", {
action : "addRelationship",
parentContribution : treeRootId.id,
relationshipType: 'childof',
user: workspaceUser,
receivingchannel : channel,
childContribution: selectedNodes[i].attributes.id

});
}

dojox.cometd.endBatch();
//Ext.getCmp('treeExplorer').getRootNode().appendChild(selectedNodes);
Ext.getCmp('openProjectWindow').close();
}
}
},{
text: 'Cancel',
handler: function(){
Ext.getCmp('openProjectWindow').close();
}
}],
buttonAlign: 'center'
});
openProjectWindow.show();

dojox.cometd.unsubscribe(channel, actionCenterProject, 'openProjectInDesktopDialog');

},

openRecycleBin: function(){
var channel = "/response/" + dojox.cometd.clientId + "/openrecyclebin";

dojox.cometd.startBatch();
dojox.cometd.subscribe(channel, actionCenterProject, 'openRecycleBinInDesktopDialog');

//calls a function that gets the next project name
dojox.cometd.publish("/requests/service", {
action : "getRecycleBinProjects",
ParentId : '',
user: workspaceUser,
receivingchannel : channel,
id: ''
});
dojox.cometd.endBatch();

},

openRecycleBinInDesktopDialog: function(msg){
var myrecyclebinprojects = msg.data.recycleBinProjects;
var channel = "/response/" + dojox.cometd.clientId + "/openrecyclebin";
var openRecycleBinProjectTree = new Ext.ux.MultiSelectTreePanel({
anchor: '100%',
height: 400,
autoScroll :true,
id: 'openRecycleBinProjectTree',
listeners: {'render':function(tp){
if(myrecyclebinprojects.length > 0){
for (var i = 0; i < myrecyclebinprojects.length; i++){
tp.getRootNode().appendChild(new actioncenter.projectNode({
text: myrecyclebinprojects[i].contributionProperties.projectName.value,
leaf: false,
cls:'x-tree-node-actioncenter',
id: myrecyclebinprojects[i].id,
targetTabPanelId:'workbenchPanel',//the tabpanel that the project will attach its editor tabpanel to
uiProvider: actioncenter.projectNodeUI
}));
}
}
}
},

// tree-specific configs:
rootVisible :false,
singleExpand :true,

root :new Ext.tree.TreeNode()
//allowBlank: false
});

var openRecycleBinProjectWindow = new Ext.Window({
id:'openRecycleBinProjectWindow',
layout: 'form',
title:'Projects available in the recycle bin',
width: 500,
autoHeight: true,
bodyStyle: 'padding:5px',
draggable: true,
hideLabels: true,
resizable: false,
closable:true,
frame: true,
modal: true,
items:openRecycleBinProjectTree,
buttons:[{
text: 'Restore Projects',
handler: function(){
var sm = Ext.getCmp('openRecycleBinProjectTree').getSelectionModel();
var selectedNodes = sm.getSelectedNodes();//get current selected nodes
sm.clearSelections();
if (selectedNodes.length == 0){
Ext.MessageBox.show({
title: 'Interface Assistance',
msg: 'Please select a project to restore',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.WARNING
});
//alert('please select a project to restore');
}else{
var treeRootId = Ext.getCmp('treeExplorer').getRootNode();
var channel = "/response/" + treeRootId.id + "/childof/AC_Project_Definition";
dojox.cometd.startBatch();

for(var i=0; i<selectedNodes.length; i++){
dojox.cometd.publish("/requests/service", {
action: "restoreProjectFromRecycleBin",
user: workspaceUser,
receivingchannel : channel,
id: selectedNodes[i].id
});
var node = Ext.getCmp('openRecycleBinProjectTree').getRootNode().findChild('id',selectedNodes[i].id);
Ext.getCmp('openRecycleBinProjectTree').getRootNode().removeChild(node);
}

dojox.cometd.endBatch();
//Ext.getCmp('treeExplorer').getRootNode().appendChild(selectedNodes);
Ext.getCmp('openRecycleBinProjectWindow').close();
}
}
},{
text: 'Empty Recycle Bin',
handler: function(){
var sm = Ext.getCmp('openRecycleBinProjectTree').getSelectionModel();

var treeRootId = Ext.getCmp('treeExplorer').getRootNode();
var channel = "/response/" + treeRootId.id + "/childof/AC_Project_Definition";
dojox.cometd.startBatch();

dojox.cometd.publish("/requests/service", {
action: "emptyRecycleBin",
user: workspaceUser,
receivingchannel : channel
});
dojox.cometd.endBatch();
//Ext.getCmp('treeExplorer').getRootNode().appendChild(selectedNodes);
Ext.getCmp('openRecycleBinProjectWindow').close();

}
},{
text: 'Cancel',
handler: function(){
Ext.getCmp('openRecycleBinProjectWindow').close();
}
}],
buttonAlign: 'center'
});
openRecycleBinProjectWindow.show();
dojox.cometd.unsubscribe(channel, actionCenterProject, 'openRecycleBinInDesktopDialog');
},


/**************** CLOSING / RECYCLING A PROJECT ***************/

closeProjectInExplorer: function() {
var sm = Ext.getCmp('treeExplorer').getSelectionModel();
var selectedNodes = sm.getSelectedNodes();//get current selected nodes

var treeRootId = Ext.getCmp('treeExplorer').getRootNode();

var channel = "/response/" + treeRootId.id + "/childof/AC_Project_Definition";
dojox.cometd.startBatch();
for(var i=0; i<selectedNodes.length; i++){
var node = selectedNodes[i];
dojox.cometd.publish("/requests/service", {
action : "deleteRelationship",
parentContribution : treeRootId.id,
user: workspaceUser,
receivingchannel : channel,
childContribution: node.id,
relationshipType: "childof"
});
}
dojox.cometd.endBatch();

},

recycleProjectInExplorer: function() {
var sm = Ext.getCmp('treeExplorer').getSelectionModel();
var selectedNodes = sm.getSelectedNodes();//get current selected nodes

var treeRootId = Ext.getCmp('treeExplorer').getRootNode();

var channel = "/noresponse";
dojox.cometd.startBatch();
for(var i=0; i<selectedNodes.length; i++){
var node = selectedNodes[i];
dojox.cometd.publish("/requests/service", {
action : "moveProjectToRecycleBin",
parentContribution : treeRootId.id,
user: workspaceUser,
receivingchannel : channel,
id: node.id,
relationshipType: "childof"
});
}
dojox.cometd.endBatch();

},


/**************** CREATING A PROJECT ***************/

createProject: function() {
var treeRootId = Ext.getCmp('treeExplorer').getRootNode();


var projName = Ext.getCmp('newProjectWindow').findById('projectName').el.dom.value;

actionCenterProject.counter++;

var msg = {
data : {
result: 'success',
contribution: {
id: 'ABC123' + actionCenterProject.counter,
result: 'success',
contributionProperties: {
projectName: {
value: Ext.getCmp('newProjectWindow').findById('projectName').el.dom.value
}
}
}
}
}
Ext.getCmp('newProjectWindow').displayServiceError(msg);
//Ext.getCmp('newProjectWindow').close();

},

createProjectDialog : function() {

var newProjectWindow = new Ext.Window({
id:'newProjectWindow',
layout: 'form',
title:'Create new project',
width: 500,
autoHeight: true,
bodyStyle: 'padding:5px',
draggable: true,
resizable: false,
closable:true,
frame: true,
modal: true,
displayError: function(name,description,url){
if((name != '') && (description != '') && (url != '')){
Ext.getCmp('newProjectWindow').close();
}
else {
//display the error here.
var error = '';
if(name == ''){
error = '<b>The project name cannot be blank.<br/></b>';
}
if(description == ''){
error = error + '<b>The project description cannot be blank.<br/></b>';
}
if(url == ''){
error = error + '<b>The project URL cannot be blank.</b>';
}
Ext.getCmp('projectError').setText(error,false);
Ext.getCmp('projectError').show();
}
},
displayServiceError: function(msg){

if(msg.data.result == 'success'){
Ext.getCmp('treeExplorer').getRootNode().attributes.createChild(msg);

Ext.getCmp('newProjectWindow').close();

}
else {
//display the error here.
var error = 'Populate Error';

Ext.getCmp('projectError').setText(msg.data.errorstring,false);
Ext.getCmp('projectError').show();
}
//var channel = "/response/" + dojox.cometd.clientId + "/addProjectResult";
//dojox.cometd.unsubscribe(channel,Ext.getCmp('newProjectWindow'),'displayServiceError');
},
items:[{
xtype:'label',
id: 'projectError',
hideLabel: true,
cls: 'error-text-style',
anchor: '100%'
},{
fieldLabel:'Project name',
xtype:'textfield',
id: 'projectName',
value: 'Project',
anchor: '100%',
allowBlank: false
},{
fieldLabel:'Description',
xtype:'textarea',
id:'projectDescription',
anchor: '100%',
height: 100
},{
fieldLabel:'URL',
xtype:'textfield',
id:'projectURL',
anchor: '100%'
}],
buttons:[{
text: 'Add',
handler:
function(){
actionCenterProject.createProject();
}
},{
text: 'Cancel',
handler: function(){
Ext.getCmp('newProjectWindow').close();
}
}],
buttonAlign: 'center'
}).show();
},


/**************** GET USER LIST OF A PROJECT ***************/

/*openProjectEditors: function(type,projectid,displayName) {
},*/

getUsers: function(project) {
//var channel = "/response/" + dojox.cometd.clientId + "/getUsers";

//dojox.cometd.startBatch();
//dojox.cometd.subscribe(channel, actionCenterProject, 'getUserList');

//calls a function that gets the next project name
//dojox.cometd.publish("/requests/service", {
// action : "getUsers",
// projectID:project,
// ParentId : '',
// user: workspaceUser,
// receivingchannel : channel,
// id: ''
//});
//dojox.cometd.endBatch();
actionCenterProject.getUserList();
},

getUserList: function(msg) {
//var users = msg.data.allUsers;
//var channel = "/response/" + dojox.cometd.clientId + "/getUsers";

//if(users.length > 0){

for (var i = 0; i < 4; i++){

Ext.getCmp('userListTree').getRootNode().appendChild(new Ext.tree.TreeNode({
text: 'User' + i,
leaf: false,
id: 'User' + i
}));
}
//}
//dojox.cometd.unsubscribe(channel, actionCenterProject, 'getUserList');

},


/**************** GET USER LIST OF A PROJECT ***************/

/*openProjectEditors: function(type,projectid,displayName) {
},*/

getProjectUsers: function(project) {
//var channel = "/response/" + dojox.cometd.clientId + "/getUsersInProject";


actionCenterProject.getProjectUserList();
},

getProjectUserList: function(msg) {
//var projectusers = msg.data.allProjectUsers;
//var channel = "/response/" + dojox.cometd.clientId + "/getUsersInProject";
//if(projectusers.length > 0){
//for (var i = 0; i < projectusers.length; i++){
Ext.getCmp('participantListTree').getRootNode().appendChild(new Ext.tree.TreeNode({
text: 'Admin',
leaf: false,
id: 'Admin'
}));
//}
//}
//dojox.cometd.unsubscribe(channel, actionCenterProject, 'getProjectUserList');

}
};


/*
* END OF FILE - /actioncenters/src/main/webapp/actioncenters/offline/ProjectFunctions.js
*/

/*
* JavaScript file created by Rockstarapps Concatenation
*/

</script>


<script type="text/javascript">
var copiedNodes = new Array();
Ext.onReady( function() {


var explorerRoot;

var baseOfPage = {
buildBaseTreeNode : function(msg) {

explorerRoot = new Ext.tree.TreeNode(
{text: 'rootnode',
leaf: false,
id: '1234567',
createChild : function(msg){
var id = msg.data.contribution.id;
var projectText = msg.data.contribution.contributionProperties.projectName.value;

var existingchild = Ext.getCmp('treeExplorer').getRootNode().findChild("id",id);
if(existingchild == null) {

var projnode = new actioncenter.projectNode({
text:projectText,
id: id,
cls:'x-tree-node-actioncenter',
targetTabPanelId:'workbenchPanel',
uiProvider: actioncenter.projectNodeUI
});

Ext.getCmp('treeExplorer').getRootNode().appendChild(projnode);
projnode.subcribeAllChannels();
}
return;

},
deleteChild : function(msg) {
var childId = msg.data.contribution.id;
var node = Ext.getCmp('treeExplorer').getRootNode().findChild('id',childId);
if(node != null) {
node.unsubscribeAllChannels();
Ext.getCmp('treeExplorer').getRootNode().removeChild(node);
}
var workbenchopen = Ext.getCmp('workbenchPanel').findById(childId + '-tabpanel');
if(workbenchopen != null) {
Ext.getCmp('workbenchPanel').removeAll();
}
return;
},
expanded: true}
);

//dojox.cometd.startBatch();
//dojox.cometd.subscribe("/contributions/relationship/" + explorerRoot.id + "/childof/AC_Project_Definition/add", explorerRoot.attributes, 'createChild');
//dojox.cometd.subscribe("/contributions/relationship/" + explorerRoot.id + "/childof/AC_Project_Definition/delete", explorerRoot.attributes, 'deleteChild');
//dojox.cometd.endBatch();

baseOfPage.buildPageLayout();

},
buildPageLayout : function(msg) {
/**************** Build Page *********************/
var treeExplorer = new Ext.ux.MultiSelectTreePanel({
id: 'treeExplorer',
title :'Full Project View Explorer',
autoScroll :true,
// Control Copy and Paste on the project is disabled until URL renaming and deep copy is completed.
//keys:[{
// key: 'c',
// ctrl:true,
// fn: function(scope, event){
// var sm = Ext.getCmp('treeExplorer').getSelectionModel();
// var selectedNodes = sm.getSelectedNodes();//get current selected node
// if (selectedNodes.length == 0){
// alert('please select a node to copy first');
// }else{
// var i;
// for (i = 0;i<selectedNodes.length;i++){
// copiedNodes[i] = new Ext.tree.TreeNode({
// leaf: false,
// text: selectedNodes[i].text,
// id: selectedNodes[i].text
// });
// }
// sm.clearSelections();
// }
// }
//},{
// key: 'v',
// ctrl:true,
// fn: function(scope, event){
// var sm = Ext.getCmp('treeExplorer').getSelectionModel();
// var selectedNodes = sm.getSelectedNodes();//get current selected node
// if (selectedNodes.length == 0){
// alert('please select a node to paste first');
// }else{
// //alert(selectedNodes.length);
// selectedNodes[0].parentNode.appendChild(copiedNodes);
// }
// }
//}],

// tree-specific configs:
rootVisible :false,
lines :true,
singleExpand :true,
useArrows :false,
root : explorerRoot

} );


var tabPanel = new Ext.TabPanel({
xtype:'tabpanel',
activeTab: 0,
bodyBorder : false,
items:[{
title: '&nbsp;',
iconCls: 'tabs',
layout:'fit',
items : [treeExplorer],
bbar:[{
xtype:'tbtext',
text: '&nbsp;'
},{
xtype:'tbfill'
},{
xtype:'tbbutton',
cls:'x-btn-icon',
tooltip: 'Create a Project',
icon:'images/btn_create-directory_off.gif',
handler: actionCenterProject.createProjectDialog
},{
xtype:'tbbutton',
cls:'x-btn-icon',
tooltip: 'Export a Project',
icon:'images/btn_export-project_off.gif'
},{
xtype:'tbbutton',
cls:'x-btn-icon',
tooltip: 'Delete a Project',
icon:'images/btn_delete_off.gif',
handler: actionCenterProject.recycleProjectInExplorer
}]
}]
});


explorerPanel = new Ext.Panel({
xtype:'panel',
title:'Explorer',
layout:'fit',
id:'explorerPanel',
margins: '0 0 0 0',
region:'west',
split: true,
minWidth : 100,
maxWidth : 300,
hideBorders: true,
collapsible:true,
width: 200,
items:[tabPanel]
});



var workbenchPanel = {// this panel is used to present the layouts chosen
xtype :'panel',
id :'workbenchPanel-parent',
title :'Workbench',
margins: '0 0 0 0',
bodyBorder : false,
layout:'fit',
region :'center',
items:{
xtype: 'tabpanel',
id:'workbenchPanel',
bodyBorder : false
}
}
mainPanel = {
xtype:'panel',
layout:'border',
tbar: [{
xtype:'tbbutton',
text:'File',
menu:[{
text:'Open Project',
handler: actionCenterProject.getAvailableProjects
},
{
text:'Close Project',
handler: actionCenterProject.closeProjectInExplorer
},
{
text:'Delete Project',
handler: actionCenterProject.closeProjectInExplorer
},
{
text:'Open Project Recycle Bin',
handler: actionCenterProject.openRecycleBin
}
]
},{
xtype:'tbbutton',
text:'Edit',
menu:[{
text:'Cut'
},{
text:'Copy'
},{
text:'Paste'
}]
},{
xtype:'tbbutton',
text:'View',
menu:[{
text:'Normal'
},{
text:'Hide All'
},{
text:'Show All'
},{
text:'Hide Palettes'
},{
text:'Hide Properties'
},{
text:'Hide Explorers'
}]
},{
xtype:'tbbutton',
text:'Window'
//menu:[]
},{
xtype:'tbbutton',
text:'Perspective',
menu:[{
text:'Project'
},{
text:'ThinkLets'
}]
},{
xtype:'tbbutton',
text:'Help',
menu:[{
text:'Contextual Help'
},{
text:'General Help'
},{
text:'Contact Us'
},{
text:'Feedback'
},{
text:'About ActionCenters'
}]
},{
xtype:'tbfill'
},
{
xtype:'tbtext',
text:'YOU ARE LOGGED IN AS: <b><sec:authentication property="principal.username"/></b> | <b><a href = "logout.jsp">Log out</a></b> '
}],
items:[explorerPanel, workbenchPanel]
}
new Ext.Viewport( {// the main panel that will render the view to
// the web browser
layout :'fit',
id: 'viewPort',
items : [mainPanel],
renderTo: Ext.getBody()
});
explorerPanel.doLayout();

/*************************************************/
}
}
//var channel = "/response/" + dojox.cometd.clientId + "/getDesktopProjects";
//dojox.cometd.startBatch();
//dojox.cometd.subscribe(channel, baseOfPage, 'buildBaseTreeNode');

//calls a function that gets the next project name
//dojox.cometd.publish("/requests/service", {
// action : "getDesktopProjects",
// ParentId : '',
// id : '',
// user: workspaceUser,
// receivingchannel : channel
//});
//dojox.cometd.endBatch();
baseOfPage.buildBaseTreeNode();





});
</script>

</head>
<body>

</body>
</html>

mjaomaha
18 Jun 2009, 12:53 PM
Deadmeat,

As far as some other options, this project is open source so there are no problems getting you any and all code. We have this project running on a box and I could get you a user id and password if you send me your email at masplin@mail.unomaha.edu. I could also get you a war file. For a quick work around I modified your code to disable the control features.




//if (target.node.isSelected() && e.ctrlKey) {

// selModel.unselect(target.node);

// return;

//}

var selNodes = [];
if (!selModel.getSelectedNodes) {
// if not multiSelectionModel.. just use the target...

selNodes = [target.node];
} else {
// if target not selected select it...

if (!target.node.isSelected() || e.shiftKey) {


if(!e.ctrlKey){
selModel.select(target.node, e, e.ctrlKey);
}

}
// get selected nodes - nested nodes...

selNodes = selModel.getUniqueSelectedNodes();
}

Deadmeat
19 Jun 2009, 2:43 AM
Hi mjaomaha,

I see the problem now.

FixedMultiSelectionModel needs to override onNodeClick. Otherwise it calls the one defined in MultiSelectionModel which calls select, which causes the deselect behaviour you have observed.

Your post version above has this all commented out. Should look roughly like this:



onNodeClick : function(node, e){
if (e.shiftKey) e.preventDefault();
},


Hopefully that doesn't break anything else though.

mjaomaha
19 Jun 2009, 10:33 AM
Thanks, your suggestion got me very close. Actually this ended up being what I needed to make it work. (Maybe I'm still missing something.)




onNodeClick : function(node, e){

if ((e.shiftKey)||(e.ctrlKey)) {
e.preventDefault();
}
else {
this.select(node);
}
},


If I didn't do this, select did not unselect the other nodes I had control clicked.

ravindra1010
15 Jan 2010, 7:52 AM
Hi Deadmeat,

I already posted this question in General Forums, I did not get any replies.
So I am posting this in your thread.

I am using Ext 3.0 with your MultiSelectTree-1.1.js.
I have 2 MultiSelectTrees on a screen each getting their data from the server in separate calls. I have couple of drop down selection boxes on the screen.
When user changes drop down selections on the screen, these 2 trees are updated with the data from the server. Some times, the data supposed to be displayed as left side tree is displayed on the right side tree and vice-versa.

Following is my code which creates 2 MultiSelectTreePanels


Ext.onReady(function(){
var onetree = new Ext.ux.MultiSelectTreePanel({
title: 'Available Families and Features',
id: 'leftTree',
autoScroll:true,
renderTo: 'example3_one',
animate:true,
containerScroll: true,
enableDD: true,
ddGroup: 'twotrees',
dropConfig: {
ddGroup: 'twotrees',
allowContainerDrop: false
},
root: new Ext.tree.AsyncTreeNode({
text: 'Available Families and Features',
draggable:false,
id:'node0',
expanded:true
}),



loader: new Ext.tree.TreeLoader({
dataUrl:'StdRptConfig.do?usrAction=getLeftTreeData'
})
});

new Ext.tree.TreeSorter(onetree, {folderSort:true});

var twotree = new Ext.ux.MultiSelectTreePanel({
title: 'Excluded Families and Included Market',
id: 'rightTree',
autoScroll:true,
renderTo: 'example3_two',
animate:true,
containerScroll: true,
enableDD: true,
ddGroup: 'twotrees',
dropConfig: {
ddGroup: 'twotrees',
allowContainerDrop: false
},
root: new Ext.tree.AsyncTreeNode({
text: 'Excluded Families and Included Market',
draggable:false,
leaf: false,
id:'node0',
expanded:true
}),



loader: new Ext.tree.TreeLoader({
dataUrl:'StdRptConfig.do?usrAction=getRightTreeData'
})
});

new Ext.tree.TreeSorter(twotree, {folderSort:true});
}


Server stores the data for left tree in a session variable leftJsonObject and for right tree in rightJsonObject variable. Above TreeLoader invokes 'StdRptConfig action class which forwards the call to corresponding jsp based on usrAction query parameter passed in the dataUrl above, JSP has following code to return data from session variable to response.

leftJson.jsp
<%

response.setContentType("application/json");

out.print(session.getAttribute("leftJsonObject"));
session.removeAttribute("leftJsonObject");
%>

rightJson.jsp
<%

response.setContentType("application/json");
out.print(session.getAttribute("rightJsonObject"));
session.removeAttribute("rightJsonObject");
%>

Could you please guide me on why this data is getting exchanged between 2 trees ?

Deadmeat
16 Jan 2010, 2:37 AM
Could you please guide me on why this data is getting exchanged between 2 trees ?


I honestly have no idea.

I can't think of anything in the MultiSelectTree code that would cause that. The loader code should be largely unchanged from the default tree. I'm not even sure where to start looking.

Is it specific to the MutliSelect version, or can you produce the same bug using default TreePanels. Is the bug browser specific, or cross browser?

Perhaps I've mistakenly overwritten a global variable somewhere. I'm not even sure how I would track this down though.

I would probably start looking at the way in which AJAX calls are bound to the TreeLoader/TreePanel objects. You can subclass the TreeLoader an insert some trace statements. Ensure the right request is feeding nodes to the right Tree.

Perhaps there is a race condition which affects this works. Can you trigger the bug reliably by introducing a delay in either the left or right ajax response by calling Thread.sleep(5000); in the JSP code.

I can't reproduce this bug on OSX, making me wonder if it's browser specific AJAX handling.

I'm interested in more information.

kavih7
6 Feb 2010, 3:24 PM
Hi deadmeat,

I noticed in your source, that you removed selections from happening in the beforeInvalidDrop method. This is exactly the solution I need and I'm just wondering why this was never officially put into the Ext library. I'm using ExtJS 3.1 and I know this thread is for ExtJS 2.x, but I thought that if this extension would be part of 3.1, that the Ext team would have taken into consideration all your "fixes." Also note that I am not using and have never played with multiselection, just single selection.

Your thoughts?

Deadmeat
6 Feb 2010, 11:38 PM
Hi deadmeat,

I noticed in your source, that you removed selections from happening in the beforeInvalidDrop method. This is exactly the solution I need and I'm just wondering why this was never officially put into the Ext library. I'm using ExtJS 3.1 and I know this thread is for ExtJS 2.x, but I thought that if this extension would be part of 3.1, that the Ext team would have taken into consideration all your "fixes." Also note that I am not using and have never played with multiselection, just single selection.

Your thoughts?

You can use overrides (Ext.override) to replace code in the default components, instead of using an extension.

I have had some fixes included (allowContainerDrop and an ordering bug) but my impression is that the tree component has not been a priority in recent releases. Apart from bug fixes, it has not improved significantly since 2.0. I felt at the time they were wary of changing the tree too much.

There is apparently a revision in the works (mentioned in the 3.1 release post) which indicated that trees would be able to use Ext.data.Store (and probably the Ext Direct framework) in 3.2. This could mean a significant upgrade to the tree component.

I would love to have the functionality of this extension included in the base version. I think it fills a significant gap in the tree functionality. It could be based on my code, which is highly derivative of the original code. An issue for me is that I do not really have the time to maintain an extension properly. However ExtJS development is not open to outsiders, and I don't know who to appeal to. I'm waiting to see what 3.2 brings. I'm assuming my extension will break.

Even if this functionality was included, I did have a short plan of what else I was going to add to it. I was interested in the functionality behind saki's remote tree. Hooks for easily switching between copy and move operations. Simple nodeui templates. Fixes for the drag cancel flickering, and editable node bugs.

Longer term I believe the entire tree component could benefit from some restructuring. I have some ideas, but no time to work on a ground up rewrite. Again I'm also wary of writing a lot of code which I'll have to support. It would be better for me if there was a way to get external improvements into the base release.

If you have any suggestions I'm open to them.