2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * @extends {WebInspector.VBox}
33 WebInspector.NavigatorView = function()
35 WebInspector.VBox.call(this);
36 this.registerRequiredCSS("navigatorView.css");
38 this.element.classList.add("navigator-container");
39 var scriptsOutlineElement = this.element.createChild("div", "outline-disclosure navigator");
40 var scriptsTreeElement = scriptsOutlineElement.createChild("ol");
41 this._scriptsTree = new WebInspector.NavigatorTreeOutline(scriptsTreeElement);
43 this.setDefaultFocusedElement(this._scriptsTree.element);
45 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.NavigatorUISourceCodeTreeNode>} */
46 this._uiSourceCodeNodes = new Map();
47 /** @type {!Map.<!WebInspector.NavigatorTreeNode, !StringMap.<!WebInspector.NavigatorFolderTreeNode>>} */
48 this._subfolderNodes = new Map();
50 this._rootNode = new WebInspector.NavigatorRootTreeNode(this);
51 this._rootNode.populate();
53 this.element.addEventListener("contextmenu", this.handleContextMenu.bind(this), false);
56 WebInspector.NavigatorView.Events = {
57 ItemSelected: "ItemSelected",
58 ItemRenamed: "ItemRenamed",
62 * @param {string} type
65 WebInspector.NavigatorView.iconClassForType = function(type)
67 if (type === WebInspector.NavigatorTreeOutline.Types.Domain)
68 return "navigator-domain-tree-item";
69 if (type === WebInspector.NavigatorTreeOutline.Types.FileSystem)
70 return "navigator-folder-tree-item";
71 return "navigator-folder-tree-item";
74 WebInspector.NavigatorView.prototype = {
75 setWorkspace: function(workspace)
77 this._workspace = workspace;
78 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
79 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
80 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved.bind(this), this);
88 this._workspace.uiSourceCodes().forEach(this._addUISourceCode.bind(this));
92 * @param {!WebInspector.UISourceCode} uiSourceCode
95 accept: function(uiSourceCode)
97 return !uiSourceCode.project().isServiceProject();
101 * @param {!WebInspector.UISourceCode} uiSourceCode
103 _addUISourceCode: function(uiSourceCode)
105 if (!this.accept(uiSourceCode))
107 var projectNode = this._projectNode(uiSourceCode.project());
108 var folderNode = this._folderNode(projectNode, uiSourceCode.parentPath());
109 var uiSourceCodeNode = new WebInspector.NavigatorUISourceCodeTreeNode(this, uiSourceCode);
110 this._uiSourceCodeNodes.put(uiSourceCode, uiSourceCodeNode);
111 folderNode.appendChild(uiSourceCodeNode);
115 * @param {!WebInspector.Event} event
117 _uiSourceCodeAdded: function(event)
119 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
120 this._addUISourceCode(uiSourceCode);
124 * @param {!WebInspector.Event} event
126 _uiSourceCodeRemoved: function(event)
128 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
129 this._removeUISourceCode(uiSourceCode);
133 * @param {!WebInspector.Event} event
135 _projectRemoved: function(event)
137 var project = /** @type {!WebInspector.Project} */ (event.data);
138 var uiSourceCodes = project.uiSourceCodes();
139 for (var i = 0; i < uiSourceCodes.length; ++i)
140 this._removeUISourceCode(uiSourceCodes[i]);
144 * @param {!WebInspector.Project} project
145 * @return {!WebInspector.NavigatorTreeNode}
147 _projectNode: function(project)
149 if (!project.displayName())
150 return this._rootNode;
152 var projectNode = this._rootNode.child(project.id());
154 var type = project.type() === WebInspector.projectTypes.FileSystem ? WebInspector.NavigatorTreeOutline.Types.FileSystem : WebInspector.NavigatorTreeOutline.Types.Domain;
155 projectNode = new WebInspector.NavigatorFolderTreeNode(this, project, project.id(), type, "", project.displayName());
156 this._rootNode.appendChild(projectNode);
162 * @param {!WebInspector.NavigatorTreeNode} projectNode
163 * @param {string} folderPath
164 * @return {!WebInspector.NavigatorTreeNode}
166 _folderNode: function(projectNode, folderPath)
171 var subfolderNodes = this._subfolderNodes.get(projectNode);
172 if (!subfolderNodes) {
173 subfolderNodes = /** @type {!StringMap.<!WebInspector.NavigatorFolderTreeNode>} */ (new StringMap());
174 this._subfolderNodes.put(projectNode, subfolderNodes);
177 var folderNode = subfolderNodes.get(folderPath);
181 var parentNode = projectNode;
182 var index = folderPath.lastIndexOf("/");
184 parentNode = this._folderNode(projectNode, folderPath.substring(0, index));
186 var name = folderPath.substring(index + 1);
187 folderNode = new WebInspector.NavigatorFolderTreeNode(this, null, name, WebInspector.NavigatorTreeOutline.Types.Folder, folderPath, name);
188 subfolderNodes.put(folderPath, folderNode);
189 parentNode.appendChild(folderNode);
194 * @param {!WebInspector.UISourceCode} uiSourceCode
195 * @param {boolean=} select
197 revealUISourceCode: function(uiSourceCode, select)
199 var node = this._uiSourceCodeNodes.get(uiSourceCode);
202 if (this._scriptsTree.selectedTreeElement)
203 this._scriptsTree.selectedTreeElement.deselect();
204 this._lastSelectedUISourceCode = uiSourceCode;
209 * @param {!WebInspector.UISourceCode} uiSourceCode
210 * @param {boolean} focusSource
212 _sourceSelected: function(uiSourceCode, focusSource)
214 this._lastSelectedUISourceCode = uiSourceCode;
215 var data = { uiSourceCode: uiSourceCode, focusSource: focusSource};
216 this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemSelected, data);
220 * @param {!WebInspector.UISourceCode} uiSourceCode
222 sourceDeleted: function(uiSourceCode)
227 * @param {!WebInspector.UISourceCode} uiSourceCode
229 _removeUISourceCode: function(uiSourceCode)
231 var node = this._uiSourceCodeNodes.get(uiSourceCode);
235 var projectNode = this._projectNode(uiSourceCode.project());
236 var subfolderNodes = this._subfolderNodes.get(projectNode);
237 var parentNode = node.parent;
238 this._uiSourceCodeNodes.remove(uiSourceCode);
239 parentNode.removeChild(node);
243 parentNode = node.parent;
244 if (!parentNode || !node.isEmpty())
247 subfolderNodes.remove(node._folderPath);
248 parentNode.removeChild(node);
254 * @param {!WebInspector.UISourceCode} uiSourceCode
256 _updateIcon: function(uiSourceCode)
258 var node = this._uiSourceCodeNodes.get(uiSourceCode);
264 var nodes = this._uiSourceCodeNodes.values();
265 for (var i = 0; i < nodes.length; ++i)
268 this._scriptsTree.removeChildren();
269 this._uiSourceCodeNodes.clear();
270 this._subfolderNodes.clear();
271 this._rootNode.reset();
275 * @param {!Event} event
277 handleContextMenu: function(event)
279 var contextMenu = new WebInspector.ContextMenu(event);
280 this._appendAddFolderItem(contextMenu);
285 * @param {!WebInspector.ContextMenu} contextMenu
287 _appendAddFolderItem: function(contextMenu)
291 WebInspector.isolatedFileSystemManager.addFileSystem();
294 var addFolderLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add folder to workspace" : "Add Folder to Workspace");
295 contextMenu.appendItem(addFolderLabel, addFolder);
299 * @param {!WebInspector.Project} project
300 * @param {string} path
302 _handleContextMenuRefresh: function(project, path)
304 project.refresh(path);
308 * @param {!WebInspector.Project} project
309 * @param {string} path
310 * @param {!WebInspector.UISourceCode=} uiSourceCode
312 _handleContextMenuCreate: function(project, path, uiSourceCode)
314 this.create(project, path, uiSourceCode);
318 * @param {!WebInspector.UISourceCode} uiSourceCode
320 _handleContextMenuRename: function(uiSourceCode)
322 this.rename(uiSourceCode, false);
326 * @param {!WebInspector.Project} project
327 * @param {string} path
329 _handleContextMenuExclude: function(project, path)
331 var shouldExclude = window.confirm(WebInspector.UIString("Are you sure you want to exclude this folder?"));
333 WebInspector.startBatchUpdate();
334 project.excludeFolder(path);
335 WebInspector.endBatchUpdate();
340 * @param {!WebInspector.UISourceCode} uiSourceCode
342 _handleContextMenuDelete: function(uiSourceCode)
344 var shouldDelete = window.confirm(WebInspector.UIString("Are you sure you want to delete this file?"));
346 uiSourceCode.project().deleteFile(uiSourceCode.path());
350 * @param {!Event} event
351 * @param {!WebInspector.UISourceCode} uiSourceCode
353 handleFileContextMenu: function(event, uiSourceCode)
355 var contextMenu = new WebInspector.ContextMenu(event);
356 contextMenu.appendApplicableItems(uiSourceCode);
357 contextMenu.appendSeparator();
359 var project = uiSourceCode.project();
360 if (project.type() === WebInspector.projectTypes.FileSystem) {
361 var path = uiSourceCode.parentPath();
362 contextMenu.appendItem(WebInspector.UIString("Rename\u2026"), this._handleContextMenuRename.bind(this, uiSourceCode));
363 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Make a copy\u2026" : "Make a Copy\u2026"), this._handleContextMenuCreate.bind(this, project, path, uiSourceCode));
364 contextMenu.appendItem(WebInspector.UIString("Delete"), this._handleContextMenuDelete.bind(this, uiSourceCode));
365 contextMenu.appendSeparator();
368 this._appendAddFolderItem(contextMenu);
373 * @param {!Event} event
374 * @param {!WebInspector.NavigatorFolderTreeNode} node
376 handleFolderContextMenu: function(event, node)
378 var contextMenu = new WebInspector.ContextMenu(event);
380 var projectNode = node;
381 while (projectNode.parent !== this._rootNode) {
382 path = "/" + projectNode.id + path;
383 projectNode = projectNode.parent;
386 var project = projectNode._project;
388 if (project.type() === WebInspector.projectTypes.FileSystem) {
389 contextMenu.appendItem(WebInspector.UIString("Refresh"), this._handleContextMenuRefresh.bind(this, project, path));
390 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "New file" : "New File"), this._handleContextMenuCreate.bind(this, project, path));
391 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Exclude folder" : "Exclude Folder"), this._handleContextMenuExclude.bind(this, project, path));
393 contextMenu.appendSeparator();
394 this._appendAddFolderItem(contextMenu);
396 function removeFolder()
398 var shouldRemove = window.confirm(WebInspector.UIString("Are you sure you want to remove this folder?"));
403 if (project.type() === WebInspector.projectTypes.FileSystem && node === projectNode) {
404 var removeFolderLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove folder from workspace" : "Remove Folder from Workspace");
405 contextMenu.appendItem(removeFolderLabel, removeFolder);
412 * @param {!WebInspector.UISourceCode} uiSourceCode
413 * @param {boolean} deleteIfCanceled
415 rename: function(uiSourceCode, deleteIfCanceled)
417 var node = this._uiSourceCodeNodes.get(uiSourceCode);
418 console.assert(node);
419 node.rename(callback.bind(this));
422 * @this {WebInspector.NavigatorView}
423 * @param {boolean} committed
425 function callback(committed)
428 if (deleteIfCanceled)
429 uiSourceCode.remove();
433 this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemRenamed, uiSourceCode);
434 this._updateIcon(uiSourceCode);
435 this._sourceSelected(uiSourceCode, true)
440 * @param {!WebInspector.Project} project
441 * @param {string} path
442 * @param {!WebInspector.UISourceCode=} uiSourceCodeToCopy
444 create: function(project, path, uiSourceCodeToCopy)
450 * @this {WebInspector.NavigatorView}
451 * @param {?string} content
453 function contentLoaded(content)
455 createFile.call(this, content || "");
458 if (uiSourceCodeToCopy)
459 uiSourceCodeToCopy.requestContent(contentLoaded.bind(this));
461 createFile.call(this);
464 * @this {WebInspector.NavigatorView}
465 * @param {string=} content
467 function createFile(content)
469 project.createFile(path, null, content || "", fileCreated.bind(this));
473 * @this {WebInspector.NavigatorView}
474 * @param {?string} path
476 function fileCreated(path)
481 uiSourceCode = project.uiSourceCode(filePath);
483 console.assert(uiSourceCode)
486 this._sourceSelected(uiSourceCode, false);
487 this.revealUISourceCode(uiSourceCode, true);
488 this.rename(uiSourceCode, true);
492 __proto__: WebInspector.VBox.prototype
497 * @extends {WebInspector.NavigatorView}
499 WebInspector.SourcesNavigatorView = function()
501 WebInspector.NavigatorView.call(this);
502 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._inspectedURLChanged, this);
505 WebInspector.SourcesNavigatorView.prototype = {
508 * @param {!WebInspector.UISourceCode} uiSourceCode
511 accept: function(uiSourceCode)
513 if (!WebInspector.NavigatorView.prototype.accept(uiSourceCode))
515 return uiSourceCode.project().type() !== WebInspector.projectTypes.ContentScripts && uiSourceCode.project().type() !== WebInspector.projectTypes.Snippets;
520 * @param {!WebInspector.Event} event
522 _inspectedURLChanged: function(event)
524 var nodes = this._uiSourceCodeNodes.values();
525 for (var i = 0; i < nodes.length; ++i) {
526 var uiSourceCode = nodes[i].uiSourceCode();
527 if (WebInspector.resourceTreeModel.inspectedPageURL() && uiSourceCode.url === WebInspector.resourceTreeModel.inspectedPageURL())
528 this.revealUISourceCode(uiSourceCode, true);
533 * @param {!WebInspector.UISourceCode} uiSourceCode
535 _addUISourceCode: function(uiSourceCode)
537 WebInspector.NavigatorView.prototype._addUISourceCode.call(this, uiSourceCode);
538 if (uiSourceCode.url === WebInspector.resourceTreeModel.inspectedPageURL())
539 this.revealUISourceCode(uiSourceCode, true);
542 __proto__: WebInspector.NavigatorView.prototype
547 * @extends {WebInspector.NavigatorView}
549 WebInspector.ContentScriptsNavigatorView = function()
551 WebInspector.NavigatorView.call(this);
554 WebInspector.ContentScriptsNavigatorView.prototype = {
557 * @param {!WebInspector.UISourceCode} uiSourceCode
560 accept: function(uiSourceCode)
562 if (!WebInspector.NavigatorView.prototype.accept(uiSourceCode))
564 return uiSourceCode.project().type() === WebInspector.projectTypes.ContentScripts;
567 __proto__: WebInspector.NavigatorView.prototype
572 * @extends {TreeOutline}
573 * @param {!Element} element
575 WebInspector.NavigatorTreeOutline = function(element)
577 TreeOutline.call(this, element);
578 this.element = element;
580 this.comparator = WebInspector.NavigatorTreeOutline._treeElementsCompare;
583 WebInspector.NavigatorTreeOutline.Types = {
587 UISourceCode: "UISourceCode",
588 FileSystem: "FileSystem"
592 * @param {!TreeElement} treeElement1
593 * @param {!TreeElement} treeElement2
596 WebInspector.NavigatorTreeOutline._treeElementsCompare = function compare(treeElement1, treeElement2)
598 // Insert in the alphabetical order, first domains, then folders, then scripts.
599 function typeWeight(treeElement)
601 var type = treeElement.type();
602 if (type === WebInspector.NavigatorTreeOutline.Types.Domain) {
603 if (treeElement.titleText === WebInspector.resourceTreeModel.inspectedPageDomain())
607 if (type === WebInspector.NavigatorTreeOutline.Types.FileSystem)
609 if (type === WebInspector.NavigatorTreeOutline.Types.Folder)
614 var typeWeight1 = typeWeight(treeElement1);
615 var typeWeight2 = typeWeight(treeElement2);
618 if (typeWeight1 > typeWeight2)
620 else if (typeWeight1 < typeWeight2)
623 var title1 = treeElement1.titleText;
624 var title2 = treeElement2.titleText;
625 result = title1.compareTo(title2);
630 WebInspector.NavigatorTreeOutline.prototype = {
632 * @return {!Array.<!WebInspector.UISourceCode>}
634 scriptTreeElements: function()
637 if (this.children.length) {
638 for (var treeElement = this.children[0]; treeElement; treeElement = treeElement.traverseNextTreeElement(false, this, true)) {
639 if (treeElement instanceof WebInspector.NavigatorSourceTreeElement)
640 result.push(treeElement.uiSourceCode);
646 __proto__: TreeOutline.prototype
651 * @extends {TreeElement}
652 * @param {string} type
653 * @param {string} title
654 * @param {!Array.<string>} iconClasses
655 * @param {boolean} hasChildren
656 * @param {boolean=} noIcon
658 WebInspector.BaseNavigatorTreeElement = function(type, title, iconClasses, hasChildren, noIcon)
661 TreeElement.call(this, "", null, hasChildren);
662 this._titleText = title;
663 this._iconClasses = iconClasses;
664 this._noIcon = noIcon;
667 WebInspector.BaseNavigatorTreeElement.prototype = {
670 this.listItemElement.removeChildren();
671 if (this._iconClasses) {
672 for (var i = 0; i < this._iconClasses.length; ++i)
673 this.listItemElement.classList.add(this._iconClasses[i]);
676 this.listItemElement.createChild("div", "selection");
679 this.imageElement = this.listItemElement.createChild("img", "icon");
681 this.titleElement = this.listItemElement.createChild("div", "base-navigator-tree-element-title");
682 this.titleElement.textContent = this._titleText
686 * @param {!Array.<string>} iconClasses
688 updateIconClasses: function(iconClasses)
690 for (var i = 0; i < this._iconClasses.length; ++i)
691 this.listItemElement.classList.remove(this._iconClasses[i]);
692 this._iconClasses = iconClasses;
693 for (var i = 0; i < this._iconClasses.length; ++i)
694 this.listItemElement.classList.add(this._iconClasses[i]);
699 if (this.listItemElement)
700 this.listItemElement.scrollIntoViewIfNeeded(true);
708 return this._titleText;
711 set titleText(titleText)
713 if (this._titleText === titleText)
715 this._titleText = titleText || "";
716 if (this.titleElement)
717 this.titleElement.textContent = this._titleText;
728 __proto__: TreeElement.prototype
733 * @extends {WebInspector.BaseNavigatorTreeElement}
734 * @param {!WebInspector.NavigatorView} navigatorView
735 * @param {string} type
736 * @param {string} title
738 WebInspector.NavigatorFolderTreeElement = function(navigatorView, type, title)
740 var iconClass = WebInspector.NavigatorView.iconClassForType(type);
741 WebInspector.BaseNavigatorTreeElement.call(this, type, title, [iconClass], true);
742 this._navigatorView = navigatorView;
745 WebInspector.NavigatorFolderTreeElement.prototype = {
746 onpopulate: function()
748 this._node.populate();
753 WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
755 this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
759 * @param {!WebInspector.NavigatorFolderTreeNode} node
761 setNode: function(node)
765 while (node && !node.isRoot()) {
766 paths.push(node._title);
770 this.tooltip = paths.join("/");
774 * @param {!Event} event
776 _handleContextMenuEvent: function(event)
781 this._navigatorView.handleFolderContextMenu(event, this._node);
784 __proto__: WebInspector.BaseNavigatorTreeElement.prototype
789 * @extends {WebInspector.BaseNavigatorTreeElement}
790 * @param {!WebInspector.NavigatorView} navigatorView
791 * @param {!WebInspector.UISourceCode} uiSourceCode
792 * @param {string} title
794 WebInspector.NavigatorSourceTreeElement = function(navigatorView, uiSourceCode, title)
796 this._navigatorView = navigatorView;
797 this._uiSourceCode = uiSourceCode;
798 WebInspector.BaseNavigatorTreeElement.call(this, WebInspector.NavigatorTreeOutline.Types.UISourceCode, title, this._calculateIconClasses(), false);
799 this.tooltip = uiSourceCode.originURL();
802 WebInspector.NavigatorSourceTreeElement.prototype = {
804 * @return {!WebInspector.UISourceCode}
808 return this._uiSourceCode;
812 * @return {!Array.<string>}
814 _calculateIconClasses: function()
816 return ["navigator-" + this._uiSourceCode.contentType().name() + "-tree-item"];
819 updateIcon: function()
821 this.updateIconClasses(this._calculateIconClasses());
826 WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
827 this.listItemElement.draggable = true;
828 this.listItemElement.addEventListener("click", this._onclick.bind(this), false);
829 this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
830 this.listItemElement.addEventListener("mousedown", this._onmousedown.bind(this), false);
831 this.listItemElement.addEventListener("dragstart", this._ondragstart.bind(this), false);
834 _onmousedown: function(event)
836 if (event.which === 1) // Warm-up data for drag'n'drop
837 this._uiSourceCode.requestContent(callback.bind(this));
839 * @param {?string} content
840 * @this {WebInspector.NavigatorSourceTreeElement}
842 function callback(content)
844 this._warmedUpContent = content;
848 _shouldRenameOnMouseDown: function()
850 if (!this._uiSourceCode.canRename())
852 var isSelected = this === this.treeOutline.selectedTreeElement;
853 var isFocused = this.treeOutline.childrenListElement.isSelfOrAncestor(document.activeElement);
854 return isSelected && isFocused && !WebInspector.isBeingEdited(this.treeOutline.element);
857 selectOnMouseDown: function(event)
859 if (event.which !== 1 || !this._shouldRenameOnMouseDown()) {
860 TreeElement.prototype.selectOnMouseDown.call(this, event);
863 setTimeout(rename.bind(this), 300);
866 * @this {WebInspector.NavigatorSourceTreeElement}
870 if (this._shouldRenameOnMouseDown())
871 this._navigatorView.rename(this.uiSourceCode, false);
875 _ondragstart: function(event)
877 event.dataTransfer.setData("text/plain", this._warmedUpContent);
878 event.dataTransfer.effectAllowed = "copy";
887 this._navigatorView._sourceSelected(this.uiSourceCode, true);
892 * @param {!Event} event
894 _onclick: function(event)
896 this._navigatorView._sourceSelected(this.uiSourceCode, false);
903 ondblclick: function(event)
905 var middleClick = event.button === 1;
906 this._navigatorView._sourceSelected(this.uiSourceCode, !middleClick);
916 this._navigatorView._sourceSelected(this.uiSourceCode, true);
926 this._navigatorView.sourceDeleted(this.uiSourceCode);
931 * @param {!Event} event
933 _handleContextMenuEvent: function(event)
936 this._navigatorView.handleFileContextMenu(event, this._uiSourceCode);
939 __proto__: WebInspector.BaseNavigatorTreeElement.prototype
946 WebInspector.NavigatorTreeNode = function(id)
949 /** @type {!StringMap.<!WebInspector.NavigatorTreeNode>} */
950 this._children = new StringMap();
953 WebInspector.NavigatorTreeNode.prototype = {
955 * @return {!TreeElement}
957 treeElement: function() { throw "Not implemented"; },
959 dispose: function() { },
972 hasChildren: function()
979 if (this.isPopulated())
982 this.parent.populate();
983 this._populated = true;
987 wasPopulated: function()
989 var children = this.children();
990 for (var i = 0; i < children.length; ++i)
991 this.treeElement().appendChild(children[i].treeElement());
995 * @param {!WebInspector.NavigatorTreeNode} node
997 didAddChild: function(node)
999 if (this.isPopulated())
1000 this.treeElement().appendChild(node.treeElement());
1004 * @param {!WebInspector.NavigatorTreeNode} node
1006 willRemoveChild: function(node)
1008 if (this.isPopulated())
1009 this.treeElement().removeChild(node.treeElement());
1015 isPopulated: function()
1017 return this._populated;
1025 return !this._children.size();
1029 * @param {string} id
1030 * @return {?WebInspector.NavigatorTreeNode}
1034 return this._children.get(id) || null;
1038 * @return {!Array.<!WebInspector.NavigatorTreeNode>}
1040 children: function()
1042 return this._children.values();
1046 * @param {!WebInspector.NavigatorTreeNode} node
1048 appendChild: function(node)
1050 this._children.put(node.id, node);
1052 this.didAddChild(node);
1056 * @param {!WebInspector.NavigatorTreeNode} node
1058 removeChild: function(node)
1060 this.willRemoveChild(node);
1061 this._children.remove(node.id);
1068 this._children.clear();
1074 * @extends {WebInspector.NavigatorTreeNode}
1075 * @param {!WebInspector.NavigatorView} navigatorView
1077 WebInspector.NavigatorRootTreeNode = function(navigatorView)
1079 WebInspector.NavigatorTreeNode.call(this, "");
1080 this._navigatorView = navigatorView;
1083 WebInspector.NavigatorRootTreeNode.prototype = {
1093 * @return {!TreeOutline}
1095 treeElement: function()
1097 return this._navigatorView._scriptsTree;
1100 __proto__: WebInspector.NavigatorTreeNode.prototype
1105 * @extends {WebInspector.NavigatorTreeNode}
1106 * @param {!WebInspector.NavigatorView} navigatorView
1107 * @param {!WebInspector.UISourceCode} uiSourceCode
1109 WebInspector.NavigatorUISourceCodeTreeNode = function(navigatorView, uiSourceCode)
1111 WebInspector.NavigatorTreeNode.call(this, uiSourceCode.name());
1112 this._navigatorView = navigatorView;
1113 this._uiSourceCode = uiSourceCode;
1114 this._treeElement = null;
1117 WebInspector.NavigatorUISourceCodeTreeNode.prototype = {
1119 * @return {!WebInspector.UISourceCode}
1121 uiSourceCode: function()
1123 return this._uiSourceCode;
1126 updateIcon: function()
1128 if (this._treeElement)
1129 this._treeElement.updateIcon();
1133 * @return {!TreeElement}
1135 treeElement: function()
1137 if (this._treeElement)
1138 return this._treeElement;
1140 this._treeElement = new WebInspector.NavigatorSourceTreeElement(this._navigatorView, this._uiSourceCode, "");
1143 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._titleChanged, this);
1144 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
1145 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
1147 return this._treeElement;
1151 * @param {boolean=} ignoreIsDirty
1153 updateTitle: function(ignoreIsDirty)
1155 if (!this._treeElement)
1158 var titleText = this._uiSourceCode.displayName();
1159 if (!ignoreIsDirty && (this._uiSourceCode.isDirty() || this._uiSourceCode.hasUnsavedCommittedChanges()))
1160 titleText = "*" + titleText;
1161 this._treeElement.titleText = titleText;
1167 hasChildren: function()
1174 if (!this._treeElement)
1176 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._titleChanged, this);
1177 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
1178 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
1181 _titleChanged: function(event)
1186 _workingCopyChanged: function(event)
1191 _workingCopyCommitted: function(event)
1197 * @param {boolean=} select
1199 reveal: function(select)
1201 this.parent.populate();
1202 this.parent.treeElement().expand();
1203 this._treeElement.reveal();
1205 this._treeElement.select(true);
1209 * @param {function(boolean)=} callback
1211 rename: function(callback)
1213 if (!this._treeElement)
1216 // Tree outline should be marked as edited as well as the tree element to prevent search from starting.
1217 var treeOutlineElement = this._treeElement.treeOutline.element;
1218 WebInspector.markBeingEdited(treeOutlineElement, true);
1221 * @param {!Element} element
1222 * @param {string} newTitle
1223 * @param {string} oldTitle
1224 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1226 function commitHandler(element, newTitle, oldTitle)
1228 if (newTitle !== oldTitle) {
1229 this._treeElement.titleText = newTitle;
1230 this._uiSourceCode.rename(newTitle, renameCallback.bind(this));
1233 afterEditing.call(this, true);
1237 * @param {boolean} success
1238 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1240 function renameCallback(success)
1243 WebInspector.markBeingEdited(treeOutlineElement, false);
1245 this.rename(callback);
1248 afterEditing.call(this, true);
1252 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1254 function cancelHandler()
1256 afterEditing.call(this, false);
1260 * @param {boolean} committed
1261 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1263 function afterEditing(committed)
1265 WebInspector.markBeingEdited(treeOutlineElement, false);
1267 this._treeElement.treeOutline.childrenListElement.focus();
1269 callback(committed);
1272 var editingConfig = new WebInspector.InplaceEditor.Config(commitHandler.bind(this), cancelHandler.bind(this));
1273 this.updateTitle(true);
1274 WebInspector.InplaceEditor.startEditing(this._treeElement.titleElement, editingConfig);
1275 window.getSelection().setBaseAndExtent(this._treeElement.titleElement, 0, this._treeElement.titleElement, 1);
1278 __proto__: WebInspector.NavigatorTreeNode.prototype
1283 * @extends {WebInspector.NavigatorTreeNode}
1284 * @param {!WebInspector.NavigatorView} navigatorView
1285 * @param {?WebInspector.Project} project
1286 * @param {string} id
1287 * @param {string} type
1288 * @param {string} folderPath
1289 * @param {string} title
1291 WebInspector.NavigatorFolderTreeNode = function(navigatorView, project, id, type, folderPath, title)
1293 WebInspector.NavigatorTreeNode.call(this, id);
1294 this._navigatorView = navigatorView;
1295 this._project = project;
1297 this._folderPath = folderPath;
1298 this._title = title;
1301 WebInspector.NavigatorFolderTreeNode.prototype = {
1303 * @return {!TreeElement}
1305 treeElement: function()
1307 if (this._treeElement)
1308 return this._treeElement;
1309 this._treeElement = this._createTreeElement(this._title, this);
1310 return this._treeElement;
1314 * @return {!TreeElement}
1316 _createTreeElement: function(title, node)
1318 var treeElement = new WebInspector.NavigatorFolderTreeElement(this._navigatorView, this._type, title);
1319 treeElement.setNode(node);
1323 wasPopulated: function()
1325 if (!this._treeElement || this._treeElement._node !== this)
1327 this._addChildrenRecursive();
1330 _addChildrenRecursive: function()
1332 var children = this.children();
1333 for (var i = 0; i < children.length; ++i) {
1334 var child = children[i];
1335 this.didAddChild(child);
1336 if (child instanceof WebInspector.NavigatorFolderTreeNode)
1337 child._addChildrenRecursive();
1341 _shouldMerge: function(node)
1343 return this._type !== WebInspector.NavigatorTreeOutline.Types.Domain && node instanceof WebInspector.NavigatorFolderTreeNode;
1346 didAddChild: function(node)
1348 function titleForNode(node)
1353 if (!this._treeElement)
1356 var children = this.children();
1358 if (children.length === 1 && this._shouldMerge(node)) {
1359 node._isMerged = true;
1360 this._treeElement.titleText = this._treeElement.titleText + "/" + node._title;
1361 node._treeElement = this._treeElement;
1362 this._treeElement.setNode(node);
1367 if (children.length === 2)
1368 oldNode = children[0] !== node ? children[0] : children[1];
1369 if (oldNode && oldNode._isMerged) {
1370 delete oldNode._isMerged;
1371 var mergedToNodes = [];
1372 mergedToNodes.push(this);
1373 var treeNode = this;
1374 while (treeNode._isMerged) {
1375 treeNode = treeNode.parent;
1376 mergedToNodes.push(treeNode);
1378 mergedToNodes.reverse();
1379 var titleText = mergedToNodes.map(titleForNode).join("/");
1384 nodes.push(treeNode);
1385 children = treeNode.children();
1386 treeNode = children.length === 1 ? children[0] : null;
1387 } while (treeNode && treeNode._isMerged);
1389 if (!this.isPopulated()) {
1390 this._treeElement.titleText = titleText;
1391 this._treeElement.setNode(this);
1392 for (var i = 0; i < nodes.length; ++i) {
1393 delete nodes[i]._treeElement;
1394 delete nodes[i]._isMerged;
1398 var oldTreeElement = this._treeElement;
1399 var treeElement = this._createTreeElement(titleText, this);
1400 for (var i = 0; i < mergedToNodes.length; ++i)
1401 mergedToNodes[i]._treeElement = treeElement;
1402 oldTreeElement.parent.appendChild(treeElement);
1404 oldTreeElement.setNode(nodes[nodes.length - 1]);
1405 oldTreeElement.titleText = nodes.map(titleForNode).join("/");
1406 oldTreeElement.parent.removeChild(oldTreeElement);
1407 this._treeElement.appendChild(oldTreeElement);
1408 if (oldTreeElement.expanded)
1409 treeElement.expand();
1411 if (this.isPopulated())
1412 this._treeElement.appendChild(node.treeElement());
1415 willRemoveChild: function(node)
1417 if (node._isMerged || !this.isPopulated())
1419 this._treeElement.removeChild(node._treeElement);
1422 __proto__: WebInspector.NavigatorTreeNode.prototype