PageSourceSearch

https://www.kadastrs.lv/javascripts/live_tree.js?1788797250

js kadastrs.lv collected 2026-10-02 03:56:24 UTC 28,400 bytes, 810 lines download raw bytes

1// LiveTree, version: 0.1.2
2//
3// Home page: http://www.epiphyte.ca/code/live_tree.html
4//
5// Copyright (c) 2005-2006 Emanuel Borsboom
6// 
7// Permission is hereby granted, free of charge, to any person obtaining a
8// copy of this software and associated documentation files (the 
9// "Software"), to deal in the Software without restriction, including 
10// without limitation the rights to use, copy, modify, merge, publish, 
11// distribute, sublicense, and/or sell copies of the Software, and to permit
12// persons to whom the Software is furnished to do so, subject to the 
13// following conditions:
14// 
15// The above copyright notice and this permission notice shall be included
16// in all copies or substantial portions of the Software.
17// 
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
21// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
22// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24// USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26function LiveTree(id, options) {
27    this.id = id;
28    
29    if (options == null) {
30        options = {};
31    }
32    
33    this.dataUrl = options.dataUrl;
34    this.cssClass = options.cssClass;
35    this.cssStyle = options.cssStyle;
36    this.expandRootItem = (options.expandRootItem == null ? true : options.expandRootItem);	
37    this.hideRootItem = (options.hideRootItem == null ? false : options.hideRootItem);
38    this.rootItemId = options.rootItemId;
39    this.expandItemOnClick = (options.expandItemOnClick == null ? true : options.expandItemOnClick);
40    this.initialData = options.initialData;
41    this.scroll = (options.scroll == null ? true : options.scroll);
42    this.preloadItems = (options.preloadItems == null ? true : options.preloadItems);
43    
44    this.collapsedItemIconHtml = options.collapsedItemIconHtml;
45    this.expandedItemIconHtml = options.expandedItemIconHtml;
46    this.leafIconHtml = options.leafIconHtml;
47    this.loadingIconHtml = options.loadingIconHtml;
48    this.loadingTreeHtml = options.loadingTreeHtml;
49    this.searchingHtml = options.searchingHtml;
50    this.loadingItemHtml = options.loadingItemHtml;
51
52    this.onClickItem = options.onClickItem;
53    this.allowClickBranch = (options.allowClickBranch == null ? true : options.allowClickBranch);
54    this.allowClickLeaf = (options.allowClickLeaf == null ? true : options.allowClickLeaf);
55    this.onExpandItem = options.onExpandItem;
56    this.onCollapseItem = options.onCollapseItem;
57    this.onLoadItem = options.onLoadItem;
58    
59    this._root = {};
60    this._itemsIndex = {};
61    this._activeItemId = null;
62    this._scrollToItemIdOnLoad = null;
63    this._scrollToItemMustBeExpanded = false;
64    this._searchCount = 0;
65    this._preloadCount = 0;
66    this._updateItemDisplay = null;
67}
68
69//LiveTree.DEV_SHOW_PRELOADS = true;
70//LiveTree.DEV_SHOW_ITEM_IDS = true;
71
72LiveTree.prototype._markItemForUpdateDisplay = function (item) {
73    var tree = this;
74    // This is not very intelligent yet... basically if only one item needs to be updated, that's fine, otherwise the whole tree is updated.
75    if (tree._updateItemDisplay == null) {
76        tree._updateItemDisplay = item;
77    } else if (tree._updateItemDisplay != item) {
78        tree._updateItemDisplay = tree._root;
79    }	
80}
81
82LiveTree.prototype._getClass = function (suffix) {
83    if (suffix != "") {
84        suffix = "_" + suffix;
85    }
86    result = 'live_tree' + suffix;
87    if (this.cssClass != null) {
88        result += ' ' + this.cssClass + suffix;
89    }
90    return result;
91}
92
93LiveTree.prototype._escapeId = function (itemId) {
94    //XXX find out exactly what characters are allowed in HTML id
95    return escape(itemId);
96}
97
98LiveTree.prototype._getCollapsedItemIconHtml = function (item) {
99    if (this.collapsedItemIconHtml != null) {
100        return this.collapsedItemIconHtml;
101    } else {
102        return '<img src="/images/live_tree_transparent_pixel.gif" alt="&gt;" id="' + this.id + '_item_icon_' + this._escapeId(item.id) + '" class="' + this._getClass("item_icon") + ' ' + this._getClass("branch_collapsed_icon") + '" />';
103    }
104}
105
106LiveTree.prototype._getExpandedItemIconHtml = function (item) {
107    if (this.expandedItemIconHtml != null) {
108        return this.expandedItemIconHtml;
109    } else {
110        return '<img src="/images/live_tree_transparent_pixel.gif" alt="v" id="' + this.id + '_item_icon_' + this._escapeId(item.id) + '" class="' + this._getClass("item_icon") + ' ' + this._getClass("branch_expanded_icon") + '" />';
111    }
112}
113
114LiveTree.prototype._getLeafIconHtml = function (item) {
115    if (this.leafIconHtml != null) {
116        return this.leafIconHtml;
117    } else {
118        return '<img src="/images/live_tree_transparent_pixel.gif" alt=" " id="' + this.id + '_item_icon_' + this._escapeId(item.id) + '" class="' + this._getClass("item_icon") + ' ' + this._getClass("leaf_icon") + '" />';
119    }
120}
121
122LiveTree.prototype._getLoadingIconHtml = function () {
123    if (this.loadingIconHtml != null) {
124        return this.loadingIconHtml;
125    } else {
126        return '<img src="/images/live_tree_loading_spinner.gif" alt="[loading]" class="' + this._getClass("loading_icon") + '" />';
127    }
128}
129
130LiveTree.prototype._getLoadingTreeHtml = function () {
131    if (this.loadingTreeHtml != null) {
132        return this.loadingTreeHtml;
133    } else {
134        return '<span class="' + this._getClass("loading_tree") + '">' + this._getLoadingIconHtml() + 'Loading tree data&hellip;</span>';
135    }
136}
137
138LiveTree.prototype._getSearchingHtml = function () {
139    if (this.searchingHtml != null) {
140        return this.searchingHtml;
141    } else {
142        return '<div class="' + this._getClass("searching") + '">' + this._getLoadingIconHtml() + 'Lūdzu, uzgaidiet&hellip;</div>';
143    }
144}
145
146LiveTree.prototype._getLoadingItemHtml = function () {
147    if (this.loadingItemHtml != null) {
148        return this.loadingItemHtml;
149    } else {
150        return this._getLoadingIconHtml() + 'Loading&hellip;';
151    }
152}
153
154LiveTree.prototype._startPreloads = function (item) {
155    var tree = this;
156    if (!tree.preloadItems || tree._preloadCount > 0) {
157        return false;
158    }
159    if (item == null) {
160        item = tree._root;
161    }
162    //alert("XXX startPreloads " + item.id);
163    if (!item.isExpanded || item.isLoading) {
164        return false;
165    }
166    var tailBranch = true;
167    for (var i = 0; i < item.children.length; i++) {
168        var child = item.children[i];
169        if (!child.isLeaf && ( child.isLoaded || child.isLoading )) {
170            tailBranch = false;
171        }
172    }
173    var doLoad = false;
174    if (tailBranch) {
175        for (var i = 0; i < item.children.length; i++) {
176            var child = item.children[i];
177            if (!child.isLeaf) {
178                if (!child.isLoaded && !child.isLoading) {
179                    //alert("setting loading for " + child.id);
180                    doLoad = true;
181                    child.isLoading = true;
182                    child.isLoadingBackground = true;
183                }
184            }
185        }
186    }
187    var didLoad = false;
188    if (doLoad) {
189        //alert("XXX preloading children of " + item.id);
190        tree._preloadCount++;
191        if (item == tree._root) {
192            tree._requestItem(tree._root.children[0].id, 2, tree._onPreloadItemReceived.bind(tree));	
193        } else {
194            tree._requestItem(item.id, 3, tree._onPreloadItemReceived.bind(tree));	
195        }
196        if (LiveTree.DEV_SHOW_PRELOADS) {
197            tree._markItemForUpdateDisplay(item);
198        }
199        didLoad = true;
200    } else {
201        for (var i = 0; i < item.children.length; i++) {
202            var child = item.children[i];
203            if (!child.isLeaf && child.isLoaded) {
204                if (tree._startPreloads(child)) {
205                    didLoad = true;
206                }
207            }
208        }
209    }
210
211    return didLoad;
212}
213
214LiveTree.prototype._stopLoading = function () {
215    var tree = this;
216    function recurse(item) {
217        if (item.isLoading) {
218            item.isLoading = false;
219            item.isExpanded = false;
220        }		
221        if (item.children != null) {
222            for (var i = 0; i < item.children.length; i++) {
223                recurse(item.children[i]);
224            }
225        }
226    }
227    recurse(tree._root);
228    tree._markItemForUpdateDisplay(tree._root);
229    tree._searchCount = 0;
230    tree._preloadCount = 0;
231    tree._updateDisplay();
232}
233
234LiveTree.prototype._onItemFailure = function (request) {
235    alert("LiveTree error: could not get data from server: HTTP error: " + request.status);
236    //alert(request.responseText); //XXX
237    this._stopLoading();
238}
239
240LiveTree.prototype._requestItem = function (itemId, depth, onItemCallback, options) {
241    var tree = this;	
242    if (options == null) {
243        options = {};
244    }
245    var url = tree.dataUrl;
246    var requestOptions = new Object();
247    var delim = "?";
248    if (itemId != null) {
249        requestOptions.itemId = itemId;
250        url += delim + "item_id=" + escape(itemId);		
251        delim = "&";
252    }
253    if (depth != null) {
254        requestOptions.depth = depth;
255        url += delim + "depth=" + depth;
256        delim = "&";
257    }
258    if (options.includeParents) {
259        requestOptions.includeParents = true;
260        requestOptions.rootItemId = tree.rootItemId;
261        url += delim + "include_parents=1&root_item_id=" + escape(tree.rootItemId);
262        tree._searchCount++;
263    }
264    if (options.initialRequest) {
265        requestOptions.initialRequest = true;
266    }
267    new Ajax.Request(url, {onSuccess: function (request) { tree._onItemResponse(request, onItemCallback, requestOptions) }, onFailure: tree._onItemFailure.bind(tree), evalScripts:true, asynchronous:true, method:"get"});
268    return true;
269}
270
271LiveTree.prototype._onExpandItemReceived = function (item, requestOptions) {
272    var tree = this;
273    //alert("XXX _onExpandItemReceived item.id=" + item.id);
274    item.isLoading = false;
275    tree._markItemForUpdateDisplay(item);
276    tree._startPreloads();
277    tree._updateDisplay();	
278}
279
280LiveTree.prototype._onPreloadItemReceived = function (item, requestOptions) {
281    var tree = this;
282    if (tree._preloadCount <= 0) {
283        return;
284    }
285    //alert("XXX got preload item");
286    tree._preloadCount--;
287    item.isLoading = false;
288    for (var i = 0; i < item.children.length; i++) {
289        item.children[i].isLoading = false;		
290    }
291    tree._startPreloads();
292    tree._markItemForUpdateDisplay(item);
293    tree._updateDisplay();	
294}
295
296LiveTree.prototype._onClickExpand = function (item) {
297    var tree = this;
298    var expanded = tree._expandItem(item);
299    tree._updateDisplay();	
300    if (expanded) {
301        tree.scrollToItem(item.id);
302        if (item.isLoading) {
303            tree._scrollToItemIdOnLoad = item.id;
304            tree._scrollToItemMustBeExpanded = true;
305        }
306        if (tree.onExpandItem != null) {
307            tree.onExpandItem(item);
308        }
309    }
310}
311
312LiveTree.prototype._onClickCollapse = function (item) {
313    var tree = this;
314    if (!item.isExpanded) {
315        return;
316    }
317    item.isExpanded = false;
318    tree._markItemForUpdateDisplay(item);
319    tree._updateDisplay();	
320    if (tree.onCollapseItem != null) {
321        tree.onCollapseItem(item);
322    }
323}
324
325LiveTree.prototype._onClickItem = function (item) {
326    var tree = this;
327    if (tree.expandItemOnClick && !item.isExpanded && !item.isLeaf) {
328        tree._onClickExpand(item);		
329    }
330    if (tree.onClickItem != null && ((tree.allowClickLeaf && item.isLeaf) || (tree.allowClickBranch && !item.isLeaf))) {
331        tree.onClickItem(item);
332    }
333    tree._updateDisplay();
334}
335
336LiveTree.prototype._getItem = function (itemId) {
337    return this._itemsIndex[itemId];
338}
339
340LiveTree.prototype._getItemElementId = function (itemId) {
341    return this.id + "_item_" + this._escapeId(itemId);
342}
343
344LiveTree.prototype._getItemElement = function (itemId) {
345    return $(this._getItemElementId(itemId));
346}
347
348LiveTree.prototype._isRootItem = function (item) {
349    var tree = this;
350    return item == tree._root || (tree.hideRootItem && item == tree._root.children[0]);
351}
352
353LiveTree.prototype._renderItemHeading = function (item) {
354    var tree = this;
355    var html = '';
356    if (!item.isLeaf) {
357        html += '<a href="#" id="' + tree.id + '_branch_expand_collapse_link_' + tree._escapeId(item.id) + '" class="' + this._getClass("branch_expand_collapse_link") + '">';
358        if (item.isExpanded) {
359            html += tree._getExpandedItemIconHtml(item);
360        } else {
361            html += tree._getCollapsedItemIconHtml(item);
362        }
363        html += '</a>';
364    } else {
365        html += tree._getLeafIconHtml(item);
366    }
367    var itemLinkExists = false;
368    var extraNameClass = "";
369    if (item.id == tree._activeItemId) {
370        extraNameClass = " " + this._getClass("active_item_name");
371    }
372    var name_html = '<span id="' + tree.id + '_item_name_' + tree._escapeId(item.id) + '" class="' + this._getClass("item_name") + extraNameClass + '">' + item.name + '</span>';
373    if (((tree.onClickItem != null && ((tree.allowClickLeaf && item.isLeaf) || (tree.allowClickBranch && !item.isLeaf))) ||
374            (tree.expandItemOnClick && !item.isLeaf && !item.isExpanded)) && !item.isLoadingDisplay) {
375        name_html = '<a href="#" id="' + tree.id + '_item_link_' + tree._escapeId(item.id) + '" class="' + this._getClass("item_link") + '">' + name_html + '</a>';
376        itemLinkExists = true;
377    }
378    if (LiveTree.DEV_SHOW_ITEM_IDS) {
379        name_html = "(" + item.id + ") " + name_html;
380    }
381    html += name_html;
382    if (LiveTree.DEV_SHOW_PRELOADS) {
383        if (item.isLoading && item.isLoadingBackground) {
384            html += " " + tree._getLoadingIconHtml();
385        }
386    }
387    $(tree.id + "_item_heading_" + tree._escapeId(item.id)).innerHTML = html;
388    if (!item.isLeaf) {
389        if (item.isExpanded) {
390            $(tree.id + '_branch_expand_collapse_link_' + tree._escapeId(item.id)).onclick = function () { tree._onClickCollapse(item); return false }		
391        } else {
392            $(tree.id + '_branch_expand_collapse_link_' + tree._escapeId(item.id)).onclick = function () { tree._onClickExpand(item); return false }
393        }
394    }
395    if (itemLinkExists) {
396        $(tree.id + '_item_link_' + tree._escapeId(item.id)).onclick = function() { tree._onClickItem(item); return false }
397    }
398}
399
400LiveTree.prototype._hideItem = function (child) {
401    var tree = this;
402    var elem = tree._getItemElement(child.id);
403    if (elem) {
404        $(tree.id).removeChild(elem);
405        if (child.isLoaded || (child.isLoading && !child.isLoadingBackgroun
405d)) {
406            tree._hideItemChildren(child);
407        }
408    }
409}
410
411LiveTree.prototype._hideItemChildren = function (item) {
412    var tree = this;
413    tree._hideItem(tree._getLoadingDisplayChild(item));
414    if (!item.isLoading) {
415        for (var i = 0; i < item.children.length; i++) {
416            tree._hideItem(item.children[i]);
417        }
418    }
419    item.childrenVisible = false;
420}
421
422LiveTree.prototype._updateItemChildren = function (item, afterElem, indentLevel, containerElem) {
423    var tree = this;
424    
425    function doUpdateChild(child) {
426        var elem = tree._getItemElement(child.id);
427        if (elem == null) {
428            var html = "";
429            html += '<div id="' + tree.id + '_item_' + tree._escapeId(child.id) + '" class="' + tree._getClass("item") + '">';
430            for (var j = 0; j < indentLevel; j++) {
431                html += '<div class="' + tree._getClass("item_indent") + '">';
432            }
433            html += '<span id="' + tree.id + '_item_heading_' + tree._escapeId(child.id) + '" class="' + tree._getClass("item_heading") + '"></span>';
434            for (var j = 0; j < indentLevel; j++) {
435                html += '</div>';
436            }
437            html += '</div>';
438            new Insertion.After(afterElem, html);
439            elem = tree._getItemElement(child.id);
440        }
441        tree._renderItemHeading(child);
442        afterElem = elem;
443        if (child.isLoaded || (child.isLoading && !child.isLoadingBackground)) {
444            afterElem = tree._updateItemChildren(child, afterElem, indentLevel + 1, containerElem);
445        }
446    }
447    
448    if (!item.isExpanded) {
449        tree._hideItemChildren(item);
450    } else {
451        if (item.isLoaded) {
452            tree._hideItem(tree._getLoadingDisplayChild(item));
453            for (var i = 0; i < item.children.length; i++) {	
454                doUpdateChild(item.children[i]);
455            }
456        } else {
457            doUpdateChild(tree._getLoadingDisplayChild(item));
458        }
459        item.childrenVisible = true;
460    }
461    return afterElem;
462}
463
464LiveTree.prototype._getLoadingDisplayChild = function (item) {
465    var tree = this;
466    var loadingChild = {id: "___LIVE_TREE_LOADING_" + item.id + "___", 
467                         name: tree._getLoadingItemHtml(), 
468                         children: [], 
469                         isLoadingDisplay: true};
470    tree._setItemDerivedAttributes(loadingChild);
471    return loadingChild;
472}
473
474LiveTree.prototype._updateDisplay = function () {
475    var tree = this;
476    if (tree._searchCount > 0) {
477        Element.show(tree.id + "_searching");
478    } else {
479        Element.hide(tree.id + "_searching");
480    }
481    var updateItem = tree._updateItemDisplay;	
482    if (updateItem != null) {
483        tree._updateItemDisplay = null;
484        if (tree._isRootItem(updateItem)) {
485            if (tree.hideRootItem) {
486                updateItem = tree._root.children[0];
487            }
488            tree._updateItemChildren(updateItem, $(tree.id + "_root"), 0, $(tree.id));
489        } else {
490            tree._renderItemHeading(updateItem);
491            
492            var indentLevel = 0;
493            var parentItem = updateItem;
494            while (!tree._isRootItem(parentItem)) {
495                indentLevel++;
496                parentItem = parentItem.parent;
497            }
498            
499            if (updateItem.isLoaded || (updateItem.isLoading && !updateItem.isLoadingBackground)) {
500                tree._updateItemChildren(updateItem, tree._getItemElement(updateItem.id), indentLevel, $(tree.id));
501            }
502        }
503    }
504    tree._checkScrollOnLoad();
505}
506
507LiveTree.prototype._checkScrollOnLoad = function () {
508    var tree = this;
509    if (tree._scrollToItemIdOnLoad == null) {
510        return;
511    }
512    var item = tree._itemsIndex[tree._scrollToItemIdOnLoad];
513    if (item == null) {
514        return;
515    }
516    if (tree._scrollToItemMustBeExpanded) {
517        if (item.isLoaded) {
518            // The user may have collapsed the item while it was loading, so only scroll to it if it's still expanded.
519            if (item.isExpanded) {
520                tree.scrollToItem(item.id);
521            }
522            tree._scrollToItemIdOnLoad = null;
523        }
524    } else {
525        tree.scrollToItem(item.id);
526        tree._scrollToItemIdOnLoad = null;		
527    }
528}
529
530LiveTree.prototype._getElementPosition = function (destinationLink) {
531    // borrowed from http://www.sitepoint.com/print/scroll-smoothly-javascript
532    var destx = destinationLink.offsetLeft;  
533    var desty = destinationLink.offsetTop;
534    var thisNode = destinationLink;
535    while (thisNode.offsetParent &&  
536            (thisNode.offsetParent != document.body)) {
537        thisNode = thisNode.offsetParent;
538        destx += thisNode.offsetLeft;
539        desty += thisNode.offsetTop;
540    }
541    return { x: destx, y: desty }
542}
543
544LiveTree.prototype._scrollTo = function (top) {
545    var tree = this;
546    if (!tree.scroll) {
547        return;
548    }
549    var containerElem = $(tree.id);
550    containerElem.scrollTop = top;
551}
552
553LiveTree.prototype.scrollToItem = function (itemId) {
554    var tree = this;
555    if (!tree.scroll) {
556        return;
557    }
558    var itemElem = tree._getItemElement(itemId);
559    if (itemElem == null) {
560        return;
561    }
562    var containerElem = $(tree.id);
563    var itemPos = tree._getElementPosition(itemElem);
564    var containerPos = tree._getElementPosition(containerElem);
565    var itemTop = itemPos.y - containerPos.y;
566    var containerHeight = containerElem.offsetHeight - 35; //HACK: adjust for space used by scrollbars and other decoration
567    if (itemTop + itemElem.offsetHeight > containerElem.scrollTop + containerHeight ||
568            itemTop < containerElem.scrollTop) {
569        // item is currently not entirely visible
570        if (itemElem.offsetHeight > containerHeight) {
571            // item is too big to fit, so scroll to the top
572            tree._scrollTo(itemTop);
573        } else {
574            if (itemTop < containerElem.scrollTop + containerHeight) {
575                // item is partially onscreen (the top is showing), so put whole item at bottom
576                tree._scrollTo(itemTop + itemElem.offsetHeight - containerHeight);
577            } else {
578                // item is entirely offscreen, so center it
579                tree._scrollTo(itemTop - containerHeight/2 + itemElem.offsetHeight/2);
580            }
581        }
582    }
583    tree._scrollToItemOnLoad = null;
584}
585
586LiveTree.prototype._expandItem = function (item) {
587    var tree = this;
588    
589    // Make sure all item's parents are expanded as well
590    var didExpand = false;
591    var parent = item.parent;
592    while (parent != tree._root && parent != null) {
593        if (!parent.isExpanded) {
594            parent.isExpanded = true;
595            tree._markItemForUpdateDisplay(parent);
596            didExpand = true;
597        }
598        parent = parent.parent;
599    }	
600
601    // Expand the selected item
602    var needToLoad = false;
603    if (!item.isExpanded) {
604        needToLoad = (item.children == null && !item.isLoading);
605        if (needToLoad) {
606            item.isLoading = true;
607        }
608        item.isLoadingBackground = false;
609        item.isExpanded = true;
610        tree._markItemForUpdateDisplay(item);
611        didExpand = true;
612    }
613    
614    // If the item has not loaded, load it now
615    if (needToLoad) {
616        tree._requestItem(item.id, 2, tree._onExpandItemReceived.bind(tree));	
617    }	
618
619    tree._startPreloads();	
620    return didExpand;
621}
622
623LiveTree.prototype._onExpandItemParentsReceived = function (item, requestOptions) {
624    var tree = this;
625    var requestedItem = tree._getItem(requestOptions.itemId);
626    this._expandItem(requestedItem);
627    tree._startPreloads();
628    tree._updateDisplay();	
629}
630
631LiveTree.prototype.expandItem = function (itemId) {
632    var tree = this;
633    var item = tree._getItem(itemId);
634    var search = false;
635    if (item == null) {
636        tree._requestItem(itemId, 2, tree._onExpandItemParentsReceived.bind(tree), { includeParents: true });
637        search = true;
638    } else {
639        this._expandItem(this._itemsIndex[itemId]);
640    }
641    tree._updateDisplay();
642    if (search) {
643        tree._scrollTo(0);
644        tree._scrollToItemIdOnLoad = itemId;		
645        tree._scrollToItemMustBeExpanded = false;
646    } else {
647        tree.scrollToItem(itemId);
648    }
649}
650
651LiveTree.prototype._onExpandParentsOfItemReceived = function (item, requestOptions) {
652    var tree = this;
653    //alert("XXX _onExpandParentsOfItemReceived item.id=" + item.id);
654    var requestedItem = tree._getItem(requestOptions.itemId);
655    tree._expandItem(requestedItem.parent);
656    tree._startPreloads();
657    tree._updateDisplay();	
658}
659
660LiveTree.prototype.expandParentsOfItem = function (itemId) {
661    var tree = this;
662    var item = tree._getItem(itemId);
663    var search = false;
664    if (item == null) {
665        tree._requestItem(itemId, 1, tree._onExpandParentsOfItemReceived.bind(tree), { includeParents: true });
666        search = true;
667    } else {
668        tree._expandItem(item.parent);
669    }
670    tree._updateDisplay();
671    if (search) {
672        tree._scrollTo(0);
673        tree._scrollToItemIdOnLoad = itemId;		
674        tree._scrollToItemMustBeExpanded = false;
675    } else {
676        tree.scrollToItem(itemId);
677    }
678}
679
680LiveTree.prototype.activateItem = function (itemId) {
681    var tree = this;
682    // un-highlight the old active item
683    var oldElem = $(tree.id + '_item_name_' + tree._escapeId(tree._activeItemId));
684    if (oldElem != null) {
685        oldElem.className = tree._getClass("item_name");
686    }
687    // highlight the new active item
688    var elem = $(tree.id + '_item_name_' + tree._escapeId(itemId));
689    if (elem != null) {
690        elem.className = tree._getClass("item_name") + " " + tree._getClass("active_item_name");
691    }
692    tree._activeItemId = itemId;
693    tree.scrollToItem(itemId);
694}
695
696LiveTree.prototype.getHtml = function() {
697    var tree = this;	
698    var html = '';
699    html += '<div id="' + tree.id + '" class="' + tree._getClass("") + '"';
700    if (tree.cssStyle != null) {
701        html += ' style="' + tree.cssStyle + '"';
702    }
703    html += '>';
704    html += '<div id="' + tree.id + '_searching" style="display:none">' + tree._getSearchingHtml() + '</div>';
705    html += '<div id="' + tree.id + '_loading">' + tree._getLoadingTreeHtml() + '</div>';
706    html += '<div id="' + tree.id + '_root"></div>';
707    html += '</div>';
708    return html;
709}
710
711LiveTree.prototype._setItemDerivedAttributes = function (child) {
712    child.isLeaf = !(child.children == null || child.children.length > 0);
713    child.isLoaded = child.children != null;
714}
715
716LiveTree.prototype._setupNewItemChildren = function (item) {
717    var tree = this;
718    if (item.children != null) {
719        for (var i = 0; i < item.children.length; i++) {
720            var child = item.children[i];
721            tree._setItemDerivedAttributes(child);
722            child.parent = item;
723            tree._itemsIndex[child.id] = child;
724            tree._setupNewItemChildren(child);
725        }
726    }
727}
728
729LiveTree.prototype._addNewItems = function (newItem) {
730    var tree = this;
731    var oldItem = tree._getItem(newItem.id);
732    if (newItem.children != null && oldItem != null) {
733        if (!oldItem.isLoaded) {		
734            // Old item has been seen, but its children were not loaded.
735            // New item does have children, so add the children to the old item and flag it as as loaded.
736            oldItem.children = newItem.children;
737            tree._setupNewItemChildren(oldItem);
738            oldItem.isLoaded = true;
739        } else {
740            // Item is already in the tree and has loaded, so recurse to new item's children
741            for (var i = 0; i < newItem.children.length; i++) {
742                tree._addNewItems(newItem.children[i]);
743            }
744        }
745    }
746    return oldItem;
747}
748
749LiveTree.prototype._onItemResponse = function (request, onItemCallback, requestOptions) {
750    var tree = this;
751    if (requestOptions.includeParents && tree._searchCount > 0) {
752        tree._searchCount--;
753    }
754    var item;
755    try {
756        eval("item = " + request.responseText);
757    } catch (e) {
758        //alert("LiveTree error: cannot parse data from server: " + e);
759        tree._stopLoading();
760        return;
761    }
762    
763    if (requestOptions.initialRequest) {
764        tree._handleInitialItem(item);
765    } else {	
766        var oldItem = tree._addNewItems(item);
767        if (oldItem == null) {
768            alert("LiveTree error: cannot add received item to tree");
769            tree._stopLoading();
770            return;
771        }
772    }
773    onItemCallback(oldItem, requestOptions);
774}
775
776LiveTree.prototype._onInitialItemReceived = function () {
777    var tree = this;
778    this.rootItemId = tree._root.children[0].id;
779    Element.hide($(tree.id + "_loading"));
780    if (tree.hideRootItem || tree.expandRootItem) {
781        tree._expandItem(tree._root.children[0]);
782    }
783    tree._root.isExpanded = true;
784    tree._markItemForUpdateDisplay(tree._root);
785    tree._startPreloads();
786    tree._updateDisplay();		
787}
788
789LiveTree.prototype._handleInitialItem = function (item) {
790    var tree = this;
791    tree._root.children = [item];
792    tree._root.isLoaded = true;
793    tree._setupNewItemChildren(tree._root);
794}
795
796LiveTree.prototype.start = function() {
797    var tree = this;	
798    if (tree.initialData != null) {
799        tree._handleInitialItem(tree.initialData);
800        tree._onInitialItemReceived(tree.initialData);
801    } else {
802        tree._requestItem(tree.rootItemId, (tree.expandRootItem || tree.hideRootItem) ? 2 : 1, tree._onInitialItemReceived.bind(tree), { initialRequest: true });
803    }
804}
805
806LiveTree.prototype.render = function () {
807    var tree = this;	
808    document.write(tree.getHtml());
809    tree.start();
810}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.