1 /** 2 * Copyright (C) 2005-2010 Alfresco Software Limited. 3 * 4 * This file is part of Alfresco 5 * 6 * Alfresco is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * Alfresco is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public License 17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /** 21 * DocumentList TreeView component. 22 * 23 * @namespace Alfresco 24 * @class Alfresco.DocListTree 25 */ 26 (function() 27 { 28 /** 29 * YUI Library aliases 30 */ 31 var Dom = YAHOO.util.Dom, 32 Event = YAHOO.util.Event; 33 34 /** 35 * Alfresco Slingshot aliases 36 */ 37 var $combine = Alfresco.util.combinePaths; 38 39 /** 40 * DocumentList TreeView constructor. 41 * 42 * @param {String} htmlId The HTML id of the parent element 43 * @return {Alfresco.DocListTree} The new DocListTree instance 44 * @constructor 45 */ 46 Alfresco.DocListTree = function DLT_constructor(htmlId) 47 { 48 Alfresco.DocListTree.superclass.constructor.call(this, "Alfresco.DocListTree", htmlId, ["treeview", "json"]); 49 50 // Path filterId 51 this.filterId = "path"; 52 53 // Register with Filter Manager 54 Alfresco.util.FilterManager.register(this.name, this.filterId); 55 56 // Initialise prototype properties 57 this.currentFilter = {}; 58 this.pathsToExpand = []; 59 60 // Decoupled event listeners 61 YAHOO.Bubbling.on("folderCopied", this.onFolderCopied, this); 62 YAHOO.Bubbling.on("folderCreated", this.onFolderCreated, this); 63 YAHOO.Bubbling.on("folderDeleted", this.onFolderDeleted, this); 64 YAHOO.Bubbling.on("folderMoved", this.onFolderMoved, this); 65 YAHOO.Bubbling.on("folderRenamed", this.onFolderRenamed, this); 66 YAHOO.Bubbling.on("filterChanged", this.onFilterChanged, this); 67 YAHOO.Bubbling.on("dropTargetOwnerRequest", this.onDropTargetOwnerRequest, this); 68 YAHOO.Bubbling.on("documentDragOver", this.onDocumentDragOver, this); 69 YAHOO.Bubbling.on("documentDragOut", this.onDocumentDragOut, this); 70 71 return this; 72 }; 73 74 YAHOO.extend(Alfresco.DocListTree, Alfresco.component.Base, 75 { 76 /** 77 * Object container for initialization options 78 */ 79 options: 80 { 81 /** 82 * Current siteId. 83 * 84 * @property siteId 85 * @type string 86 */ 87 siteId: "", 88 89 /** 90 * ContainerId representing root container 91 * 92 * @property containerId 93 * @type string 94 * @default "documentLibrary" 95 */ 96 containerId: "documentLibrary", 97 98 /** 99 * Evaluate child folders flag 100 * 101 * @property evaluateChildFolders 102 * @type boolean 103 * @default true 104 */ 105 evaluateChildFolders: true, 106 107 /** 108 * Maximum folder count configuration setting 109 * 110 * @property maximumFolderCount 111 * @type int 112 * @default -1 113 */ 114 maximumFolderCount: -1, 115 116 /** 117 * Indicates whether or not to set each tree node as a YUI Drag and Drop 118 * target. 119 * 120 * @property setDropTargets 121 * @type boolean 122 * @default false 123 */ 124 setDropTargets: false 125 }, 126 127 /** 128 * Flag set after TreeView instantiated. 129 * 130 * @property isReady 131 * @type boolean 132 */ 133 isReady: false, 134 135 /** 136 * Initial filter on page load. 137 * 138 * @property initialFilter 139 * @type string 140 */ 141 initialFilter: null, 142 143 /** 144 * Current path being browsed. 145 * 146 * @property currentPath 147 * @type string 148 */ 149 currentPath: "", 150 151 /** 152 * Currently active filter 153 * 154 * @property currentFilter 155 * @type object 156 */ 157 currentFilter: null, 158 159 /** 160 * Tracks if this component is the active filter owner. 161 * 162 * @property isFilterOwner 163 * @type boolean 164 */ 165 isFilterOwner: false, 166 167 /** 168 * Paths we have to expand as a result of a deep navigation event. 169 * 170 * @property pathsToExpand 171 * @type array 172 */ 173 pathsToExpand: null, 174 175 /** 176 * Selected tree node. 177 * 178 * @property selectedNode 179 * @type {YAHOO.widget.Node} 180 */ 181 selectedNode: null, 182 183 /** 184 * Fired by YUI when parent element is available for scripting 185 * @method onReady 186 */ 187 onReady: function DLT_onReady() 188 { 189 // Reference to self - used in inline functions 190 var me = this; 191 192 // Create twister from our H2 tag 193 Alfresco.util.createTwister(this.id + "-h2", this.name.substring(this.name.lastIndexOf(".") + 1)); 194 195 /** 196 * Dynamically loads TreeView nodes. 197 * This MUST be inline in order to have access to the Alfresco.DocListTree class. 198 * @method fnLoadNodeData 199 * @param node {object} Parent node 200 * @param fnLoadComplete {function} Expanding node's callback function 201 */ 202 this.fnLoadNodeData = function DLT_oR_fnLoadNodeData(node, fnLoadComplete) 203 { 204 // Get the path this node refers to 205 var nodePath = node.data.path; 206 207 // Prepare URI for XHR data request 208 var uri = me._buildTreeNodeUrl.call(me, nodePath); 209 210 // Prepare the XHR callback object 211 var callback = 212 { 213 success: function DLT_lND_success(oResponse) 214 { 215 var results = YAHOO.lang.JSON.parse(oResponse.responseText), item, treeNode; 216 217 // Update parent node's nodeRef if we didn't have it before 218 if (results.parent && node.data.nodeRef.length === 0) 219 { 220 node.data.nodeRef = results.parent.nodeRef; 221 } 222 223 if (results.items) 224 { 225 for (var i = 0, j = results.items.length; i < j; i++) 226 { 227 item = results.items[i]; 228 item.path = $combine(nodePath, item.name); 229 treeNode = this._buildTreeNode(item, node, false); 230 231 if (!item.hasChildren) 232 { 233 treeNode.isLeaf = true; 234 } 235 } 236 } 237 238 if (results.resultsTrimmed) 239 { 240 tempNode = new YAHOO.widget.TextNode( 241 { 242 label: "<" + this.msg("message.folders-trimmed") + ">", 243 style: "folders-trimmed" 244 }, node, false); 245 } 246 247 /** 248 * Execute the node's loadComplete callback method which comes in via the argument 249 * in the response object 250 */ 251 oResponse.argument.fnLoadComplete(); 252 }, 253 254 // If the XHR call is not successful, fire the TreeView callback anyway 255 failure: function DLT_lND_failure(oResponse) 256 { 257 if (oResponse.status == 401) 258 { 259 // Our session has likely timed-out, so refresh to offer the login page 260 window.location.reload(); 261 } 262 else 263 { 264 try 265 { 266 var response = YAHOO.lang.JSON.parse(oResponse.responseText); 267 268 // Get the "Documents" node 269 var rootNode = this.widgets.treeview.getRoot(); 270 var docNode = rootNode.children[0]; 271 docNode.isLoading = false; 272 docNode.isLeaf = true; 273 docNode.label = response.message; 274 docNode.labelStyle = "ygtverror"; 275 rootNode.refresh(); 276 } 277 catch(e) 278 { 279 } 280 } 281 }, 282 283 // Callback function scope 284 scope: me, 285 286 // XHR response argument information 287 argument: 288 { 289 "node": node, 290 "fnLoadComplete": fnLoadComplete 291 } 292 }; 293 294 // Make the XHR call using Connection Manager's asyncRequest method 295 YAHOO.util.Connect.asyncRequest('GET', uri, callback); 296 }; 297 298 // Build the TreeView widget 299 this._buildTree(); 300 301 this.isReady = true; 302 if (this.initialFilter !== null) 303 { 304 // We weren't ready for the first filterChanged event, so fake it here 305 this.onFilterChanged("filterChanged", 306 [ 307 null, 308 this.initialFilter 309 ]); 310 } 311 }, 312 313 /** 314 * Handles "dropTargetOwnerRequest" by determining whether or not the target belongs to the TreeView 315 * widget, and if it does determines it's nodeRef and uses the callback function with it. 316 * 317 * @method onDropTargetOwnerRequest 318 * @property layer The name of the event 319 * @property args The event payload 320 */ 321 onDropTargetOwnerRequest: function DLT_onDropTargetOwnerRequest(layer, args) 322 { 323 if (args && args[1] && args[1].elementId) 324 { 325 var node = this.widgets.treeview.getNodeByElement(Dom.get(args[1].elementId)); 326 if (node != null) 327 { 328 // Perform the drag out to clear the highlight... 329 this.onDocumentDragOut(layer, args); 330 331 var nodeRef = node.data.nodeRef; 332 var path = node.data.path; 333 args[1].callback.call(args[1].scope, nodeRef, path); 334 } 335 } 336 }, 337 338 /** 339 * Handles applying the styling and node creation required when a document is dragged 340 * over a tree node. 341 * 342 * @method onDocumentDragOver 343 * @property layer The name of the event 344 * @property args The event payload 345 */ 346 onDocumentDragOver: function DLTB_onDocumentDragOver(layer, args) 347 { 348 if (args && args[1] && args[1].elementId) 349 { 350 var rootEl = this.widgets.treeview.getEl(); 351 if (args[1].event.clientX > rootEl.clientWidth) 352 { 353 // If the current x co-ordinate of the mouse pointer is greater than the width 354 // of the tree element then we shouldn't add a highlight. This is to address 355 // the issue where the overflow of wide tree nodes is hidden behind the 356 // document list. Without this test it is possible to show a highlight on 357 // a tree node when it appears as though the mouse is not over it. 358 } 359 else 360 { 361 // The current x co-ordinate of the mouse pointer is within the tree element so 362 // the node can be highlighted... 363 var dropTargetEl = Dom.get(args[1].elementId); 364 if (dropTargetEl != this.widgets.treeview.getEl()) 365 { 366 var node = this.widgets.treeview.getNodeByElement(dropTargetEl); 367 if (node != null) 368 { 369 var currEl = dropTargetEl; 370 while (currEl.tagName != "TABLE") 371 { 372 currEl = currEl.parentNode; 373 } 374 Dom.addClass(currEl, "documentDragOverHighlight"); 375 376 var folderCell = dropTargetEl.parentNode.children[dropTargetEl.parentNode.children.length - 2]; 377 while (folderCell.children.length == 1) 378 { 379 var arrowSpan = document.createElement("span"); 380 Dom.addClass(arrowSpan, "documentDragOverArrow"); 381 folderCell.appendChild(arrowSpan); 382 } 383 } 384 } 385 } 386 } 387 }, 388 389 /** 390 * Handles applying the styling and node deletion required when a document is dragged 391 * out of a tree node. 392 * 393 * @method onDocumentDragOut 394 * @property layer The name of the event 395 * @property args The event payload 396 */ 397 onDocumentDragOut: function DLTB_onDocumentDragOut(layer, args) 398 { 399 if (args && args[1] && args[1].elementId) 400 { 401 var dropTargetEl = Dom.get(args[1].elementId); 402 if (dropTargetEl == this.widgets.treeview.getEl()) 403 { 404 // If the document has been dragged out of the tree element then we need 405 // to remove any highlight and arrow from previously highlighted tree nodes. 406 // This would be the case if the highlighted tree node is wider than the 407 // tree element and the mouse has moved to the right of the splitter so is 408 // outside of the tree but still over the tree node... 409 var highlights = Dom.getElementsByClassName("documentDragOverHighlight", "table", dropTargetEl); // Should be only one 410 for (var i = 0, j = highlights.length; i < j ; i++) 411 { 412 Dom.removeClass(highlights[i], "documentDragOverHighlight"); 413 } 414 var arrows = Dom.getElementsByClassName("documentDragOverArrow", "span", dropTargetEl); 415 for (var i = 0, j = arrows.length; i < j ; i++) 416 { 417 arrows[i].parentNode.removeChild(arrows[i]); 418 } 419 } 420 else 421 { 422 // If the document has been dragged out of a tree node then we need to 423 // remove the highlight and arrow previously added when the document was 424 // dragged over it... 425 var node = this.widgets.treeview.getNodeByElement(dropTargetEl); 426 if (node != null) 427 { 428 var currEl = dropTargetEl; 429 while (currEl.tagName != "TABLE") 430 { 431 currEl = currEl.parentNode; 432 } 433 Dom.removeClass(currEl, "documentDragOverHighlight"); 434 var folderCell = dropTargetEl.parentNode.children[dropTargetEl.parentNode.children.length - 2]; 435 while (folderCell.children.length > 1) 436 { 437 folderCell.removeChild(Dom.getLastChild(folderCell)); 438 } 439 } 440 } 441 } 442 }, 443 444 /** 445 * Fired by YUI TreeView when a node has finished expanding 446 * @method onExpandComplete 447 * @param oNode {YAHOO.widget.Node} the node recently expanded 448 */ 449 onExpandComplete: function DLT_onExpandComplete(oNode) 450 { 451 // Make sure the tree's Dom has been updated 452 this.widgets.treeview.render(); 453 454 // Redrawing the tree will clear the highlight 455 if (this.isFilterOwner) 456 { 457 this._showHighlight(true); 458 } 459 460 if (this.pathsToExpand && this.pathsToExpand.length > 0) 461 { 462 var node = this.widgets.treeview.getNodeByProperty("path", this.pathsToExpand.shift()); 463 if (node !== null) 464 { 465 if (node.data.path == this.currentPath) 466 { 467 this._updateSelectedNode(node); 468 } 469 Alfresco.logger.debug("node.expand: DLT_onExpandComplete"); 470 node.expand(); 471 } 472 } 473 else if (this.initialFilter !== null) 474 { 475 // We missed the filterChanged event, so fake it here 476 this.onFilterChanged("filterChanged", 477 [ 478 null, 479 { 480 filterId: this.initialFilter.filterId, 481 filterOwner: this.initialFilter.filterOwner, 482 filterData: this.initialFilter.filterData 483 } 484 ]); 485 this.initialFilter = null; 486 } 487 else 488 { 489 // Finished expanding, can now safely set DND targets... 490 this._applyDropTargets(); 491 } 492 }, 493 494 /** 495 * Creates the drag and drop targets within the tree. The targets get removed 496 * each time that the tree is refreshed in anyway, so it is imperative that they 497 * get reset when required. 498 * 499 * @method _applyDropTargets 500 */ 501 _applyDropTargets: function DLT__applyDropTargets() 502 { 503 if (this.options.setDropTargets) 504 { 505 var rootEl = this.widgets.treeview.getEl(); 506 507 // Set the root element of the tree as a drop target. This is necessary in order 508 // to handle the specific problem of the hidden overflow of tree nodes being at 509 // the same location of the screen as the main DocumentList drop targets. Drop events 510 // will be ignored for this element, but dragOut events will be used to ensure that 511 // all tree highlights are cleared. 512 new YAHOO.util.DDTarget(rootEl); 513 Dom.addClass(rootEl, "documentDroppableHighlights"); 514 515 var dndTargets = Dom.getElementsByClassName("ygtvcell", "td", rootEl); 516 for (var i = 0, j = dndTargets.length; i < j; i++) 517 { 518 new YAHOO.util.DDTarget(dndTargets[i]); 519 Dom.addClass(dndTargets[i], "documentDroppable"); 520 Dom.addClass(dndTargets[i], "documentDroppableHighlights"); 521 } 522 } 523 }, 524 525 /** 526 * Fired by YUI TreeView when a node label is clicked 527 * @method onNodeClicked 528 * @param args.event {HTML Event} the event object 529 * @param args.node {YAHOO.widget.Node} the node clicked 530 * @return allowExpand {boolean} allow or disallow node expansion 531 */ 532 onNodeClicked: function DLT_onNodeClicked(args) 533 { 534 var node = args.node; 535 536 if (this.isFilterOwner && node == this.selectedNode) 537 { 538 YAHOO.Bubbling.fire("metadataRefresh"); 539 } 540 else 541 { 542 this._updateSelectedNode(node); 543 544 // Fire the change filter event 545 YAHOO.Bubbling.fire("changeFilter", 546 { 547 filterOwner: this.name, 548 filterId: this.filterId, 549 filterData: node.data.path 550 }); 551 } 552 553 Event.stopEvent(args.event); 554 // Prevent the tree node from expanding (TODO: user preference?) 555 return false; 556 }, 557 558 /** 559 * Path changed handler 560 * @method pathChanged 561 * @param path {string} New path 562 * @param flags {object} Logic control flags 563 */ 564 pathChanged: function DLT_pathChanged(path, flags) 565 { 566 // ensure path starts with leading slash 567 path = $combine("/", path); 568 this.currentPath = path; 569 570 // Search the tree to see if this path's node is expanded 571 var node = this.widgets.treeview.getNodeByProperty("path", path); 572 if (node !== null) 573 { 574 // Node found 575 this._updateSelectedNode(node); 576 if (!node.expanded) 577 { 578 Alfresco.logger.debug("node.expand: DLT_pathChanged", path); 579 node.expand(); 580 } 581 while (node.parent !== null) 582 { 583 node = node.parent; 584 if (!node.expanded) 585 { 586 Alfresco.logger.debug("node.expand: DLT_onPathChanged (parent)", path); 587 node.expand(); 588 } 589 } 590 return; 591 } 592 593 /** 594 * The path's node hasn't been loaded into the tree. Create a stack 595 * of parent paths that we need to expand one-by-one in order to 596 * eventually display the current path's node 597 */ 598 var paths = path.split("/"), 599 expandPath = "/"; 600 // Check for root path special case (split will have created 2 empty array members) 601 if (path === "/") 602 { 603 paths = [""]; 604 } 605 for (var i = 0; i < paths.length; i++) 606 { 607 // Push the path onto the list of paths to be expanded 608 expandPath = $combine(expandPath, paths[i]); 609 this.pathsToExpand.push(expandPath); 610 } 611 612 // Kick off the expansion process by expanding the first unexpanded path 613 do 614 { 615 node = this.widgets.treeview.getNodeByProperty("path", this.pathsToExpand.shift()); 616 } while (this.pathsToExpand.length > 0 && node && node.expanded); 617 618 if (node !== null) 619 { 620 Alfresco.logger.debug("node.expand: DLT_onPathChanged (pathsToExpand)", this.pathsToExpand); 621 node.expand(); 622 } 623 }, 624 625 626 /** 627 * BUBBLING LIBRARY EVENT HANDLERS FOR PAGE EVENTS 628 * Disconnected event handlers for inter-component event notification 629 */ 630 631 /** 632 * Fired when a folder has been renamed 633 * @method onFolderRenamed 634 * @param layer {string} the event source 635 * @param args {object} arguments object 636 */ 637 onFolderRenamed: function DLT_onFolderRenamed(layer, args) 638 { 639 var obj = args[1]; 640 if (obj && (obj.file !== null)) 641 { 642 var node = this.widgets.treeview.getNodeByProperty("nodeRef", obj.file.node.nodeRef); 643 if (node !== null) 644 { 645 // Node found, so rename it 646 node.label = obj.file.displayName; 647 node.data.path = $combine(obj.file.location.path, obj.file.location.file); 648 this.widgets.treeview.render(); 649 this._showHighlight(true); 650 } 651 } 652 653 // Make sure that the drag and drop targets are correctly set... 654 this._applyDropTargets(); 655 }, 656 657 /** 658 * Fired when a folder has been copied 659 * 660 * Event data contains: 661 * nodeRef - the nodeRef of the newly copied object 662 * destination - new parent path 663 * @method onFolderCopied 664 * @param layer {string} the event source 665 * @param args {object} arguments object 666 */ 667 onFolderCopied: function DLT_onFolderCopied(layer, args) 668 { 669 var obj = args[1]; 670 if (obj !== null) 671 { 672 if (obj.nodeRef && obj.destination) 673 { 674 // Do we have the parent of the node's copy loaded? 675 var nodeDest = this.widgets.treeview.getNodeByProperty("path", $combine("/", obj.destination)); 676 if (nodeDest) 677 { 678 if (nodeDest.expanded) 679 { 680 this._sortNodeChildren(nodeDest); 681 } 682 else 683 { 684 nodeDest.isLeaf = false; 685 } 686 } 687 688 this.widgets.treeview.render(); 689 this._showHighlight(true); 690 } 691 } 692 693 // Make sure that the drag and drop targets are correctly set... 694 this._applyDropTargets(); 695 }, 696 697 /** 698 * Fired when a folder has been created 699 * @method onFolderCreated 700 * @param layer {string} the event source 701 * @param args {object} arguments object 702 */ 703 onFolderCreated: function DLT_onFolderCreated(layer, args) 704 { 705 var obj = args[1]; 706 if (obj && (obj.parentNodeRef !== null)) 707 { 708 var parentNode = this.widgets.treeview.getNodeByProperty("nodeRef", obj.parentNodeRef); 709 if (parentNode !== null) 710 { 711 this._sortNodeChildren(parentNode); 712 } 713 } 714 715 // Make sure that the drag and drop targets are correctly set... 716 this._applyDropTargets(); 717 718 }, 719 720 /** 721 * Fired when a folder has been deleted 722 * @method onFolderDeleted 723 * @param layer {string} the event source 724 * @param args {object} arguments object 725 */ 726 onFolderDeleted: function DLT_onFolderDeleted(layer, args) 727 { 728 var obj = args[1]; 729 if (obj !== null) 730 { 731 var node = null; 732 733 if (obj.path) 734 { 735 // ensure path starts with leading slash 736 node = this.widgets.treeview.getNodeByProperty("path", $combine("/", obj.path)); 737 } 738 else if (obj.nodeRef) 739 { 740 node = this.widgets.treeview.getNodeByProperty("nodeRef", obj.nodeRef); 741 } 742 743 if (node !== null) 744 { 745 var parentNode = node.parent; 746 // Node found, so delete it 747 this.widgets.treeview.removeNode(node); 748 // Have all the parent child nodes been removed now? 749 if (parentNode !== null) 750 { 751 if (!parentNode.hasChildren()) 752 { 753 parentNode.isLeaf = true; 754 } 755 } 756 this.widgets.treeview.render(); 757 this._showHighlight(true); 758 } 759 } 760 761 // Make sure that the drag and drop targets are correctly set... 762 this._applyDropTargets(); 763 }, 764 765 /** 766 * Fired when a folder has been moved 767 * 768 * Event data contains: 769 * nodeRef - the nodeRef of the moved object 770 * destination - new parent path 771 * @method onFolderMoved 772 * @param layer {string} the event source 773 * @param args {object} arguments object 774 */ 775 onFolderMoved: function DLT_onFolderMoved(layer, args) 776 { 777 var obj = args[1]; 778 if (obj !== null) 779 { 780 if (typeof obj.nodeRef !== "undefined" && typeof obj.destination !== "undefined") 781 { 782 var nodeSrc = null; 783 784 // we should be able to find the original node 785 nodeSrc = this.widgets.treeview.getNodeByProperty("nodeRef", obj.nodeRef); 786 787 if (nodeSrc !== null) 788 { 789 var parentNode = nodeSrc.parent; 790 // Node found, so delete it 791 this.widgets.treeview.removeNode(nodeSrc, true); 792 // Have all the parent's child nodes been removed now? 793 if (parentNode !== null) 794 { 795 if (!parentNode.hasChildren()) 796 { 797 parentNode.isLeaf = true; 798 } 799 } 800 // Do we have the node's new parent loaded? 801 var nodeDest = this.widgets.treeview.getNodeByProperty("path", $combine("/", obj.destination)); 802 if (nodeDest) 803 { 804 // The node may already be loading if this was a multiple-folder move 805 if (!nodeDest.isLoading) 806 { 807 if (nodeDest.isLeaf) 808 { 809 nodeDest.isLeaf = false; 810 } 811 else if (nodeDest.expanded) 812 { 813 this._sortNodeChildren(nodeDest); 814 } 815 this.widgets.treeview.render(); 816 this._showHighlight(true); 817 } 818 } 819 } 820 } 821 } 822 823 // Make sure that the drag and drop targets are correctly set... 824 this._applyDropTargets(); 825 }, 826 827 /** 828 * Fired when the currently active filter has changed 829 * @method onFilterChanged 830 * @param layer {string} the event source 831 * @param args {object} arguments object 832 */ 833 onFilterChanged: function DLT_onFilterChanged(layer, args) 834 { 835 var obj = args[1]; 836 if ((obj !== null) && (obj.filterId !== null)) 837 { 838 obj.filterOwner = obj.filterOwner || Alfresco.util.FilterManager.getOwner(obj.filterId); 839 840 // Defer if event received before we're ready 841 if (!this.isReady) 842 { 843 this.initialFilter = Alfresco.util.cleanBubblingObject(obj); 844 Alfresco.logger.debug("DLT_onFilterChanged (deferring)", this.initialFilter); 845 return; 846 } 847 848 Alfresco.logger.debug("DLT_onFilterChanged", obj); 849 this.initialFilter = null; 850 851 this.currentFilter = Alfresco.util.cleanBubblingObject(obj); 852 this.isFilterOwner = (obj.filterOwner == this.name); 853 if (this.isFilterOwner) 854 { 855 this.pathChanged(this.currentFilter.filterData, obj); 856 } 857 this._showHighlight(this.isFilterOwner); 858 } 859 }, 860 861 862 /** 863 * PRIVATE FUNCTIONS 864 */ 865 866 /** 867 * Creates the TreeView control and renders it to the parent element. 868 * @method _buildTree 869 * @private 870 */ 871 _buildTree: function DLT__buildTree() 872 { 873 // Create a new tree 874 var tree = new YAHOO.widget.TreeView(this.id + "-treeview"); 875 this.widgets.treeview = tree; 876 877 // Having both focus and highlight are just confusing (YUI 2.7.0 addition) 878 YAHOO.widget.TreeView.FOCUS_CLASS_NAME = ""; 879 880 // Turn dynamic loading on for entire tree 881 tree.setDynamicLoad(this.fnLoadNodeData); 882 883 // Get root node for tree 884 var root = tree.getRoot(); 885 886 // Add default top-level node 887 this._buildTreeNode( 888 { 889 name: Alfresco.util.message("node.root", this.name), 890 path: "/", 891 nodeRef: "" 892 }, root, false); 893 894 // Register tree-level listeners 895 tree.subscribe("clickEvent", this.onNodeClicked, this, true); 896 tree.subscribe("expandComplete", this.onExpandComplete, this, true); 897 898 // Render tree with this one top-level node 899 tree.render(); 900 }, 901 902 /** 903 * @method _sortNodeChildren 904 * @param node {object} Parent node 905 * @param onSortComplete {object} Optional callback object literal 906 * @private 907 */ 908 _sortNodeChildren: function DLT__sortNodeChildren(node, onSortComplete) 909 { 910 // Is the node a leaf? 911 if (node.isLeaf) 912 { 913 // Yes, so clearing the leaf flag and redrawing will automatically query the child nodes 914 node.isLeaf = false; 915 this.widgets.treeview.render(); 916 this._showHighlight(true); 917 return; 918 } 919 920 // Get the path this node refers to 921 var nodePath = node.data.path; 922 923 // Prepare URI for XHR data request 924 var uri = this._buildTreeNodeUrl(nodePath); 925 926 // Prepare the XHR callback object 927 var callback = 928 { 929 success: function DLT_sNC_success(oResponse) 930 { 931 var results = YAHOO.lang.JSON.parse(oResponse.responseText); 932 933 if (results.items) 934 { 935 var kids = oResponse.argument.node.children; 936 var items = results.items; 937 for (var i = 0, j = items.length; i < j; i++) 938 { 939 if ((kids.length <= i) || (kids[i].data.nodeRef != items[i].nodeRef)) 940 { 941 // Node has moved - search for correct node for this position and swap if found 942 var kidFound = false; 943 for (var m = i, n = kids.length; m < n; m++) 944 { 945 if (kids[m].data.nodeRef == items[i].nodeRef) 946 { 947 var temp = kids[i]; 948 kids[i] = kids[m]; 949 kids[m] = temp; 950 kidFound = true; 951 break; 952 } 953 } 954 955 // If we get here we couldn't find the node, so create one and insert it 956 if (!kidFound) 957 { 958 var item = items[i]; 959 item.path = $combine(oResponse.argument.node.data.path, item.name); 960 var tempNode = this._buildTreeNode(item); 961 962 if (!item.hasChildren) 963 { 964 tempNode.isLeaf = true; 965 } 966 967 if (kids.length === 0) 968 { 969 var parentNode = oResponse.argument.node; 970 parentNode.isLeaf = false; 971 tempNode.appendTo(parentNode); 972 } 973 else if (kids.length > i) 974 { 975 tempNode.insertBefore(kids[i]); 976 } 977 else 978 { 979 tempNode.insertAfter(kids[kids.length - 1]); 980 } 981 } 982 } 983 } 984 985 // Update the tree 986 this.widgets.treeview.render(); 987 this._showHighlight(true); 988 989 // Execute the onSortComplete callback 990 var callback = oResponse.argument.onSortComplete; 991 if (callback && typeof callback.fn == "function") 992 { 993 callback.fn.call(callback.scope ? callback.scope : this, callback.obj); 994 } 995 } 996 }, 997 998 // If the XHR call is not successful, no further processing - tree may not be sorted correctly 999 failure: function DLT_sNC_failure(oResponse) 1000 { 1001 Alfresco.logger.error("DLT_sNC_failure", oResponse); 1002 }, 1003 1004 // XHR response argument information 1005 argument: 1006 { 1007 node: node, 1008 onSortComplete: onSortComplete 1009 }, 1010 1011 scope: this, 1012 1013 // Timeout -- abort the transaction after 7 seconds 1014 timeout: 7000 1015 }; 1016 1017 // Make the XHR call using Connection Manager's asyncRequest method 1018 YAHOO.util.Connect.asyncRequest('GET', uri, callback); 1019 }, 1020 1021 /** 1022 * Highlights the currently selected node. 1023 * @method _showHighlight 1024 * @param isVisible {boolean} Whether the highlight is visible or not 1025 * @private 1026 */ 1027 _showHighlight: function DLT__showHighlight(isVisible) 1028 { 1029 if (this.selectedNode !== null) 1030 { 1031 if (isVisible) 1032 { 1033 Dom.addClass(this.selectedNode.getEl(), "selected"); 1034 } 1035 else 1036 { 1037 Dom.removeClass(this.selectedNode.getEl(), "selected"); 1038 } 1039 } 1040 }, 1041 1042 /** 1043 * Updates the currently selected node. 1044 * @method _updateSelectedNode 1045 * @param node {object} New node to set as currently selected one 1046 * @private 1047 */ 1048 _updateSelectedNode: function DLT__updateSelectedNode(node) 1049 { 1050 if (this.isFilterOwner) 1051 { 1052 this._showHighlight(false); 1053 this.selectedNode = node; 1054 this._showHighlight(true); 1055 } 1056 else 1057 { 1058 this.selectedNode = node; 1059 } 1060 }, 1061 1062 /** 1063 * Build a tree node using passed-in data 1064 * 1065 * @method _buildTreeNode 1066 * @param p_oData {object} Object literal containing required data for new node 1067 * @param p_oParent {object} Optional parent node 1068 * @param p_expanded {object} Optional expanded/collaped state flag 1069 * @return {YAHOO.widget.TextNode} The new tree node 1070 */ 1071 _buildTreeNode: function DLT__buildTreeNode(p_oData, p_oParent, p_expanded) 1072 { 1073 return new YAHOO.widget.TextNode( 1074 { 1075 label: p_oData.name, 1076 path: p_oData.path, 1077 nodeRef: p_oData.nodeRef, 1078 description: p_oData.description 1079 }, p_oParent, p_expanded); 1080 }, 1081 1082 /** 1083 * Build URI parameter string for treenode JSON data webscript 1084 * 1085 * @method _buildTreeNodeUrl 1086 * @param path {string} Path to query 1087 */ 1088 _buildTreeNodeUrl: function DLT__buildTreeNodeUrl(path) 1089 { 1090 var uriTemplate ="slingshot/doclib/treenode/site/" + $combine(encodeURIComponent(this.options.siteId), encodeURIComponent(this.options.containerId), Alfresco.util.encodeURIPath(path)); 1091 uriTemplate += "?perms=false&children=" + this.options.evaluateChildFolders + "&max=" + this.options.maximumFolderCount; 1092 return Alfresco.constants.PROXY_URI + uriTemplate; 1093 } 1094 }); 1095 })(); 1096