2 * (c) 2005-2009 Richard Cowin (http://openrico.org)
3 * (c) 2005-2009 Matt Brown (http://dowdybrown.com)
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software distributed under the
11 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12 * either express or implied. See the License for the specific language governing permissions
13 * and limitations under the License.
17 // Requires prototype.js and ricoCommon.js
18 // Data for the tree can be obtained via AJAX requests
19 // Each record in the AJAX response should contain 5 or 6 cells:
20 // cells[0]=parent node id
22 // cells[2]=description
23 // cells[3]=L/zero (leaf), C/non-zero (container)
24 // cells[4]=0->not selectable, 1->selectable (use default action), otherwise the node is selectable and cells[4] contains the action
25 // cells[5]=leafIcon (optional)
28 Rico.TreeControl = function(id,url,options) {
29 this.initialize(id,url,options);
32 Rico.TreeControl.prototype = {
34 * @class Implements a pop-up tree control.
37 * @param id unique identifier
38 * @param url data source
39 * @param options object may contain any of the following:<dl>
40 * <dt>nodeIdDisplay</dt><dd> first, last, tooltip, or none? default=none</dd>
41 * <dt>showCheckBox </dt><dd> show checkbox next to each item? default=false</dd>
42 * <dt>showFolders </dt><dd> show folder icons? default=false</dd>
43 * <dt>showPlusMinus</dt><dd> show +/- icons to open/close branches? default=true</dd>
44 * <dt>showLines </dt><dd> show vertical lines connecting each level? default=true</dd>
45 * <dt>defaultAction</dt><dd> Rico event handle to call when user clicks on an item, default is to call returnValue method</dd>
46 * <dt>height </dt><dd> control height? default=300px</dd>
47 * <dt>width </dt><dd> control width? default=300px</dd>
48 * <dt>leafIcon </dt><dd> url to img, default=doc.gif ('none'=no leaf icon)</dd>
51 initialize: function(id,url,options) {
52 Rico.extend(this, new Rico.Popup());
53 Rico.extend(this.options, {
60 defaultAction: Rico.eventHandle(this,'nodeClick'),
63 leafIcon: Rico.imgDir+'doc.gif'
65 Rico.extend(this.options, options || {});
68 this.close=this.closePopup;
69 this.hoverSet = new Rico.HoverSet([]);
73 var imgsrc = ["node.gif","nodelast.gif","folderopen.gif","folderclosed.gif"];
75 for (var i=0;i<imgsrc.length;i++) {
77 img.src = Rico.imgDir+imgsrc[i];
79 this.treeDiv=document.createElement("div");
80 this.treeDiv.id=this.id;
81 this.treeDiv.className='ricoTree';
82 if (Rico.theme.treeContent) Rico.addClass(this.treeDiv,Rico.theme.treeContent);
83 this.treeDiv.style.height=this.options.height;
84 this.treeDiv.style.width=this.options.width;
85 this.createContainer();
86 this.content.className=Rico.theme.tree || 'ricoTreeContainer';
87 this.content.appendChild(this.treeDiv);
88 if (this.options.showCheckBox) {
89 this.buttonDiv=document.createElement("div");
90 this.buttonDiv.style.width=this.options.width;
91 this.buttonDiv.className='ricoTreeButtons';
92 if (Rico.getStyle(this.container,'position')=='absolute') {
93 var span=document.createElement("span");
94 span.innerHTML=RicoTranslate.getPhraseById('treeSave');
95 Rico.setStyle(span,{'float':'left',cursor:'pointer'});
96 this.buttonDiv.appendChild(span);
97 Rico.eventBind(span, 'click', Rico.eventHandle(this,'saveSelection'));
99 var span=document.createElement("span");
100 span.innerHTML=RicoTranslate.getPhraseById('treeClear');
101 Rico.setStyle(span,{'float':'right',cursor:'pointer'});
102 this.buttonDiv.appendChild(span);
103 this.content.appendChild(this.buttonDiv);
104 Rico.eventBind(span, 'click', Rico.eventHandle(this,'clrCheckBoxEvent'));
109 setTreeDiv: function(divId) {
110 this.treeDiv = Rico.$(divId);
111 this.openPopup = function() {};
116 if (this.treeDiv.childNodes.length == 0 && this.dataSource) {
121 loadXMLDoc: function(branchPin) {
122 var parms = { id: this.id };
123 if (branchPin) parms.Parent=branchPin;
124 Rico.log('Tree loadXMLDoc: '+this.id);
125 new Rico.ajaxRequest(this.dataSource, {parameters:parms,method:'get',onComplete:Rico.bind(this,'processResponse')});
128 domID: function(nodeID,part) {
129 return 'RicoTree_'+part+'_'+this.id+'_'+nodeID;
132 processResponse: function(request) {
133 var response = request.responseXML.getElementsByTagName("ajax-response");
134 if (response == null || response.length != 1) return;
135 var rowsElement = response[0].getElementsByTagName('rows')[0];
136 var trs = rowsElement.getElementsByTagName("tr");
138 for (var i=0; i < trs.length; i++) {
139 var cells = trs[i].getElementsByTagName("td");
140 if (cells.length < 5) continue;
142 content[5]=this.options.leafIcon;
143 for (var j=0; j<cells.length; j++) {
144 content[j]=Rico.getContentAsString(cells[j],true);
146 content[3] = content[3].match(/^0|L$/i) ? 0 : 1;
147 content[4] = parseInt(content[4]);
148 rowdata.push(content);
150 for (var i=0; i < rowdata.length; i++) {
151 var moreChildren=(i < rowdata.length-1) && (rowdata[i][0]==rowdata[i+1][0]);
152 this.addNode(rowdata[i][0],rowdata[i][1],rowdata[i][2],rowdata[i][3],rowdata[i][4],rowdata[i][5],!moreChildren);
156 DisplayImages: function(row,arNames) {
158 for(i=0;i<arNames.length;i++) {
159 img = document.createElement("img");
160 img.src=Rico.imgDir+arNames[i] + ".gif";
161 td=row.insertCell(-1);
166 addNode: function(parentId, nodeId, nodeDesc, isContainer, isSelectable, leafIcon, isLast) {
167 var parentNode=Rico.$(this.domID(parentId,'Parent'));
168 var parentChildren=Rico.$(this.domID(parentId,'Children'));
169 var level=parentNode ? parentNode.TreeLevel+1 : 0;
170 //alert("addNode at level " + level + " (" + nodeId + ")")
171 var tab = document.createElement("table");
172 var div = document.createElement("div");
173 div.id=this.domID(nodeId,'Children');
174 div.className='ricoTreeBranch';
175 div.style.display=parentNode ? 'none' : '';
179 tab.id=this.domID(nodeId,'Parent');
181 tab.TreeContainer=isContainer;
182 tab.TreeFetchedChildren=this.dataSource ? false : true;
183 var row=tab.insertRow(0);
185 for (var i=0; i<level-1; i++) {
186 td[i]=row.insertCell(-1);
189 var tdParent=parentNode.getElementsByTagName('td');
190 for (var i=0; i<level-2; i++) {
191 td[i].innerHTML=tdParent[i].innerHTML;
193 var img = document.createElement("img");
194 img.src=Rico.imgDir+(parentChildren.nextSibling && this.options.showLines ? "nodeline" : "nodeblank")+".gif";
195 td[level-2].appendChild(img);
198 var suffix=isLast && this.options.showLines ? 'last' : '';
199 var prefix=this.options.showLines ? 'node' : '';
200 if (this.options.showPlusMinus && isContainer) {
201 var img = document.createElement("img");
203 img.style.cursor='pointer';
204 Rico.eventBind(img, 'click', Rico.eventHandle(this,'clickBranch'));
205 img.src=Rico.imgDir+prefix+"p"+suffix+".gif";
206 row.insertCell(-1).appendChild(img);
207 } else if (this.options.showLines) {
208 var img = document.createElement("img");
209 img.src=Rico.imgDir+"node"+suffix+".gif";
210 row.insertCell(-1).appendChild(img);
212 if (this.options.showFolders && (isContainer || (leafIcon && leafIcon!='none'))) {
213 var img = document.createElement("img");
218 img.style.cursor='pointer';
219 Rico.eventBind(img, 'click', Rico.eventHandle(this,'clickBranch'));
220 img.src=Rico.imgDir+"folderclosed.gif";
222 row.insertCell(-1).appendChild(img);
225 if (isSelectable && this.options.showCheckBox) {
226 var chkbx=document.createElement("input");
227 chkbx.type="checkbox";
229 row.insertCell(-1).appendChild(chkbx);
232 if (isSelectable && !this.options.showCheckBox) {
233 var span=document.createElement('a');
234 if (typeof isSelectable=='string') {
235 span.href=isSelectable;
237 span.href='javascript:void(0)';
238 Rico.eventBind(span, 'click', this.options.defaultAction);
240 this.hoverSet.add(span);
242 var span=document.createElement('p');
244 span.id=this.domID(nodeId,'Desc');
245 span.className='ricoTreeLevel'+level;
246 switch (this.options.nodeIdDisplay) {
247 case 'last': nodeDesc+=' ('+nodeId+')'; break;
248 case 'first': nodeDesc=nodeId+' - '+nodeDesc; break;
249 case 'tooltip': span.title=nodeId; break;
251 span.appendChild(document.createTextNode(nodeDesc));
252 row.insertCell(-1).appendChild(span);
254 var parent=parentChildren || this.treeDiv;
255 parent.appendChild(tab);
256 parent.appendChild(div);
259 nodeClick: function(e) {
260 var node=Rico.eventElement(e);
261 if (this.returnValue) {
262 var t=this.domID('','Desc');
263 this.returnValue(node.id.substr(t.length),node.innerHTML);
268 saveSelection: function(e) {
269 if (this.returnValue) {
270 this.returnValue(this.getCheckedItems());
275 getCheckedItems: function() {
276 var inp=this.treeDiv.getElementsByTagName('input');
278 for (var i=0; i<inp.length; i++) {
279 if (inp[i].type=='checkbox' && inp[i].checked) {
280 vals.push(inp[i].value);
286 setCheckBoxes: function(val) {
287 var inp=this.treeDiv.getElementsByTagName('input');
288 for (var i=0; i<inp.length; i++) {
289 if (inp[i].type=='checkbox') {
295 clrCheckBoxEvent: function(e) {
297 this.setCheckBoxes(false);
300 clickBranch: function(e) {
301 var node=Rico.eventElement(e);
302 var tab=Rico.getParentByTagName(node,'table');
303 if (!tab || !tab.TreeContainer) return;
304 var a=tab.id.split('_');
306 var childDiv=Rico.$(a.join('_'));
307 Rico.toggle(childDiv);
308 if (node.tagName=='IMG') {
309 var v=Rico.visible(childDiv);
310 if (node.src.match(/node(p|m)(last)?\.gif$/)) {
311 node.src=node.src.replace(/nodep|nodem/,'node'+(v ? 'm' : 'p'));
312 } else if (node.src.match(/folder(open|closed)\.gif$/)) {
313 node.src=node.src.replace(/folder(open|closed)/,'folder'+(v ? 'open' : 'closed'));
314 } else if (node.src.match(/\b(m|p)\.gif$/)) {
315 node.src=node.src.replace(/(p|m)\.gif/,v ? 'm\.gif' : 'p\.gif');
318 if (!tab.TreeFetchedChildren) {
319 tab.TreeFetchedChildren=1;
320 this.loadXMLDoc(node.name);
326 Rico.includeLoaded('ricoTree.js');