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=">" 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…</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…</div>'; 143 } 144} 145 146LiveTree.prototype._getLoadingItemHtml = function () { 147 if (this.loadingItemHtml != null) { 148 return this.loadingItemHtml; 149 } else { 150 return this._getLoadingIconHtml() + 'Loading…'; 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.