Loading rico1 and rico3 files
[infodrom/rico3] / ricoClient / js / ricoTree.js
1 /*
2  *  (c) 2005-2009 Richard Cowin (http://openrico.org)
3  *  (c) 2005-2009 Matt Brown (http://dowdybrown.com)
4  *
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
7  *
8  *         http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
14  */
15
16 //  Rico Tree Control
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
21 //   cells[1]=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)
26
27
28 Rico.TreeControl = function(id,url,options) {
29   this.initialize(id,url,options);
30 };
31
32 Rico.TreeControl.prototype = {
33 /**
34  * @class Implements a pop-up tree control.
35  * @extends Rico.Popup
36  * @constructs
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>
49  *</dl>
50  */
51   initialize: function(id,url,options) {
52     Rico.extend(this, new Rico.Popup());
53     Rico.extend(this.options, {
54       ignoreClicks:true,
55       nodeIdDisplay:'none',
56       showCheckBox: false,
57       showFolders: false,
58       showPlusMinus: true,
59       showLines: true,
60       defaultAction: Rico.eventHandle(this,'nodeClick'),
61       height: '300px',
62       width: '300px',
63       leafIcon: Rico.imgDir+'doc.gif'
64     });
65     Rico.extend(this.options, options || {});
66     this.id=id;
67     this.dataSource=url;
68     this.close=this.closePopup;
69     this.hoverSet = new Rico.HoverSet([]);
70   },
71
72   atLoad : function() {
73     var imgsrc = ["node.gif","nodelast.gif","folderopen.gif","folderclosed.gif"];
74     // preload images
75     for (var i=0;i<imgsrc.length;i++) {
76       var img=new Image();
77       img.src = Rico.imgDir+imgsrc[i];
78     }
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'));
98       }
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'));
105     }
106     this.close();
107   },
108
109   setTreeDiv: function(divId) {
110     this.treeDiv = Rico.$(divId);
111     this.openPopup = function() {};
112   },
113
114   open: function() {
115     this.openPopup();
116     if (this.treeDiv.childNodes.length == 0 && this.dataSource) {
117       this.loadXMLDoc();
118     }
119   },
120
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')});
126   },
127
128   domID: function(nodeID,part) {
129     return 'RicoTree_'+part+'_'+this.id+'_'+nodeID;
130   },
131
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");
137     var rowdata=[];
138     for (var i=0; i < trs.length; i++) {
139       var cells = trs[i].getElementsByTagName("td");
140       if (cells.length < 5) continue;
141       var content=[];
142       content[5]=this.options.leafIcon;
143       for (var j=0; j<cells.length; j++) {
144         content[j]=Rico.getContentAsString(cells[j],true);
145       }
146       content[3] = content[3].match(/^0|L$/i) ? 0 : 1;
147       content[4] = parseInt(content[4]);
148       rowdata.push(content);
149     }
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);
153     }
154   },
155
156   DisplayImages: function(row,arNames) {
157     var i,img,td;
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);
162       td.appendChild(img);
163     }
164   },
165
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' : '';
176     tab.border=0;
177     tab.cellSpacing=0;
178     tab.cellPadding=0;
179     tab.id=this.domID(nodeId,'Parent');
180     tab.TreeLevel=level;
181     tab.TreeContainer=isContainer;
182     tab.TreeFetchedChildren=this.dataSource ? false : true;
183     var row=tab.insertRow(0);
184     var td=[];
185     for (var i=0; i<level-1; i++) {
186       td[i]=row.insertCell(-1);
187     }
188     if (level>1) {
189       var tdParent=parentNode.getElementsByTagName('td');
190       for (var i=0; i<level-2; i++) {
191         td[i].innerHTML=tdParent[i].innerHTML;
192       }
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);
196     }
197     if (level>0) {
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");
202         img.name=nodeId;
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);
211       }
212       if (this.options.showFolders && (isContainer || (leafIcon && leafIcon!='none'))) {
213         var img = document.createElement("img");
214         if (!isContainer) {
215           img.src=leafIcon;
216         } else {
217           img.name=nodeId;
218           img.style.cursor='pointer';
219           Rico.eventBind(img, 'click', Rico.eventHandle(this,'clickBranch'));
220           img.src=Rico.imgDir+"folderclosed.gif";
221         }
222         row.insertCell(-1).appendChild(img);
223       }
224     }
225     if (isSelectable && this.options.showCheckBox) {
226       var chkbx=document.createElement("input");
227       chkbx.type="checkbox";
228       chkbx.value=nodeId;
229       row.insertCell(-1).appendChild(chkbx);
230     }
231
232     if (isSelectable && !this.options.showCheckBox) {
233       var span=document.createElement('a');
234       if (typeof isSelectable=='string') {
235         span.href=isSelectable;
236       } else {
237         span.href='javascript:void(0)';
238         Rico.eventBind(span, 'click', this.options.defaultAction);
239       }
240       this.hoverSet.add(span);
241     } else {
242       var span=document.createElement('p');
243     }
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;
250     }
251         span.appendChild(document.createTextNode(nodeDesc));
252     row.insertCell(-1).appendChild(span);
253
254     var parent=parentChildren || this.treeDiv;
255     parent.appendChild(tab);
256     parent.appendChild(div);
257   },
258
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);
264     }
265     this.close();
266   },
267
268   saveSelection: function(e) {
269     if (this.returnValue) {
270       this.returnValue(this.getCheckedItems());
271     }
272     this.close();
273   },
274
275   getCheckedItems: function() {
276     var inp=this.treeDiv.getElementsByTagName('input');
277     var vals=[];
278     for (var i=0; i<inp.length; i++) {
279       if (inp[i].type=='checkbox' && inp[i].checked) {
280         vals.push(inp[i].value);
281       }
282     }
283     return vals;
284   },
285
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') {
290         inp[i].checked=val;
291       }
292     }
293   },
294
295   clrCheckBoxEvent: function(e) {
296     Rico.eventStop(e);
297     this.setCheckBoxes(false);
298   },
299
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('_');
305     a[1]='Children';
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');
316       }
317     }
318     if (!tab.TreeFetchedChildren) {
319       tab.TreeFetchedChildren=1;
320       this.loadXMLDoc(node.name);
321     }
322   }
323
324 };
325
326 Rico.includeLoaded('ricoTree.js');