3 // Oct 2006, rewritten Jan 2008
4 // email: dowdybrown@yahoo.com
5 // leafIcon logic added by Marco Catunda
6 // Requires prototype.js and ricoCommon.js
8 // Data for the tree is obtained via AJAX requests
9 // Each record in the AJAX response should contain 5 or 6 cells:
10 // cells[0]=parent node id
12 // cells[2]=description
13 // cells[3]=L/zero (leaf), C/non-zero (container)
14 // cells[4]=0->not selectable, 1->selectable (use default action), otherwise the node is selectable and cells[4] contains the action
15 // cells[5]=leafIcon (optional)
18 Rico.TreeControl = Class.create(
19 /** @lends Rico.TreeControl# */
22 * @class Implements a pop-up tree control.
25 * @param id unique identifier
26 * @param url data source
27 * @param options object may contain any of the following:<dl>
28 * <dt>nodeIdDisplay</dt><dd> first, last, tooltip, or none? default=none</dd>
29 * <dt>showCheckBox </dt><dd> show checkbox next to each item? default=false</dd>
30 * <dt>showFolders </dt><dd> show folder icons? default=false</dd>
31 * <dt>showPlusMinus</dt><dd> show +/- icons to open/close branches? default=true</dd>
32 * <dt>showLines </dt><dd> show vertical lines connecting each level? default=true</dd>
33 * <dt>defaultAction</dt><dd> function to call when user clicks on an item, default is to call returnValue method</dd>
34 * <dt>height </dt><dd> control height? default=300px</dd>
35 * <dt>width </dt><dd> control width? default=300px</dd>
36 * <dt>leafIcon </dt><dd> url to img, default=doc.gif ('none'=no leaf icon)</dd>
39 initialize: function(id,url,options) {
40 Object.extend(this, new Rico.Popup({ignoreClicks:true}));
41 Object.extend(this.options, {
47 defaultAction: this.nodeClick.bindAsEventListener(this),
50 leafIcon: Rico.imgDir+'doc.gif'
52 Object.extend(this.options, options || {});
55 this.close=this.closePopup;
59 var imgsrc = ["node.gif","nodelast.gif","folderopen.gif","folderclosed.gif"];
61 for (var i=0;i<imgsrc.length;i++) {
63 img.src = Rico.imgDir+imgsrc[i];
65 this.treeDiv=document.createElement("div");
66 this.treeDiv.id=this.id;
67 this.treeDiv.className='ricoTree';
68 this.treeDiv.style.height=this.options.height;
69 this.treeDiv.style.width=this.options.width;
70 this.container=document.createElement("div");
71 this.container.style.display="none";
72 this.container.className='ricoTreeContainer';
73 this.container.appendChild(this.treeDiv);
74 document.body.appendChild(this.container);
75 if (this.options.showCheckBox) {
76 this.buttonDiv=document.createElement("div");
77 this.buttonDiv.style.width=this.options.width;
78 this.buttonDiv.className='ricoTreeButtons';
79 if (Element.getStyle(this.container,'position')=='absolute') {
80 var span=document.createElement("span");
81 span.innerHTML=RicoTranslate.getPhraseById('treeSave');
82 Element.setStyle(span,{'float':'left',cursor:'pointer'});
83 this.buttonDiv.appendChild(span);
84 Event.observe(span,'click',this.saveSelection.bindAsEventListener(this));
86 var span=document.createElement("span");
87 span.innerHTML=RicoTranslate.getPhraseById('treeClear');
88 Element.setStyle(span,{'float':'right',cursor:'pointer'});
89 this.buttonDiv.appendChild(span);
90 this.container.appendChild(this.buttonDiv);
91 Event.observe(span,'click',this.clrCheckBoxEvent.bindAsEventListener(this));
93 this.setDiv(this.container);
97 setTreeDiv: function(divId) {
98 this.treeDiv = $(divId);
99 this.openPopup = function() {};
104 if (this.treeDiv.childNodes.length == 0 && this.dataSource) {
109 loadXMLDoc: function(branchPin) {
110 var parms="id="+this.id;
112 parms+="&Parent="+branchPin;
114 Rico.writeDebugMsg('Tree loadXMLDoc:\n'+parms+'\n'+this.dataSource);
115 var request=new Ajax.Request(this.dataSource, {parameters:parms,method:'get',onComplete:this.processResponse.bind(this)});
118 domID: function(nodeID,part) {
119 return 'RicoTree_'+part+'_'+this.id+'_'+nodeID;
122 processResponse: function(request) {
123 var response = request.responseXML.getElementsByTagName("ajax-response");
124 if (response == null || response.length != 1) return;
125 var rowsElement = response[0].getElementsByTagName('rows')[0];
126 var trs = rowsElement.getElementsByTagName("tr");
128 for (var i=0; i < trs.length; i++) {
129 var cells = trs[i].getElementsByTagName("td");
130 if (cells.length < 5) continue;
132 content[5]=this.options.leafIcon;
133 for (var j=0; j<cells.length; j++) {
134 content[j]=RicoUtil.getContentAsString(cells[j],true);
136 content[3] = content[3].match(/^0|L$/i) ? 0 : 1;
137 content[4] = parseInt(content[4]);
138 rowdata.push(content);
140 for (var i=0; i < rowdata.length; i++) {
141 var moreChildren=(i < rowdata.length-1) && (rowdata[i][0]==rowdata[i+1][0]);
142 this.addNode(rowdata[i][0],rowdata[i][1],rowdata[i][2],rowdata[i][3],rowdata[i][4],rowdata[i][5],!moreChildren);
146 DisplayImages: function(row,arNames) {
148 for(i=0;i<arNames.length;i++) {
149 img = document.createElement("img");
150 img.src=Rico.imgDir+arNames[i] + ".gif";
151 td=row.insertCell(-1);
156 addNode: function(parentId, nodeId, nodeDesc, isContainer, isSelectable, leafIcon, isLast) {
157 var parentNode=$(this.domID(parentId,'Parent'));
158 var parentChildren=$(this.domID(parentId,'Children'));
159 var level=parentNode ? parentNode.TreeLevel+1 : 0;
160 //alert("addNode at level " + level + " (" + nodeId + ")")
161 var tab = document.createElement("table");
162 var div = document.createElement("div");
163 div.id=this.domID(nodeId,'Children');
164 div.className='ricoTreeBranch';
165 div.style.display=parentNode ? 'none' : '';
169 tab.id=this.domID(nodeId,'Parent');
171 tab.TreeContainer=isContainer;
172 tab.TreeFetchedChildren=this.dataSource ? false : true;
173 var row=tab.insertRow(0);
175 for (var i=0; i<level-1; i++) {
176 td[i]=row.insertCell(-1);
179 var tdParent=parentNode.getElementsByTagName('td');
180 for (var i=0; i<level-2; i++) {
181 td[i].innerHTML=tdParent[i].innerHTML;
183 var img = document.createElement("img");
184 img.src=Rico.imgDir+(parentChildren.nextSibling && this.options.showLines ? "nodeline" : "nodeblank")+".gif";
185 td[level-2].appendChild(img);
188 var suffix=isLast && this.options.showLines ? 'last' : '';
189 var prefix=this.options.showLines ? 'node' : '';
190 if (this.options.showPlusMinus && isContainer) {
191 var img = document.createElement("img");
193 img.style.cursor='pointer';
194 img.onclick=this.clickBranch.bindAsEventListener(this);
195 img.src=Rico.imgDir+prefix+"p"+suffix+".gif";
196 row.insertCell(-1).appendChild(img);
197 } else if (this.options.showLines) {
198 var img = document.createElement("img");
199 img.src=Rico.imgDir+"node"+suffix+".gif";
200 row.insertCell(-1).appendChild(img);
202 if (this.options.showFolders && (isContainer || (leafIcon && leafIcon!='none'))) {
203 var img = document.createElement("img");
208 img.style.cursor='pointer';
209 img.onclick=this.clickBranch.bindAsEventListener(this);
210 img.src=Rico.imgDir+"folderclosed.gif";
212 row.insertCell(-1).appendChild(img);
215 if (isSelectable && this.options.showCheckBox) {
216 var chkbx=document.createElement("input");
217 chkbx.type="checkbox";
219 row.insertCell(-1).appendChild(chkbx);
222 if (isSelectable && !this.options.showCheckBox) {
223 var span=document.createElement('a');
224 if (typeof isSelectable=='string') {
225 span.href=isSelectable;
227 span.href='javascript:void(0)';
228 span.onclick=this.options.defaultAction;
231 var span=document.createElement('p');
233 span.id=this.domID(nodeId,'Desc');
234 span.className='ricoTreeLevel'+level;
235 switch (this.options.nodeIdDisplay) {
236 case 'last': nodeDesc+=' ('+nodeId+')'; break;
237 case 'first': nodeDesc=nodeId+' - '+nodeDesc; break;
238 case 'tooltip': span.title=nodeId; break;
240 span.appendChild(document.createTextNode(nodeDesc));
241 row.insertCell(-1).appendChild(span);
243 var parent=parentChildren || this.treeDiv;
244 parent.appendChild(tab);
245 parent.appendChild(div);
248 nodeClick: function(e) {
249 var node=Event.element(e);
250 if (this.returnValue) {
251 var t=this.domID('','Desc');
252 this.returnValue(node.id.substr(t.length),node.innerHTML);
257 saveSelection: function(e) {
258 if (this.returnValue) {
259 this.returnValue(this.getCheckedItems());
264 getCheckedItems: function() {
265 var inp=this.treeDiv.getElementsByTagName('input');
267 for (var i=0; i<inp.length; i++) {
268 if (inp[i].type=='checkbox' && inp[i].checked) {
269 vals.push(inp[i].value);
275 setCheckBoxes: function(val) {
276 var inp=this.treeDiv.getElementsByTagName('input');
277 for (var i=0; i<inp.length; i++) {
278 if (inp[i].type=='checkbox') {
284 clrCheckBoxEvent: function(e) {
286 this.setCheckBoxes(false);
289 clickBranch: function(e) {
290 var node=Event.element(e);
291 var tab=RicoUtil.getParentByTagName(node,'table');
292 if (!tab || !tab.TreeContainer) return;
293 var a=tab.id.split('_');
295 var childDiv=$(a.join('_'));
296 Element.toggle(childDiv);
297 if (node.tagName=='IMG') {
298 var v=Element.visible(childDiv);
299 if (node.src.match(/node(p|m)(last)?\.gif$/)) {
300 node.src=node.src.replace(/nodep|nodem/,'node'+(v ? 'm' : 'p'));
301 } else if (node.src.match(/folder(open|closed)\.gif$/)) {
302 node.src=node.src.replace(/folder(open|closed)/,'folder'+(v ? 'open' : 'closed'));
303 } else if (node.src.match(/\b(m|p)\.gif$/)) {
304 node.src=node.src.replace(/(p|m)\.gif/,v ? 'm\.gif' : 'p\.gif');
307 if (!tab.TreeFetchedChildren) {
308 tab.TreeFetchedChildren=1;
309 this.loadXMLDoc(node.name);
315 Rico.includeLoaded('ricoTree.js');