PageRenderTime 58ms CodeModel.GetById 8ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 0ms

/ddoc/candydoc/tree.js

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