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 var scriptsTreeElement = document.createElement("ol");
39 this._scriptsTree = new WebInspector.NavigatorTreeOutline(scriptsTreeElement);
41 var scriptsOutlineElement = document.createElement("div");
42 scriptsOutlineElement.classList.add("outline-disclosure");
43 scriptsOutlineElement.classList.add("navigator");
44 scriptsOutlineElement.appendChild(scriptsTreeElement);
46 this.element.classList.add("navigator-container");
47 this.element.appendChild(scriptsOutlineElement);
48 this.setDefaultFocusedElement(this._scriptsTree.element);
50 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.NavigatorUISourceCodeTreeNode>} */
51 this._uiSourceCodeNodes = new Map();
52 /** @type {!Map.<!WebInspector.NavigatorTreeNode, !StringMap.<!WebInspector.NavigatorFolderTreeNode>>} */
53 this._subfolderNodes = new Map();
55 this._rootNode = new WebInspector.NavigatorRootTreeNode(this);
56 this._rootNode.populate();
58 this.element.addEventListener("contextmenu", this.handleContextMenu.bind(this), false);
61 WebInspector.NavigatorView.Events = {
62 ItemSelected: "ItemSelected",
63 ItemRenamed: "ItemRenamed",
66 WebInspector.NavigatorView.iconClassForType = function(type)
68 if (type === WebInspector.NavigatorTreeOutline.Types.Domain)
69 return "navigator-domain-tree-item";
70 if (type === WebInspector.NavigatorTreeOutline.Types.FileSystem)
71 return "navigator-folder-tree-item";
72 return "navigator-folder-tree-item";
75 WebInspector.NavigatorView.prototype = {
76 setWorkspace: function(workspace)
78 this._workspace = workspace;
79 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
80 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
81 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset.bind(this), this);
89 this._workspace.uiSourceCodes().forEach(this._addUISourceCode.bind(this));
93 * @param {!WebInspector.UISourceCode} uiSourceCode
96 accept: function(uiSourceCode)
98 return !uiSourceCode.project().isServiceProject();
102 * @param {!WebInspector.UISourceCode} uiSourceCode
104 _addUISourceCode: function(uiSourceCode)
106 if (!this.accept(uiSourceCode))
108 var projectNode = this._projectNode(uiSourceCode.project());
109 var folderNode = this._folderNode(projectNode, uiSourceCode.parentPath());
110 var uiSourceCodeNode = new WebInspector.NavigatorUISourceCodeTreeNode(this, uiSourceCode);
111 this._uiSourceCodeNodes.put(uiSourceCode, uiSourceCodeNode);
112 folderNode.appendChild(uiSourceCodeNode);
116 * @param {!WebInspector.Event} event
118 _uiSourceCodeAdded: function(event)
120 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
121 this._addUISourceCode(uiSourceCode);
125 * @param {!WebInspector.Event} event
127 _uiSourceCodeRemoved: function(event)
129 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
130 this._removeUISourceCode(uiSourceCode);
134 * @param {!WebInspector.Event} event
136 _projectWillReset: function(event)
138 var project = /** @type {!WebInspector.Project} */ (event.data);
139 var uiSourceCodes = project.uiSourceCodes();
140 for (var i = 0; i < uiSourceCodes.length; ++i)
141 this._removeUISourceCode(uiSourceCodes[i]);
145 * @param {!WebInspector.Project} project
146 * @return {!WebInspector.NavigatorTreeNode}
148 _projectNode: function(project)
150 if (!project.displayName())
151 return this._rootNode;
153 var projectNode = this._rootNode.child(project.id());
155 var type = project.type() === WebInspector.projectTypes.FileSystem ? WebInspector.NavigatorTreeOutline.Types.FileSystem : WebInspector.NavigatorTreeOutline.Types.Domain;
156 projectNode = new WebInspector.NavigatorFolderTreeNode(this, project, project.id(), type, "", project.displayName());
157 this._rootNode.appendChild(projectNode);
163 * @param {!WebInspector.NavigatorTreeNode} projectNode
164 * @param {string} folderPath
165 * @return {!WebInspector.NavigatorTreeNode}
167 _folderNode: function(projectNode, folderPath)
172 var subfolderNodes = this._subfolderNodes.get(projectNode);
173 if (!subfolderNodes) {
174 subfolderNodes = /** @type {!StringMap.<!WebInspector.NavigatorFolderTreeNode>} */ (new StringMap());
175 this._subfolderNodes.put(projectNode, subfolderNodes);
178 var folderNode = subfolderNodes.get(folderPath);
182 var parentNode = projectNode;
183 var index = folderPath.lastIndexOf("/");
185 parentNode = this._folderNode(projectNode, folderPath.substring(0, index));
187 var name = folderPath.substring(index + 1);
188 folderNode = new WebInspector.NavigatorFolderTreeNode(this, null, name, WebInspector.NavigatorTreeOutline.Types.Folder, folderPath, name);
189 subfolderNodes.put(folderPath, folderNode);
190 parentNode.appendChild(folderNode);
195 * @param {!WebInspector.UISourceCode} uiSourceCode
196 * @param {boolean=} select
198 revealUISourceCode: function(uiSourceCode, select)
200 var node = this._uiSourceCodeNodes.get(uiSourceCode);
203 if (this._scriptsTree.selectedTreeElement)
204 this._scriptsTree.selectedTreeElement.deselect();
205 this._lastSelectedUISourceCode = uiSourceCode;
210 * @param {!WebInspector.UISourceCode} uiSourceCode
211 * @param {boolean} focusSource
213 _sourceSelected: function(uiSourceCode, focusSource)
215 this._lastSelectedUISourceCode = uiSourceCode;
216 var data = { uiSourceCode: uiSourceCode, focusSource: focusSource};
217 this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemSelected, data);
221 * @param {!WebInspector.UISourceCode} uiSourceCode
223 sourceDeleted: function(uiSourceCode)
228 * @param {!WebInspector.UISourceCode} uiSourceCode
230 _removeUISourceCode: function(uiSourceCode)
232 var node = this._uiSourceCodeNodes.get(uiSourceCode);
236 var projectNode = this._projectNode(uiSourceCode.project());
237 var subfolderNodes = this._subfolderNodes.get(projectNode);
238 var parentNode = node.parent;
239 this._uiSourceCodeNodes.remove(uiSourceCode);
240 parentNode.removeChild(node);
244 parentNode = node.parent;
245 if (!parentNode || !node.isEmpty())
248 subfolderNodes.remove(node._folderPath);
249 parentNode.removeChild(node);
255 * @param {!WebInspector.UISourceCode} uiSourceCode
257 _updateIcon: function(uiSourceCode)
259 var node = this._uiSourceCodeNodes.get(uiSourceCode);
265 var nodes = this._uiSourceCodeNodes.values();
266 for (var i = 0; i < nodes.length; ++i)
269 this._scriptsTree.removeChildren();
270 this._uiSourceCodeNodes.clear();
271 this._subfolderNodes.clear();
272 this._rootNode.reset();
276 * @param {?Event} event
278 handleContextMenu: function(event)
280 var contextMenu = new WebInspector.ContextMenu(event);
281 this._appendAddFolderItem(contextMenu);
286 * @param {!WebInspector.ContextMenu} contextMenu
288 _appendAddFolderItem: function(contextMenu)
292 WebInspector.isolatedFileSystemManager.addFileSystem();
295 var addFolderLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add folder to workspace" : "Add Folder to Workspace");
296 contextMenu.appendItem(addFolderLabel, addFolder);
300 * @param {!WebInspector.Project} project
301 * @param {string} path
303 _handleContextMenuRefresh: function(project, path)
305 project.refresh(path);
309 * @param {!WebInspector.Project} project
310 * @param {string} path
311 * @param {!WebInspector.UISourceCode=} uiSourceCode
313 _handleContextMenuCreate: function(project, path, uiSourceCode)
315 this.create(project, path, uiSourceCode);
319 * @param {!WebInspector.Project} project
320 * @param {string} path
322 _handleContextMenuExclude: function(project, path)
324 var shouldExclude = window.confirm(WebInspector.UIString("Are you sure you want to exclude this folder?"));
326 WebInspector.startBatchUpdate();
327 project.excludeFolder(path);
328 WebInspector.endBatchUpdate();
333 * @param {!WebInspector.UISourceCode} uiSourceCode
335 _handleContextMenuDelete: function(uiSourceCode)
337 var shouldDelete = window.confirm(WebInspector.UIString("Are you sure you want to delete this file?"));
339 uiSourceCode.project().deleteFile(uiSourceCode.path());
343 * @param {!Event} event
344 * @param {!WebInspector.UISourceCode} uiSourceCode
346 handleFileContextMenu: function(event, uiSourceCode)
348 var contextMenu = new WebInspector.ContextMenu(event);
349 contextMenu.appendApplicableItems(uiSourceCode);
350 contextMenu.appendSeparator();
352 var project = uiSourceCode.project();
353 var path = uiSourceCode.parentPath();
354 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Refresh parent" : "Refresh Parent"), this._handleContextMenuRefresh.bind(this, project, path));
355 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Duplicate file" : "Duplicate File"), this._handleContextMenuCreate.bind(this, project, path, uiSourceCode));
356 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Exclude parent folder" : "Exclude Parent Folder"), this._handleContextMenuExclude.bind(this, project, path));
357 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Delete file" : "Delete File"), this._handleContextMenuDelete.bind(this, uiSourceCode));
358 contextMenu.appendSeparator();
359 this._appendAddFolderItem(contextMenu);
364 * @param {!Event} event
365 * @param {!WebInspector.NavigatorFolderTreeNode} node
367 handleFolderContextMenu: function(event, node)
369 var contextMenu = new WebInspector.ContextMenu(event);
371 var projectNode = node;
372 while (projectNode.parent !== this._rootNode) {
373 path = "/" + projectNode.id + path;
374 projectNode = projectNode.parent;
377 var project = projectNode._project;
379 if (project.type() === WebInspector.projectTypes.FileSystem) {
380 contextMenu.appendItem(WebInspector.UIString("Refresh"), this._handleContextMenuRefresh.bind(this, project, path));
381 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "New file" : "New File"), this._handleContextMenuCreate.bind(this, project, path));
382 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Exclude folder" : "Exclude Folder"), this._handleContextMenuExclude.bind(this, project, path));
384 contextMenu.appendSeparator();
385 this._appendAddFolderItem(contextMenu);
387 function removeFolder()
389 var shouldRemove = window.confirm(WebInspector.UIString("Are you sure you want to remove this folder?"));
394 if (project.type() === WebInspector.projectTypes.FileSystem && node === projectNode) {
395 var removeFolderLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove folder from workspace" : "Remove Folder from Workspace");
396 contextMenu.appendItem(removeFolderLabel, removeFolder);
403 * @param {!WebInspector.UISourceCode} uiSourceCode
404 * @param {boolean} deleteIfCanceled
406 rename: function(uiSourceCode, deleteIfCanceled)
408 var node = this._uiSourceCodeNodes.get(uiSourceCode);
409 console.assert(node);
410 node.rename(callback.bind(this));
413 * @this {WebInspector.NavigatorView}
414 * @param {boolean} committed
416 function callback(committed)
419 if (deleteIfCanceled)
420 uiSourceCode.remove();
424 var data = { uiSourceCode: uiSourceCode };
425 this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemRenamed, data);
426 this._updateIcon(uiSourceCode);
427 this._sourceSelected(uiSourceCode, true)
432 * @param {!WebInspector.Project} project
433 * @param {string} path
434 * @param {!WebInspector.UISourceCode=} uiSourceCodeToCopy
436 create: function(project, path, uiSourceCodeToCopy)
442 * @this {WebInspector.NavigatorView}
443 * @param {?string} content
445 function contentLoaded(content)
447 createFile.call(this, content || "");
450 if (uiSourceCodeToCopy)
451 uiSourceCodeToCopy.requestContent(contentLoaded.bind(this));
453 createFile.call(this);
456 * @this {WebInspector.NavigatorView}
457 * @param {string=} content
459 function createFile(content)
461 project.createFile(path, null, content || "", fileCreated.bind(this));
465 * @this {WebInspector.NavigatorView}
466 * @param {?string} path
468 function fileCreated(path)
473 uiSourceCode = project.uiSourceCode(filePath);
475 console.assert(uiSourceCode)
478 this._sourceSelected(uiSourceCode, false);
479 this.rename(uiSourceCode, true);
483 __proto__: WebInspector.VBox.prototype
488 * @extends {WebInspector.NavigatorView}
490 WebInspector.SourcesNavigatorView = function()
492 WebInspector.NavigatorView.call(this);
493 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._inspectedURLChanged, this);
496 WebInspector.SourcesNavigatorView.prototype = {
499 * @param {!WebInspector.UISourceCode} uiSourceCode
502 accept: function(uiSourceCode)
504 if (!WebInspector.NavigatorView.prototype.accept(uiSourceCode))
506 return !uiSourceCode.isContentScript && uiSourceCode.project().type() !== WebInspector.projectTypes.Snippets;
510 * @param {!WebInspector.Event} event
512 _inspectedURLChanged: function(event)
514 var nodes = this._uiSourceCodeNodes.values();
515 for (var i = 0; i < nodes.length; ++i) {
516 var uiSourceCode = nodes[i].uiSourceCode();
517 if (uiSourceCode.url === WebInspector.resourceTreeModel.inspectedPageURL())
518 this.revealUISourceCode(uiSourceCode, true);
523 * @param {!WebInspector.UISourceCode} uiSourceCode
525 _addUISourceCode: function(uiSourceCode)
527 WebInspector.NavigatorView.prototype._addUISourceCode.call(this, uiSourceCode);
528 if (uiSourceCode.url === WebInspector.resourceTreeModel.inspectedPageURL())
529 this.revealUISourceCode(uiSourceCode, true);
532 __proto__: WebInspector.NavigatorView.prototype
537 * @extends {WebInspector.NavigatorView}
539 WebInspector.ContentScriptsNavigatorView = function()
541 WebInspector.NavigatorView.call(this);
544 WebInspector.ContentScriptsNavigatorView.prototype = {
547 * @param {!WebInspector.UISourceCode} uiSourceCode
550 accept: function(uiSourceCode)
552 if (!WebInspector.NavigatorView.prototype.accept(uiSourceCode))
554 return uiSourceCode.isContentScript;
557 __proto__: WebInspector.NavigatorView.prototype
562 * @extends {TreeOutline}
563 * @param {!Element} element
565 WebInspector.NavigatorTreeOutline = function(element)
567 TreeOutline.call(this, element);
568 this.element = element;
570 this.comparator = WebInspector.NavigatorTreeOutline._treeElementsCompare;
573 WebInspector.NavigatorTreeOutline.Types = {
577 UISourceCode: "UISourceCode",
578 FileSystem: "FileSystem"
582 * @param {!TreeElement} treeElement1
583 * @param {!TreeElement} treeElement2
586 WebInspector.NavigatorTreeOutline._treeElementsCompare = function compare(treeElement1, treeElement2)
588 // Insert in the alphabetical order, first domains, then folders, then scripts.
589 function typeWeight(treeElement)
591 var type = treeElement.type();
592 if (type === WebInspector.NavigatorTreeOutline.Types.Domain) {
593 if (treeElement.titleText === WebInspector.resourceTreeModel.inspectedPageDomain())
597 if (type === WebInspector.NavigatorTreeOutline.Types.FileSystem)
599 if (type === WebInspector.NavigatorTreeOutline.Types.Folder)
604 var typeWeight1 = typeWeight(treeElement1);
605 var typeWeight2 = typeWeight(treeElement2);
608 if (typeWeight1 > typeWeight2)
610 else if (typeWeight1 < typeWeight2)
613 var title1 = treeElement1.titleText;
614 var title2 = treeElement2.titleText;
615 result = title1.compareTo(title2);
620 WebInspector.NavigatorTreeOutline.prototype = {
622 * @return {!Array.<!WebInspector.UISourceCode>}
624 scriptTreeElements: function()
627 if (this.children.length) {
628 for (var treeElement = this.children[0]; treeElement; treeElement = treeElement.traverseNextTreeElement(false, this, true)) {
629 if (treeElement instanceof WebInspector.NavigatorSourceTreeElement)
630 result.push(treeElement.uiSourceCode);
636 __proto__: TreeOutline.prototype
641 * @extends {TreeElement}
642 * @param {string} type
643 * @param {string} title
644 * @param {!Array.<string>} iconClasses
645 * @param {boolean} hasChildren
646 * @param {boolean=} noIcon
648 WebInspector.BaseNavigatorTreeElement = function(type, title, iconClasses, hasChildren, noIcon)
651 TreeElement.call(this, "", null, hasChildren);
652 this._titleText = title;
653 this._iconClasses = iconClasses;
654 this._noIcon = noIcon;
657 WebInspector.BaseNavigatorTreeElement.prototype = {
660 this.listItemElement.removeChildren();
661 if (this._iconClasses) {
662 for (var i = 0; i < this._iconClasses.length; ++i)
663 this.listItemElement.classList.add(this._iconClasses[i]);
666 var selectionElement = document.createElement("div");
667 selectionElement.className = "selection";
668 this.listItemElement.appendChild(selectionElement);
671 this.imageElement = document.createElement("img");
672 this.imageElement.className = "icon";
673 this.listItemElement.appendChild(this.imageElement);
676 this.titleElement = document.createElement("div");
677 this.titleElement.className = "base-navigator-tree-element-title";
678 this._titleTextNode = document.createTextNode("");
679 this._titleTextNode.textContent = this._titleText;
680 this.titleElement.appendChild(this._titleTextNode);
681 this.listItemElement.appendChild(this.titleElement);
684 updateIconClasses: function(iconClasses)
686 for (var i = 0; i < this._iconClasses.length; ++i)
687 this.listItemElement.classList.remove(this._iconClasses[i]);
688 this._iconClasses = iconClasses;
689 for (var i = 0; i < this._iconClasses.length; ++i)
690 this.listItemElement.classList.add(this._iconClasses[i]);
695 if (this.listItemElement)
696 this.listItemElement.scrollIntoViewIfNeeded(true);
704 return this._titleText;
707 set titleText(titleText)
709 if (this._titleText === titleText)
711 this._titleText = titleText || "";
712 if (this.titleElement)
713 this.titleElement.textContent = this._titleText;
724 __proto__: TreeElement.prototype
729 * @extends {WebInspector.BaseNavigatorTreeElement}
730 * @param {!WebInspector.NavigatorView} navigatorView
731 * @param {string} type
732 * @param {string} title
734 WebInspector.NavigatorFolderTreeElement = function(navigatorView, type, title)
736 var iconClass = WebInspector.NavigatorView.iconClassForType(type);
737 WebInspector.BaseNavigatorTreeElement.call(this, type, title, [iconClass], true);
738 this._navigatorView = navigatorView;
741 WebInspector.NavigatorFolderTreeElement.prototype = {
742 onpopulate: function()
744 this._node.populate();
749 WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
751 this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
755 * @param {!WebInspector.NavigatorFolderTreeNode} node
757 setNode: function(node)
761 while (node && !node.isRoot()) {
762 paths.push(node._title);
766 this.tooltip = paths.join("/");
770 * @param {?Event} event
772 _handleContextMenuEvent: function(event)
777 this._navigatorView.handleFolderContextMenu(/** @type {!Event} */ (event), this._node);
780 __proto__: WebInspector.BaseNavigatorTreeElement.prototype
785 * @extends {WebInspector.BaseNavigatorTreeElement}
786 * @param {!WebInspector.NavigatorView} navigatorView
787 * @param {!WebInspector.UISourceCode} uiSourceCode
788 * @param {string} title
790 WebInspector.NavigatorSourceTreeElement = function(navigatorView, uiSourceCode, title)
792 this._navigatorView = navigatorView;
793 this._uiSourceCode = uiSourceCode;
794 WebInspector.BaseNavigatorTreeElement.call(this, WebInspector.NavigatorTreeOutline.Types.UISourceCode, title, this._calculateIconClasses(), false);
795 this.tooltip = uiSourceCode.originURL();
798 WebInspector.NavigatorSourceTreeElement.prototype = {
800 * @return {!WebInspector.UISourceCode}
804 return this._uiSourceCode;
808 * @return {!Array.<string>}
810 _calculateIconClasses: function()
812 return ["navigator-" + this._uiSourceCode.contentType().name() + "-tree-item"];
815 updateIcon: function()
817 this.updateIconClasses(this._calculateIconClasses());
822 WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
823 this.listItemElement.draggable = true;
824 this.listItemElement.addEventListener("click", this._onclick.bind(this), false);
825 this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
826 this.listItemElement.addEventListener("mousedown", this._onmousedown.bind(this), false);
827 this.listItemElement.addEventListener("dragstart", this._ondragstart.bind(this), false);
830 _onmousedown: function(event)
832 if (event.which === 1) // Warm-up data for drag'n'drop
833 this._uiSourceCode.requestContent(callback.bind(this));
835 * @param {?string} content
836 * @this {WebInspector.NavigatorSourceTreeElement}
838 function callback(content)
840 this._warmedUpContent = content;
844 _shouldRenameOnMouseDown: function()
846 if (!this._uiSourceCode.canRename())
848 var isSelected = this === this.treeOutline.selectedTreeElement;
849 var isFocused = this.treeOutline.childrenListElement.isSelfOrAncestor(document.activeElement);
850 return isSelected && isFocused && !WebInspector.isBeingEdited(this.treeOutline.element);
853 selectOnMouseDown: function(event)
855 if (event.which !== 1 || !this._shouldRenameOnMouseDown()) {
856 TreeElement.prototype.selectOnMouseDown.call(this, event);
859 setTimeout(rename.bind(this), 300);
862 * @this {WebInspector.NavigatorSourceTreeElement}
866 if (this._shouldRenameOnMouseDown())
867 this._navigatorView.rename(this.uiSourceCode, false);
871 _ondragstart: function(event)
873 event.dataTransfer.setData("text/plain", this._warmedUpContent);
874 event.dataTransfer.effectAllowed = "copy";
883 this._navigatorView._sourceSelected(this.uiSourceCode, true);
888 * @param {!Event} event
890 _onclick: function(event)
892 this._navigatorView._sourceSelected(this.uiSourceCode, false);
899 ondblclick: function(event)
901 var middleClick = event.button === 1;
902 this._navigatorView._sourceSelected(this.uiSourceCode, !middleClick);
912 this._navigatorView._sourceSelected(this.uiSourceCode, true);
922 this._navigatorView.sourceDeleted(this.uiSourceCode);
927 * @param {!Event} event
929 _handleContextMenuEvent: function(event)
932 this._navigatorView.handleFileContextMenu(event, this._uiSourceCode);
935 __proto__: WebInspector.BaseNavigatorTreeElement.prototype
942 WebInspector.NavigatorTreeNode = function(id)
945 /** @type {!StringMap.<!WebInspector.NavigatorTreeNode>} */
946 this._children = new StringMap();
949 WebInspector.NavigatorTreeNode.prototype = {
951 * @return {!TreeElement}
953 treeElement: function() { throw "Not implemented"; },
955 dispose: function() { },
968 hasChildren: function()
975 if (this.isPopulated())
978 this.parent.populate();
979 this._populated = true;
983 wasPopulated: function()
985 var children = this.children();
986 for (var i = 0; i < children.length; ++i)
987 this.treeElement().appendChild(children[i].treeElement());
991 * @param {!WebInspector.NavigatorTreeNode} node
993 didAddChild: function(node)
995 if (this.isPopulated())
996 this.treeElement().appendChild(node.treeElement());
1000 * @param {!WebInspector.NavigatorTreeNode} node
1002 willRemoveChild: function(node)
1004 if (this.isPopulated())
1005 this.treeElement().removeChild(node.treeElement());
1011 isPopulated: function()
1013 return this._populated;
1021 return !this._children.size();
1025 * @param {string} id
1026 * @return {?WebInspector.NavigatorTreeNode}
1030 return this._children.get(id) || null;
1034 * @return {!Array.<!WebInspector.NavigatorTreeNode>}
1036 children: function()
1038 return this._children.values();
1042 * @param {!WebInspector.NavigatorTreeNode} node
1044 appendChild: function(node)
1046 this._children.put(node.id, node);
1048 this.didAddChild(node);
1052 * @param {!WebInspector.NavigatorTreeNode} node
1054 removeChild: function(node)
1056 this.willRemoveChild(node);
1057 this._children.remove(node.id);
1064 this._children.clear();
1070 * @extends {WebInspector.NavigatorTreeNode}
1071 * @param {!WebInspector.NavigatorView} navigatorView
1073 WebInspector.NavigatorRootTreeNode = function(navigatorView)
1075 WebInspector.NavigatorTreeNode.call(this, "");
1076 this._navigatorView = navigatorView;
1079 WebInspector.NavigatorRootTreeNode.prototype = {
1089 * @return {!TreeOutline}
1091 treeElement: function()
1093 return this._navigatorView._scriptsTree;
1096 __proto__: WebInspector.NavigatorTreeNode.prototype
1101 * @extends {WebInspector.NavigatorTreeNode}
1102 * @param {!WebInspector.NavigatorView} navigatorView
1103 * @param {!WebInspector.UISourceCode} uiSourceCode
1105 WebInspector.NavigatorUISourceCodeTreeNode = function(navigatorView, uiSourceCode)
1107 WebInspector.NavigatorTreeNode.call(this, uiSourceCode.name());
1108 this._navigatorView = navigatorView;
1109 this._uiSourceCode = uiSourceCode;
1110 this._treeElement = null;
1113 WebInspector.NavigatorUISourceCodeTreeNode.prototype = {
1115 * @return {!WebInspector.UISourceCode}
1117 uiSourceCode: function()
1119 return this._uiSourceCode;
1122 updateIcon: function()
1124 if (this._treeElement)
1125 this._treeElement.updateIcon();
1129 * @return {!TreeElement}
1131 treeElement: function()
1133 if (this._treeElement)
1134 return this._treeElement;
1136 this._treeElement = new WebInspector.NavigatorSourceTreeElement(this._navigatorView, this._uiSourceCode, "");
1139 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._titleChanged, this);
1140 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
1141 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
1143 return this._treeElement;
1147 * @param {boolean=} ignoreIsDirty
1149 updateTitle: function(ignoreIsDirty)
1151 if (!this._treeElement)
1154 var titleText = this._uiSourceCode.displayName();
1155 if (!ignoreIsDirty && (this._uiSourceCode.isDirty() || this._uiSourceCode.hasUnsavedCommittedChanges()))
1156 titleText = "*" + titleText;
1157 this._treeElement.titleText = titleText;
1163 hasChildren: function()
1170 if (!this._treeElement)
1172 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._titleChanged, this);
1173 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
1174 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
1177 _titleChanged: function(event)
1182 _workingCopyChanged: function(event)
1187 _workingCopyCommitted: function(event)
1193 * @param {boolean=} select
1195 reveal: function(select)
1197 this.parent.populate();
1198 this.parent.treeElement().expand();
1199 this._treeElement.reveal();
1201 this._treeElement.select(true);
1205 * @param {function(boolean)=} callback
1207 rename: function(callback)
1209 if (!this._treeElement)
1212 // Tree outline should be marked as edited as well as the tree element to prevent search from starting.
1213 var treeOutlineElement = this._treeElement.treeOutline.element;
1214 WebInspector.markBeingEdited(treeOutlineElement, true);
1217 * @param {!Element} element
1218 * @param {string} newTitle
1219 * @param {string} oldTitle
1220 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1222 function commitHandler(element, newTitle, oldTitle)
1224 if (newTitle !== oldTitle) {
1225 this._treeElement.titleText = newTitle;
1226 this._uiSourceCode.rename(newTitle, renameCallback.bind(this));
1229 afterEditing.call(this, true);
1233 * @param {boolean} success
1234 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1236 function renameCallback(success)
1239 WebInspector.markBeingEdited(treeOutlineElement, false);
1241 this.rename(callback);
1244 afterEditing.call(this, true);
1248 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1250 function cancelHandler()
1252 afterEditing.call(this, false);
1256 * @param {boolean} committed
1257 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1259 function afterEditing(committed)
1261 WebInspector.markBeingEdited(treeOutlineElement, false);
1263 this._treeElement.treeOutline.childrenListElement.focus();
1265 callback(committed);
1268 var editingConfig = new WebInspector.InplaceEditor.Config(commitHandler.bind(this), cancelHandler.bind(this));
1269 this.updateTitle(true);
1270 WebInspector.InplaceEditor.startEditing(this._treeElement.titleElement, editingConfig);
1271 window.getSelection().setBaseAndExtent(this._treeElement.titleElement, 0, this._treeElement.titleElement, 1);
1274 __proto__: WebInspector.NavigatorTreeNode.prototype
1279 * @extends {WebInspector.NavigatorTreeNode}
1280 * @param {!WebInspector.NavigatorView} navigatorView
1281 * @param {?WebInspector.Project} project
1282 * @param {string} id
1283 * @param {string} type
1284 * @param {string} folderPath
1285 * @param {string} title
1287 WebInspector.NavigatorFolderTreeNode = function(navigatorView, project, id, type, folderPath, title)
1289 WebInspector.NavigatorTreeNode.call(this, id);
1290 this._navigatorView = navigatorView;
1291 this._project = project;
1293 this._folderPath = folderPath;
1294 this._title = title;
1297 WebInspector.NavigatorFolderTreeNode.prototype = {
1299 * @return {!TreeElement}
1301 treeElement: function()
1303 if (this._treeElement)
1304 return this._treeElement;
1305 this._treeElement = this._createTreeElement(this._title, this);
1306 return this._treeElement;
1310 * @return {!TreeElement}
1312 _createTreeElement: function(title, node)
1314 var treeElement = new WebInspector.NavigatorFolderTreeElement(this._navigatorView, this._type, title);
1315 treeElement.setNode(node);
1319 wasPopulated: function()
1321 if (!this._treeElement || this._treeElement._node !== this)
1323 this._addChildrenRecursive();
1326 _addChildrenRecursive: function()
1328 var children = this.children();
1329 for (var i = 0; i < children.length; ++i) {
1330 var child = children[i];
1331 this.didAddChild(child);
1332 if (child instanceof WebInspector.NavigatorFolderTreeNode)
1333 child._addChildrenRecursive();
1337 _shouldMerge: function(node)
1339 return this._type !== WebInspector.NavigatorTreeOutline.Types.Domain && node instanceof WebInspector.NavigatorFolderTreeNode;
1342 didAddChild: function(node)
1344 function titleForNode(node)
1349 if (!this._treeElement)
1352 var children = this.children();
1354 if (children.length === 1 && this._shouldMerge(node)) {
1355 node._isMerged = true;
1356 this._treeElement.titleText = this._treeElement.titleText + "/" + node._title;
1357 node._treeElement = this._treeElement;
1358 this._treeElement.setNode(node);
1363 if (children.length === 2)
1364 oldNode = children[0] !== node ? children[0] : children[1];
1365 if (oldNode && oldNode._isMerged) {
1366 delete oldNode._isMerged;
1367 var mergedToNodes = [];
1368 mergedToNodes.push(this);
1369 var treeNode = this;
1370 while (treeNode._isMerged) {
1371 treeNode = treeNode.parent;
1372 mergedToNodes.push(treeNode);
1374 mergedToNodes.reverse();
1375 var titleText = mergedToNodes.map(titleForNode).join("/");
1380 nodes.push(treeNode);
1381 children = treeNode.children();
1382 treeNode = children.length === 1 ? children[0] : null;
1383 } while (treeNode && treeNode._isMerged);
1385 if (!this.isPopulated()) {
1386 this._treeElement.titleText = titleText;
1387 this._treeElement.setNode(this);
1388 for (var i = 0; i < nodes.length; ++i) {
1389 delete nodes[i]._treeElement;
1390 delete nodes[i]._isMerged;
1394 var oldTreeElement = this._treeElement;
1395 var treeElement = this._createTreeElement(titleText, this);
1396 for (var i = 0; i < mergedToNodes.length; ++i)
1397 mergedToNodes[i]._treeElement = treeElement;
1398 oldTreeElement.parent.appendChild(treeElement);
1400 oldTreeElement.setNode(nodes[nodes.length - 1]);
1401 oldTreeElement.titleText = nodes.map(titleForNode).join("/");
1402 oldTreeElement.parent.removeChild(oldTreeElement);
1403 this._treeElement.appendChild(oldTreeElement);
1404 if (oldTreeElement.expanded)
1405 treeElement.expand();
1407 if (this.isPopulated())
1408 this._treeElement.appendChild(node.treeElement());
1411 willRemoveChild: function(node)
1413 if (node._isMerged || !this.isPopulated())
1415 this._treeElement.removeChild(node._treeElement);
1418 __proto__: WebInspector.NavigatorTreeNode.prototype