alternative to assert
[gtkD.git] / gtkD / docs / candydoc / tree.js
blobe2d6dd7cc36adc8f41e0168178318391e7d4aeef
1 /* This file is a part of CanDyDOC fileset.
2    File is written by Victor Nakoryakov and placed into the public domain.
4    This file is javascript with classes that represents native style tree control. */\r
5    \r
6 var pmNone = 0;\r
7 var pmPlus = 1;\r
8 var pmMinus = 2;\r
9 \r
10 var hlNone = 0;\r
11 var hlGrey = 1;\r
12 var hlSelected = 2;\r
14 function TreeView(hrefMode)\r
15 {\r
16     this.domEntry = document.createElement("div");\r
17     this.children = new Array();\r
18     this.selection = null;\r
19     this.hrefMode = hrefMode;\r
20     \r
21     this.createBranch = function(text, iconSrc)\r
22     {\r
23         var root = new TreeNode(text, iconSrc, this.hrefMode);\r
24         root.owner = this;\r
25         this.children[ this.children.length ] = root;\r
26         this.domEntry.appendChild( root.domEntry );\r
27         return root;\r
28     }\r
29     \r
30     this.branch = function(text)\r
31     {\r
32         var ret = null;\r
33         for (var i = 0; i < this.children.length; ++i)\r
34             if (this.children[i].textElement.data == text)\r
35             {\r
36                 ret = this.children[i];\r
37                 break;\r
38             }\r
39             \r
40         return ret;\r
41     }\r
42     \r
43     this.domEntry.style.fontSize = "10px";\r
44     this.domEntry.style.cursor = "default";\r
45     this.domEntry.style.whiteSpace = "nowrap";\r
46 }\r
48 var idCounter = 0;\r
49 function TreeNode(text, iconSrc, hrefMode)\r
50 {\r
51     this.id             = idCounter++;\r
52     this.parentNode     = null;\r
53     this.children       = new Array();\r
54     this.domEntry       = document.createElement("div");\r
55     this.icon           = document.createElement("img");\r
56     this.textElement    = document.createTextNode(text);\r
57     this.textSpan       = document.createElement("span");\r
58     this.lineDiv        = document.createElement("div");\r
59     this.hierarchyImgs  = new Array();\r
60     this.onclick        = null;\r
61     \r
62     function createIcon()\r
63     {\r
64         var img = document.createElement("img");\r
65         img.style.verticalAlign = "middle";\r
66         img.style.position = "relative";\r
67         img.style.top = "-1px";\r
68         img.width = 16;\r
69         img.height = 16;\r
70         return img;\r
71     }\r
72     \r
73     function createHierarchyImage()\r
74     {\r
75         var img = createIcon();\r
76         img.pointsTop = false;\r
77         img.pointsBottom = false;\r
78         img.pointsRight = false;\r
79         img.pmState = pmNone;\r
80         return img;\r
81     }\r
82     \r
83     function genHierarchyImageSrc(hierarchyImg)\r
84     {\r
85         var name = "";\r
86         if (hierarchyImg.pointsTop)\r
87             name += "t";\r
88             \r
89         if (hierarchyImg.pointsBottom)\r
90             name += "b";\r
91             \r
92         if (hierarchyImg.pointsRight)\r
93             name += "r";\r
94             \r
95         if (hierarchyImg.pmState == pmPlus)\r
96             name += "p";\r
97         else if (hierarchyImg.pmState == pmMinus)\r
98             name += "m";\r
99         \r
100         if (name == "")\r
101             name = "shim";\r
102         \r
103         return "../../candydoc/img/tree/" + name + ".gif";\r
104     }\r
105     \r
106     function setSrc(icon, src)\r
107     {\r
108         icon.src = src;\r
109         // After src change width and height are reseted in IE.\r
110         // Bug workaround:\r
111         icon.width = 16;\r
112         icon.height = 16;\r
113     }\r
114     \r
115     this.createChild = function(text, iconSrc)\r
116     {\r
117         var child = new TreeNode(text, iconSrc, this.owner.hrefMode);\r
118         this.children[ this.children.length ] = child;\r
119         this.domEntry.appendChild( child.domEntry );\r
120         child.parentNode = this;\r
121         child.owner = this.owner;\r
122         \r
123         // insert hierarchy images according to deepness level\r
124         // of created child.\r
125         \r
126         if (this.children.length > 1)\r
127         {\r
128             // there were already added child before. So copy `level-1`\r
129             // hierarchy images from it.\r
130             \r
131             var prevAddedChild = this.children[ this.children.length - 2 ];\r
132             \r
133             for (var i = 0; i < prevAddedChild.hierarchyImgs.length - 1; ++i)\r
134             {\r
135                 var prevAddedChildImg = prevAddedChild.hierarchyImgs[i];\r
136                 var img = createHierarchyImage();\r
137                 setSrc(img, prevAddedChildImg.src);\r
138                 img.pointsTop = prevAddedChildImg.pointsTop;\r
139                 img.pointsBottom = prevAddedChildImg.pointsBottom;\r
140                 img.pointsRight = prevAddedChildImg.pointsRight;\r
141                 img.pmState = prevAddedChildImg.pmState;\r
142                 \r
143                 child.hierarchyImgs[ child.hierarchyImgs.length ] = img;\r
144                 child.lineDiv.insertBefore(img, child.icon);\r
145             }\r
146             \r
147             // change last hierarchy image of prevAddedChild from |_ to |-\r
148             var lastHierarchyImg = prevAddedChild.hierarchyImgs[ prevAddedChild.hierarchyImgs.length - 1 ];\r
149             lastHierarchyImg.pointsBottom = true;\r
150             setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));\r
151                         \r
152             // change hierarchy images of prevAddedChild's children on it's last\r
153             // level to |\r
154             prevAddedChild.addHierarchyTBLine(prevAddedChild.hierarchyImgs.length - 1);\r
155         }\r
156         else\r
157         {\r
158             // this is a first child. So copy `level-2`\r
159             // hierarchy images from parent, i.e. this.\r
160             \r
161             for (var i = 0; i < this.hierarchyImgs.length - 1; ++i)\r
162             {\r
163                 var parentImg = this.hierarchyImgs[i];\r
164                 var img = createHierarchyImage();\r
165                 setSrc(img, parentImg.src);\r
166                 img.pointsTop = parentImg.pointsTop;\r
167                 img.pointsBottom = parentImg.pointsBottom;\r
168                 img.pointsRight = parentImg.pointsRight;\r
169                 img.pmState = parentImg.pmState;\r
170                 \r
171                 child.hierarchyImgs[ child.hierarchyImgs.length ] = img;\r
172                 child.lineDiv.insertBefore(img, child.icon);\r
173             }\r
174             \r
175             if (this.hierarchyImgs.length > 0) // we are not root\r
176             {\r
177                 // change last hierarchy image of parent (i.e. this): add minus to it\r
178                 var lastHierarchyImg = this.hierarchyImgs[ this.hierarchyImgs.length - 1];\r
179                 lastHierarchyImg.pmState = pmMinus;\r
180                 setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));\r
181                 lastHierarchyImg.owner = this;\r
182                 lastHierarchyImg.onclick = new Function("e", "this.owner.processPMClick(e);");\r
183                 \r
184                 // make decision on image on `level-1`. It depends on parent's (ie this)\r
185                 // image on same level.\r
186                 var parentL1HierarchyImg = lastHierarchyImg;\r
187                 var l1HierarchyImg = createHierarchyImage();\r
188                 if (parentL1HierarchyImg.pointsBottom)\r
189                 {\r
190                     l1HierarchyImg.pointsTop = true;\r
191                     l1HierarchyImg.pointsBottom = true;\r
192                 }\r
193                 setSrc(l1HierarchyImg, genHierarchyImageSrc(l1HierarchyImg));\r
194                 child.hierarchyImgs[ child.hierarchyImgs.length ] = l1HierarchyImg;\r
195                 child.lineDiv.insertBefore(l1HierarchyImg, child.icon);\r
196             }\r
197         }\r
198         \r
199         // in any case on last level our child will have icon |_\r
200         var img = createHierarchyImage();\r
201         img.pointsTop = true;\r
202         img.pointsRight = true;\r
203         setSrc(img, genHierarchyImageSrc(img));\r
204         \r
205         child.hierarchyImgs[ child.hierarchyImgs.length ] = img;\r
206         child.lineDiv.insertBefore(img, child.icon);\r
207         \r
208         return child;\r
209     }\r
210     \r
211     this.lastChild = function()\r
212     {\r
213         return this.children[ this.children.length - 1 ];\r
214     }\r
215     \r
216     this.child = function(text)\r
217     {\r
218         var ret = null;\r
219         for (var i = 0; i < this.children.length; ++i)\r
220             if (this.children[i].textElement.data == text)\r
221             {\r
222                 ret = this.children[i];\r
223                 break;\r
224             }\r
225             \r
226         return ret;\r
227     }\r
228     \r
229     this.addHierarchyTBLine = function(level)\r
230     {\r
231         for (var i = 0; i < this.children.length; ++i)\r
232         {\r
233             var img = this.children[i].hierarchyImgs[level];\r
234             img.pointsTop = true;\r
235             img.pointsBottom = true;\r
236             setSrc(img, genHierarchyImageSrc(img));\r
237             this.children[i].addHierarchyTBLine(level);\r
238         }\r
239     }\r
240     \r
241     this.expand = function()\r
242     {\r
243         var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];\r
244         \r
245         if (img.pmState == pmPlus)\r
246         {\r
247             img.pmState = pmMinus;\r
248             setSrc(img, genHierarchyImageSrc(img));\r
249             \r
250             for (var i = 0; i < this.children.length; ++i)\r
251                 this.children[i].domEntry.style.display = "";\r
252         }\r
253     }\r
254     \r
255     this.collapse = function()\r
256     {\r
257         var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];\r
258         \r
259         if (img.pmState == pmMinus)\r
260         {\r
261             img.pmState = pmPlus;\r
262             setSrc(img, genHierarchyImageSrc(img));\r
263             \r
264             for (var i = 0; i < this.children.length; ++i)\r
265                 this.children[i].domEntry.style.display = "none";\r
266         }\r
267     }\r
268     \r
269     this.toggle = function()\r
270     {\r
271         var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];\r
272         if (img.pmState == pmMinus)\r
273             this.collapse();\r
274         else\r
275             this.expand();\r
276     }\r
277     \r
278     this.select = function()\r
279     {\r
280         if (this.owner.selection != this)\r
281         {\r
282             if (this.owner.selection)\r
283                 this.owner.selection.setHighlight(hlNone);\r
284                 \r
285             this.owner.selection = this;\r
286             this.setHighlight(hlSelected);\r
287         }\r
288     }\r
289     \r
290     this.setHighlight = function(mode)\r
291     {\r
292         if (mode == hlNone)\r
293         {\r
294             this.textSpan.style.backgroundColor = "";\r
295             this.textSpan.style.color = "";\r
296             this.textSpan.style.border = "";\r
297         }\r
298         else if (mode == hlGrey)\r
299         {\r
300             this.textSpan.style.backgroundColor = "#aaaaaa";\r
301             this.textSpan.style.color = "";\r
302             this.textSpan.style.border = "";\r
303         }\r
304         else if (mode == hlSelected)\r
305         {\r
306             this.textSpan.style.backgroundColor = "3399cc";\r
307             this.textSpan.style.color = "white";\r
308             this.textSpan.style.border = "dotted 1px red";\r
309         }\r
310     }\r
311     \r
312     this.setOnclick = function(proc)\r
313     {\r
314         this.onclick = proc;\r
315     }\r
316     \r
317     this.setRef = function(url)\r
318     {\r
319         if (this.anchor)\r
320             this.anchor.href = url;\r
321     }\r
322     \r
323     this.processPMClick = function(e)\r
324     {\r
325         this.toggle();\r
326         \r
327         // prevent this line selection, stop bubbling\r
328         if (e)\r
329             e.stopPropagation(); // Mozilla way\r
330         if (window.event)\r
331             window.event.cancelBubble = true; // IE way\r
332     }\r
333     \r
334     this.processOnclick = function()\r
335     {\r
336         this.select();\r
337         if (this.onclick instanceof Function)\r
338             this.onclick();\r
339     }\r
340     \r
341     ///////////////////////////////////////////////////////////////////////////\r
342     if (iconSrc)\r
343         this.icon.src = iconSrc;\r
344     else\r
345     {\r
346         this.icon.width = 0;\r
347         this.icon.height = 0;\r
348     }\r
349     \r
350     this.icon.style.verticalAlign = "middle";\r
351     this.icon.style.position = "relative";\r
352     this.icon.style.top = "-1px";\r
353     this.icon.style.paddingRight = "2px";\r
354     \r
355     if (!hrefMode)\r
356     {\r
357         this.textSpan.appendChild( this.textElement );\r
358     }\r
359     else\r
360     {\r
361         this.anchor = document.createElement("a");\r
362         this.anchor.appendChild( this.textElement );\r
363         this.textSpan.appendChild( this.anchor );\r
364     }\r
365     \r
366     this.lineDiv.appendChild( this.icon );\r
367     this.lineDiv.appendChild( this.textSpan );\r
368     this.domEntry.appendChild( this.lineDiv );\r
369     \r
370     this.lineDiv.owner = this;\r
371     \r
372     if (!hrefMode)\r
373         this.lineDiv.onclick = new Function("this.owner.processOnclick();");\r